summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libelfu/modelops.h1
-rw-r--r--src/model/layout.c97
-rw-r--r--src/model/reladd.c3
3 files changed, 101 insertions, 0 deletions
diff --git a/include/libelfu/modelops.h b/include/libelfu/modelops.h
index 8374b02..c1d30e3 100644
--- a/include/libelfu/modelops.h
+++ b/include/libelfu/modelops.h
@@ -22,6 +22,7 @@ ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count);
GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
GElf_Word align, int w, int x,
ElfuPhdr **injPhdr);
+int elfu_mLayoutAuto(ElfuElf *me);
int elfu_mCheck(ElfuElf *me);
diff --git a/src/model/layout.c b/src/model/layout.c
index 2febed1..d5d49d8 100644
--- a/src/model/layout.c
+++ b/src/model/layout.c
@@ -215,3 +215,100 @@ GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
}
return 0;
}
+
+
+
+
+static int cmpPhdrOffs(const void *mp1, const void *mp2)
+{
+ assert(mp1);
+ assert(mp2);
+
+ ElfuPhdr *p1 = *(ElfuPhdr**)mp1;
+ ElfuPhdr *p2 = *(ElfuPhdr**)mp2;
+
+ assert(p1);
+ assert(p2);
+
+
+ if (p1->phdr.p_offset < p2->phdr.p_offset) {
+ return -1;
+ } else if (p1->phdr.p_offset == p2->phdr.p_offset) {
+ return 0;
+ } else /* if (p1->phdr.p_offset > p2->phdr.p_offset) */ {
+ return 1;
+ }
+}
+
+int elfu_mLayoutAuto(ElfuElf *me)
+{
+ ElfuPhdr *mp;
+ ElfuScn *ms;
+ ElfuPhdr **phdrArr;
+ GElf_Off lastend = 0;
+ size_t i, j;
+
+ assert(me);
+
+ phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr));
+ if (!phdrArr) {
+ ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n");
+ return 1;
+ }
+
+ i = 0;
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ phdrArr[i] = mp;
+ i++;
+ }
+
+ /* Assume we have at least one LOAD PHDR,
+ * and that it ends after EHDR and PHDRs */
+ assert(i > 1);
+
+ /* Sort array by file offset */
+ qsort(phdrArr, i, sizeof(*phdrArr), cmpPhdrOffs);
+
+ lastend = OFFS_END(phdrArr[0]->phdr.p_offset, phdrArr[0]->phdr.p_filesz);
+
+ /* Wiggle offsets of 2nd, 3rd etc so take minimum space */
+ for (j = 1; j < i; j++) {
+ GElf_Off subalign = phdrArr[j]->phdr.p_offset % phdrArr[j]->phdr.p_align;
+
+ if ((lastend % phdrArr[j]->phdr.p_align) <= subalign) {
+ lastend += subalign - (lastend % phdrArr[j]->phdr.p_align);
+ } else {
+ lastend += phdrArr[j]->phdr.p_align - ((lastend % phdrArr[j]->phdr.p_align) - subalign);
+ }
+
+ phdrArr[j]->phdr.p_offset = lastend;
+
+ elfu_mPhdrUpdateChildOffsets(phdrArr[j]);
+
+ lastend = OFFS_END(phdrArr[j]->phdr.p_offset, phdrArr[j]->phdr.p_filesz);
+ }
+
+ free(phdrArr);
+
+
+ /* Place orphaned sections afterwards, maintaining alignment */
+ CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
+ lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
+
+ ms->shdr.sh_offset = lastend;
+
+ lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
+ }
+
+
+ /* Move SHDRs to end */
+ lastend = ROUNDUP(lastend, 8);
+ me->ehdr.e_shoff = lastend;
+
+
+ return 0;
+}
diff --git a/src/model/reladd.c b/src/model/reladd.c
index ffd02f4..2de03dc 100644
--- a/src/model/reladd.c
+++ b/src/model/reladd.c
@@ -160,4 +160,7 @@ void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
/* Do relocations and other stuff */
elfu_mScnForall(mrel, subScnAdd2, me, NULL);
+
+ /* Re-layout to accommodate new contents */
+ elfu_mLayoutAuto(me);
}