summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornorly <ny-git@enpas.org>2013-06-22 01:11:40 +0100
committernorly <ny-git@enpas.org>2013-06-24 03:59:08 +0100
commit1c80cd18a94dcf58c852812e9b70098a6a87730e (patch)
tree97f719ef106e294f154f6459ac6576ca574b0272
parent5c887f4d91f3817f13c3c5fad9952b16273e73db (diff)
PHDR addition. Should make x86-64 work in many cases.
-rw-r--r--include/libelfu/generic.h1
-rw-r--r--src/libelfu/modelops/layout.c107
2 files changed, 106 insertions, 2 deletions
diff --git a/include/libelfu/generic.h b/include/libelfu/generic.h
index 73c6b0e..340719c 100644
--- a/include/libelfu/generic.h
+++ b/include/libelfu/generic.h
@@ -4,6 +4,7 @@
#include <gelf.h>
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define ROUNDUP(x, align) ((x) + ((align) - ((x) % (align))) % (align))
diff --git a/src/libelfu/modelops/layout.c b/src/libelfu/modelops/layout.c
index 3ab3b05..a75729e 100644
--- a/src/libelfu/modelops/layout.c
+++ b/src/libelfu/modelops/layout.c
@@ -68,6 +68,79 @@ static GElf_Word shiftStuffAtAfterOffset(ElfuElf *me,
+static ElfuPhdr* appendPhdr(ElfuElf *me)
+{
+ ElfuPhdr *phdrmp;
+ ElfuPhdr *newmp;
+
+ ELFU_DEBUG("Appending new PHDR\n");
+
+ /* See if we have enough space for more PHDRs. If not, expand
+ * the PHDR they are in. */
+ phdrmp = elfu_mPhdrByOffset(me, me->ehdr.e_phoff);
+ if (!phdrmp) {
+ /* No LOAD maps PHDRs into memory. Let re-layouter move them. */
+ } else {
+ GElf_Off phdr_maxsz = OFFS_END(phdrmp->phdr.p_offset, phdrmp->phdr.p_filesz);
+ ElfuScn *firstms = CIRCLEQ_FIRST(&phdrmp->childScnList);
+
+ /* How much can the PHDR table expand within its LOAD segment? */
+ if (firstms) {
+ phdr_maxsz = MIN(firstms->shdr.sh_offset, phdr_maxsz);
+ }
+ phdr_maxsz -= me->ehdr.e_phoff;
+
+ /* If we don't have enough space, try to make some by expanding
+ * the LOAD segment we are in. There is no other way. */
+ if (phdr_maxsz < (me->ehdr.e_phnum + 1) * me->ehdr.e_phentsize) {
+ ElfuPhdr *mp;
+ ElfuScn *ms;
+ GElf_Word size = ROUNDUP(me->ehdr.e_phentsize, phdrmp->phdr.p_align);
+
+ /* Move our sections */
+ CIRCLEQ_FOREACH(ms, &phdrmp->childScnList, elemChildScn) {
+ if (ms->shdr.sh_offset >= me->ehdr.e_phoff) {
+ ms->shdr.sh_offset += size;
+ }
+ }
+
+ /* Move our PHDRs */
+ CIRCLEQ_FOREACH(mp, &phdrmp->childPhdrList, elemChildPhdr) {
+ if (mp->phdr.p_offset > me->ehdr.e_phoff) {
+ mp->phdr.p_offset += size;
+ } else {
+ mp->phdr.p_vaddr -= size;
+ mp->phdr.p_paddr -= size;
+ }
+
+ if (mp->phdr.p_type == PT_PHDR) {
+ mp->phdr.p_filesz += me->ehdr.e_phentsize;
+ mp->phdr.p_memsz += me->ehdr.e_phentsize;
+ }
+ }
+
+ /* Move other PHDRs and sections */
+ assert(size <= shiftStuffAtAfterOffset(me, me->ehdr.e_phoff + 1, size));
+
+ /* Remap ourselves */
+ phdrmp->phdr.p_vaddr -= size;
+ phdrmp->phdr.p_paddr -= size;
+ phdrmp->phdr.p_filesz += size;
+ phdrmp->phdr.p_memsz += size;
+ }
+ }
+
+ newmp = elfu_mPhdrAlloc();
+ assert(newmp);
+ CIRCLEQ_INSERT_TAIL(&me->phdrList, newmp, elem);
+
+ return newmp;
+}
+
+
+
+
+
/* Finds a suitable PHDR to insert a hole into and expands it
* if necessary.
* Returns memory address the hole will be mapped to, or 0 if
@@ -94,7 +167,7 @@ GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
&lowestOffs, &highestOffsEnd);
- if (0 && ((w && (highestAddr->phdr.p_flags & PF_W))
+ if (((w && (highestAddr->phdr.p_flags & PF_W))
|| (x && (highestAddr->phdr.p_flags & PF_X)))
/* Merging only works if the LOAD is the last both in file and mem */
&& highestAddr == highestOffsEnd) {
@@ -155,7 +228,7 @@ GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
*injPhdr = highestAddr;
}
return highestAddr->phdr.p_vaddr + (injOffset - highestAddr->phdr.p_offset);
- } else if (0 && ((w && (lowestAddr->phdr.p_flags & PF_W))
+ } else if (((w && (lowestAddr->phdr.p_flags & PF_W))
|| (x && (lowestAddr->phdr.p_flags & PF_X)))
&& /* Enough space to expand downwards? */
(lowestAddr->phdr.p_vaddr > 3 * lowestAddr->phdr.p_align)
@@ -207,8 +280,38 @@ GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
*injPhdr = lowestAddr;
}
return lowestAddr->phdr.p_vaddr + (injOffset - lowestAddr->phdr.p_offset);
+ } else {
+ ElfuPhdr *newmp;
+ GElf_Off injOffset;
+ GElf_Addr injAddr;
+
+ /* Add a new LOAD PHDR. */
+ newmp = appendPhdr(me);
+
+ /* ELF spec: We need (p_offset % p_align) == (p_vaddr % p_align) */
+ injOffset = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
+ injOffset = ROUNDUP(injOffset, align);
+ injAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_memsz);
+ injAddr = ROUNDUP(injAddr, highestAddr->phdr.p_align);
+ injAddr += injOffset % highestAddr->phdr.p_align;
+
+ newmp->phdr.p_align = highestAddr->phdr.p_align;
+ newmp->phdr.p_filesz = size;
+ newmp->phdr.p_memsz = size;
+ newmp->phdr.p_flags = PF_R | (x ? PF_X : 0) | (w ? PF_W : 0);
+ newmp->phdr.p_type = PT_LOAD;
+ newmp->phdr.p_offset = injOffset;
+ newmp->phdr.p_vaddr = injAddr;
+ newmp->phdr.p_paddr = newmp->phdr.p_vaddr;
+
+ *injPhdr = newmp;
+
+ return injAddr;
}
+
+
+
ERROR:
if (injPhdr) {
*injPhdr = NULL;