From bd748f84aeea9600063bf822312a802bf09d1839 Mon Sep 17 00:00:00 2001 From: norly Date: Fri, 31 May 2013 01:43:22 +0100 Subject: [PATCH] Pull out existing re-layouting code --- include/libelfu/modelops.h | 6 + src/model/layout.c | 217 +++++++++++++++++++++++++++++++++++ src/model/phdr.c | 19 +++ src/model/reladd.c | 229 +------------------------------------ 4 files changed, 248 insertions(+), 223 deletions(-) create mode 100644 src/model/layout.c diff --git a/include/libelfu/modelops.h b/include/libelfu/modelops.h index 9495aca..8374b02 100644 --- a/include/libelfu/modelops.h +++ b/include/libelfu/modelops.h @@ -8,6 +8,7 @@ size_t elfu_mPhdrCount(ElfuElf *me); +void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp); typedef int (SectionHandlerFunc)(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2); @@ -18,6 +19,11 @@ char* elfu_mScnName(ElfuElf *me, ElfuScn *ms); 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_mCheck(ElfuElf *me); ElfuScn* elfu_mCloneScn(ElfuScn *ms); diff --git a/src/model/layout.c b/src/model/layout.c new file mode 100644 index 0000000..2febed1 --- /dev/null +++ b/src/model/layout.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include + + + +static GElf_Word shiftStuffAtAfterOffset(ElfuElf *me, + GElf_Off offset, + GElf_Word size) +{ + ElfuPhdr *mp; + ElfuScn *ms; + /* Force a minimum alignment, just to be sure. */ + GElf_Word align = 64; + + /* Find maximum alignment size by which we have to shift. + * Assumes alignment sizes are always 2^x. */ + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_offset >= offset) { + if (mp->phdr.p_align > align) { + align = mp->phdr.p_align; + } + } + } + + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + if (ms->shdr.sh_offset >= offset) { + if (ms->shdr.sh_addralign > align) { + align = ms->shdr.sh_addralign; + } + } + } + + size = ROUNDUP(size, align); + + /* Shift stuff */ + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + + if (mp->phdr.p_offset >= offset) { + mp->phdr.p_offset += size; + + elfu_mPhdrUpdateChildOffsets(mp); + } + } + + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + if (ms->shdr.sh_offset >= offset) { + ms->shdr.sh_offset += size; + } + } + + if (me->ehdr.e_phoff >= offset) { + me->ehdr.e_phoff += size; + } + + if (me->ehdr.e_shoff >= offset) { + me->ehdr.e_shoff += size; + } + + return size; +} + + + + + +/* 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 + * the operation failed. */ +GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size, + GElf_Word align, int w, int x, + ElfuPhdr **injPhdr) +{ + ElfuPhdr *first = NULL; + ElfuPhdr *last = NULL; + ElfuPhdr *mp; + + assert(!(w && x)); + + /* Find first and last LOAD PHDRs. + * Don't compare p_memsz - segments don't overlap in memory. */ + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) { + first = mp; + } + if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) { + last = mp; + } + } + + if ((w && (last->phdr.p_flags & PF_W)) + || (x && (last->phdr.p_flags & PF_X))) { + /* Need to append. */ + GElf_Off injOffset = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz); + GElf_Word injSpace = 0; + GElf_Word nobitsize = last->phdr.p_memsz - last->phdr.p_filesz; + + /* Expand NOBITS if any */ + if (nobitsize > 0) { + GElf_Off endOff = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz); + GElf_Off endAddr = OFFS_END(last->phdr.p_vaddr, last->phdr.p_filesz); + ElfuScn *ms; + + ELFU_INFO("Expanding NOBITS at address 0x%jx...\n", endAddr); + + CIRCLEQ_FOREACH(ms, &last->childScnList, elemChildScn) { + if (ms->shdr.sh_offset == endOff) { + assert(ms->shdr.sh_type == SHT_NOBITS); + assert(ms->shdr.sh_size == nobitsize); + ms->data.d_buf = malloc(ms->shdr.sh_size); + memset(ms->data.d_buf, '\0', ms->shdr.sh_size); + if (!ms->data.d_buf) { + ELFU_WARN("mExpandNobits: Could not allocate %jd bytes for NOBITS expansion. Data may be inconsistent.\n", ms->shdr.sh_size); + assert(0); + goto ERROR; + } + + ms->data.d_align = 1; + ms->data.d_off = 0; + ms->data.d_type = ELF_T_BYTE; + ms->data.d_size = ms->shdr.sh_size; + ms->data.d_version = elf_version(EV_NONE); + + ms->shdr.sh_type = SHT_PROGBITS; + ms->shdr.sh_addr = endAddr; + } + } + + injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize); + injSpace -= nobitsize; + injOffset += nobitsize; + last->phdr.p_filesz += nobitsize; + assert(last->phdr.p_filesz == last->phdr.p_memsz); + } + + /* Calculate how much space we need, taking alignment into account */ + size += ROUNDUP(injOffset, align) - injOffset; + + /* If there is not enough space left, create even more. */ + if (injSpace < size) { + injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace); + } + assert(injSpace >= size); + + /* Remap ourselves */ + last->phdr.p_filesz += size; + last->phdr.p_memsz += size; + + injOffset = ROUNDUP(injOffset, align); + + if (injPhdr) { + *injPhdr = last; + } + return last->phdr.p_vaddr + (injOffset - last->phdr.p_offset); + } else if ((w && (first->phdr.p_flags & PF_W)) + || (x && (first->phdr.p_flags & PF_X))) { + /* Need to prepend or split up the PHDR. */ + GElf_Off injOffset = OFFS_END(first->phdr.p_offset, first->phdr.p_filesz); + ElfuScn *ms; + + /* Round up size to take PHDR alignment into account. + * We assume that this is a multiple of the alignment asked for. */ + assert(first->phdr.p_align >= align); + size = ROUNDUP(size, first->phdr.p_align); + + /* Find first section. We assume there is at least one. */ + assert(!CIRCLEQ_EMPTY(&first->childScnList)); + injOffset = CIRCLEQ_FIRST(&first->childScnList)->shdr.sh_offset; + + /* Move our sections */ + CIRCLEQ_FOREACH(ms, &first->childScnList, elemChildScn) { + if (ms->shdr.sh_offset >= injOffset) { + ms->shdr.sh_offset += size; + } + } + + /* Move our PHDRs */ + CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) { + if (mp->phdr.p_offset >= injOffset) { + mp->phdr.p_offset += size; + } else { + mp->phdr.p_vaddr -= size; + mp->phdr.p_paddr -= size; + } + } + + /* Move other PHDRs and sections */ + assert(size <= shiftStuffAtAfterOffset(me, injOffset, size)); + + /* Remap ourselves */ + first->phdr.p_vaddr -= size; + first->phdr.p_paddr -= size; + first->phdr.p_filesz += size; + first->phdr.p_memsz += size; + + injOffset = ROUNDUP(injOffset, align); + + if (injPhdr) { + *injPhdr = first; + } + return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset); + } + + ERROR: + if (injPhdr) { + *injPhdr = NULL; + } + return 0; +} diff --git a/src/model/phdr.c b/src/model/phdr.c index 26a26e3..d26eb77 100644 --- a/src/model/phdr.c +++ b/src/model/phdr.c @@ -15,3 +15,22 @@ size_t elfu_mPhdrCount(ElfuElf *me) return i; } + + + +void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp) +{ + ElfuScn *ms; + ElfuPhdr *mpc; + + assert(mp); + assert(mp->phdr.p_type == PT_LOAD); + + CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) { + mpc->phdr.p_offset = mp->phdr.p_offset + (mpc->phdr.p_vaddr - mp->phdr.p_vaddr); + } + + CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) { + ms->shdr.sh_offset = mp->phdr.p_offset + (ms->shdr.sh_addr - mp->phdr.p_vaddr); + } +} diff --git a/src/model/reladd.c b/src/model/reladd.c index bc13b97..ffd02f4 100644 --- a/src/model/reladd.c +++ b/src/model/reladd.c @@ -6,224 +6,6 @@ #include - -static GElf_Word makeSpaceAtOffset(ElfuElf *me, GElf_Off offset, GElf_Word size) -{ - ElfuPhdr *mp; - ElfuScn *ms; - /* Force a minimum alignment, just to be sure. */ - GElf_Word align = 64; - - /* Find maximum alignment size by which we have to shift. - * Assumes alignment sizes are always 2^x. */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - if (mp->phdr.p_offset >= offset) { - if (mp->phdr.p_align > align) { - align = mp->phdr.p_align; - } - } - } - - CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { - if (ms->shdr.sh_offset >= offset) { - if (ms->shdr.sh_addralign > align) { - align = ms->shdr.sh_addralign; - } - } - } - - size = ROUNDUP(size, align); - - /* Shift stuff */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - if (mp->phdr.p_type != PT_LOAD) { - continue; - } - - if (mp->phdr.p_offset >= offset) { - ElfuScn *ms; - ElfuPhdr *mpc; - - mp->phdr.p_offset += size; - CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) { - mpc->phdr.p_offset += size; - } - CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) { - ms->shdr.sh_offset += size; - } - } - } - - CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { - if (ms->shdr.sh_offset >= offset) { - ms->shdr.sh_offset += size; - } - } - - if (me->ehdr.e_phoff >= offset) { - me->ehdr.e_phoff += size; - } - - if (me->ehdr.e_shoff >= offset) { - me->ehdr.e_shoff += size; - } - - return size; -} - - - -/* 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 - * the operation failed. */ -static GElf_Addr getSpaceInPhdr(ElfuElf *me, GElf_Word size, - GElf_Word align, int w, int x, - ElfuPhdr **injPhdr) -{ - ElfuPhdr *first = NULL; - ElfuPhdr *last = NULL; - ElfuPhdr *mp; - - assert(!(w && x)); - - /* Find first and last LOAD PHDRs. - * Don't compare p_memsz - segments don't overlap in memory. */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - if (mp->phdr.p_type != PT_LOAD) { - continue; - } - if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) { - first = mp; - } - if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) { - last = mp; - } - } - - if ((w && (last->phdr.p_flags & PF_W)) - || (x && (last->phdr.p_flags & PF_X))) { - /* Need to append. */ - GElf_Off injOffset = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz); - GElf_Word injSpace = 0; - GElf_Word nobitsize = last->phdr.p_memsz - last->phdr.p_filesz; - - /* Expand NOBITS if any */ - if (nobitsize > 0) { - GElf_Off endOff = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz); - GElf_Off endAddr = OFFS_END(last->phdr.p_vaddr, last->phdr.p_filesz); - ElfuScn *ms; - - ELFU_INFO("Expanding NOBITS at address 0x%jx...\n", endAddr); - - CIRCLEQ_FOREACH(ms, &last->childScnList, elemChildScn) { - if (ms->shdr.sh_offset == endOff) { - assert(ms->shdr.sh_type == SHT_NOBITS); - assert(ms->shdr.sh_size == nobitsize); - ms->data.d_buf = malloc(ms->shdr.sh_size); - memset(ms->data.d_buf, '\0', ms->shdr.sh_size); - if (!ms->data.d_buf) { - ELFU_WARN("mExpandNobits: Could not allocate %jd bytes for NOBITS expansion. Data may be inconsistent.\n", ms->shdr.sh_size); - assert(0); - goto ERROR; - } - - ms->data.d_align = 1; - ms->data.d_off = 0; - ms->data.d_type = ELF_T_BYTE; - ms->data.d_size = ms->shdr.sh_size; - ms->data.d_version = elf_version(EV_NONE); - - ms->shdr.sh_type = SHT_PROGBITS; - ms->shdr.sh_addr = endAddr; - } - } - - injSpace += makeSpaceAtOffset(me, endOff, nobitsize); - injSpace -= nobitsize; - injOffset += nobitsize; - last->phdr.p_filesz += nobitsize; - assert(last->phdr.p_filesz == last->phdr.p_memsz); - } - - /* Calculate how much space we need, taking alignment into account */ - size += ROUNDUP(injOffset, align) - injOffset; - - /* If there is not enough space left, create even more. */ - if (injSpace < size) { - injSpace += makeSpaceAtOffset(me, injOffset, size - injSpace); - } - assert(injSpace >= size); - - /* Remap ourselves */ - last->phdr.p_filesz += size; - last->phdr.p_memsz += size; - - injOffset = ROUNDUP(injOffset, align); - - if (injPhdr) { - *injPhdr = last; - } - return last->phdr.p_vaddr + (injOffset - last->phdr.p_offset); - } else if ((w && (first->phdr.p_flags & PF_W)) - || (x && (first->phdr.p_flags & PF_X))) { - /* Need to prepend or split up the PHDR. */ - GElf_Off injOffset = OFFS_END(first->phdr.p_offset, first->phdr.p_filesz); - ElfuScn *ms; - - /* Round up size to take PHDR alignment into account. - * We assume that this is a multiple of the alignment asked for. */ - assert(first->phdr.p_align >= align); - size = ROUNDUP(size, first->phdr.p_align); - - /* Find first section. We assume there is at least one. */ - assert(!CIRCLEQ_EMPTY(&first->childScnList)); - injOffset = CIRCLEQ_FIRST(&first->childScnList)->shdr.sh_offset; - - /* Move our sections */ - CIRCLEQ_FOREACH(ms, &first->childScnList, elemChildScn) { - if (ms->shdr.sh_offset >= injOffset) { - ms->shdr.sh_offset += size; - } - } - - /* Move our PHDRs */ - CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) { - if (mp->phdr.p_offset >= injOffset) { - mp->phdr.p_offset += size; - } else { - mp->phdr.p_vaddr -= size; - mp->phdr.p_paddr -= size; - } - } - - /* Move other PHDRs and sections */ - assert(size <= makeSpaceAtOffset(me, injOffset, size)); - - /* Remap ourselves */ - first->phdr.p_vaddr -= size; - first->phdr.p_paddr -= size; - first->phdr.p_filesz += size; - first->phdr.p_memsz += size; - - injOffset = ROUNDUP(injOffset, align); - - if (injPhdr) { - *injPhdr = first; - } - return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset); - } - - ERROR: - if (injPhdr) { - *injPhdr = NULL; - } - return 0; -} - - - - static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn) { ElfuScn *newscn = NULL; @@ -238,11 +20,12 @@ static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn) } - injAddr = getSpaceInPhdr(me, newscn->shdr.sh_size, - newscn->shdr.sh_addralign, - newscn->shdr.sh_flags & SHF_WRITE, - newscn->shdr.sh_flags & SHF_EXECINSTR, - &injPhdr); + injAddr = elfu_mLayoutGetSpaceInPhdr(me, + newscn->shdr.sh_size, + newscn->shdr.sh_addralign, + newscn->shdr.sh_flags & SHF_WRITE, + newscn->shdr.sh_flags & SHF_EXECINSTR, + &injPhdr); if (!injPhdr) { ELFU_WARN("insertSection: Could not find a place to insert section.\n"); -- 2.30.2