From 4addee4bda6064926b24cd1ae929303003bd9ff1 Mon Sep 17 00:00:00 2001 From: norly Date: Thu, 30 May 2013 04:01:51 +0100 Subject: [PATCH] Redesign data structures, make basic reladd work. The memory ELF model is now a tree structure: ELF +--> PHDRs +--> PHDR +--> Section | | +--> Section | | ... | | \--> Section | | | +--> PHDR +--> Section | | ... | ... | \--> Orphaned sections +--> Section ... \--> Section This effectively introduces semantics into the binary blob we are editing, and allows us to re-layout its contents much more easily while keeping as close as possible to what is assumed to be the original semantics. As a side-effect, a first meta-function had to be introduced (elfu_mScnForall) in order to traverse all leaves of the tree. Much old code has been removed given the leaner environment available now, and automated insertion of .text and .data sections from object files into executables now works. However nothing else is inserted (such as string tables or .bss) and no relocation takes place yet. --- include/libelfu/generic.h | 4 + include/libelfu/libelfu.h | 1 - include/libelfu/modelops.h | 26 +- include/libelfu/modeltypes.h | 46 --- include/libelfu/types.h | 41 +++ src/main.c | 14 +- src/model/check.c | 63 +---- src/model/clone.c | 7 +- src/model/count.c | 55 ---- src/model/dump.c | 20 +- src/model/expandNobits.c | 116 -------- src/model/fromFile.c | 9 +- src/model/insert.c | 289 ------------------- src/model/phdr.c | 17 ++ src/model/reladd.c | 494 +++++++++++++++++++++------------ src/model/section-by-type.c | 22 -- src/model/section-in-segment.c | 56 ---- src/model/section-name.c | 23 -- src/model/section.c | 170 ++++++++++++ src/model/toFile.c | 30 +- src/options.c | 20 +- 21 files changed, 616 insertions(+), 907 deletions(-) delete mode 100644 include/libelfu/modeltypes.h delete mode 100644 src/model/count.c delete mode 100644 src/model/expandNobits.c delete mode 100644 src/model/insert.c create mode 100644 src/model/phdr.c delete mode 100644 src/model/section-by-type.c delete mode 100644 src/model/section-in-segment.c delete mode 100644 src/model/section-name.c create mode 100644 src/model/section.c diff --git a/include/libelfu/generic.h b/include/libelfu/generic.h index 6a06634..c8a9c79 100644 --- a/include/libelfu/generic.h +++ b/include/libelfu/generic.h @@ -3,6 +3,10 @@ #include + +#define ROUNDUP(x, align) ((x) + ((align) - ((x) % (align))) % (align)) + + #define OFFS_END(off, sz) ((off) + (sz)) #define OVERLAPPING(off1, sz1, off2, sz2) \ diff --git a/include/libelfu/libelfu.h b/include/libelfu/libelfu.h index cc55282..328a75d 100644 --- a/include/libelfu/libelfu.h +++ b/include/libelfu/libelfu.h @@ -6,7 +6,6 @@ #include #include -#include #include #include diff --git a/include/libelfu/modelops.h b/include/libelfu/modelops.h index 31a4ed9..9495aca 100644 --- a/include/libelfu/modelops.h +++ b/include/libelfu/modelops.h @@ -4,19 +4,19 @@ #include #include -#include +#include -size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms); -size_t elfu_mCountScns(ElfuElf *me); -size_t elfu_mCountPhdrs(ElfuElf *me); +size_t elfu_mPhdrCount(ElfuElf *me); -char* elfu_mScnName(ElfuElf *me, ElfuScn *ms); -ElfuScn* elfu_mScnFirstInSegment(ElfuElf *me, ElfuPhdr *mp); -ElfuScn* elfu_mScnLastInSegment(ElfuElf *me, ElfuPhdr *mp); +typedef int (SectionHandlerFunc)(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2); +int elfu_mScnForall(ElfuElf *me, SectionHandlerFunc f, void *aux1, void *aux2); +size_t elfu_mScnCount(ElfuElf *me); +size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms); +char* elfu_mScnName(ElfuElf *me, ElfuScn *ms); +ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count); -ElfuScn* elfu_mScnByType(ElfuElf *me, Elf32_Word type); int elfu_mCheck(ElfuElf *me); @@ -31,16 +31,6 @@ void elfu_mDumpElf(ElfuElf *me); ElfuElf* elfu_mFromElf(Elf *e); void elfu_mToElf(ElfuElf *me, Elf *e); - - void elfu_mExpandNobits(ElfuElf *me, GElf_Off off); - -GElf_Xword elfu_mInsertSpaceBefore(ElfuElf *me, GElf_Off off, GElf_Xword size); -GElf_Xword elfu_mInsertSpaceAfter(ElfuElf *me, GElf_Off off, GElf_Xword size); - -void elfu_mInsertScnInChainBefore(ElfuElf *me, ElfuScn *oldscn, ElfuScn *newscn); -void elfu_mInsertScnInChainAfter(ElfuElf *me, ElfuScn *oldscn, ElfuScn *newscn); - - void elfu_mReladd(ElfuElf *me, ElfuElf *mrel); #endif diff --git a/include/libelfu/modeltypes.h b/include/libelfu/modeltypes.h deleted file mode 100644 index bea9322..0000000 --- a/include/libelfu/modeltypes.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef __LIBELFU_MODELTYPES_H__ -#define __LIBELFU_MODELTYPES_H__ - -#include - -#include -#include - - -typedef struct ElfuScn { - GElf_Shdr shdr; - - Elf_Data data; - - struct ElfuScn *linkptr; - struct ElfuScn *infoptr; - - CIRCLEQ_ENTRY(ElfuScn) elemChildScn; - CIRCLEQ_ENTRY(ElfuScn) elem; -} ElfuScn; - - -typedef struct ElfuPhdr { - GElf_Phdr phdr; - - CIRCLEQ_HEAD(ChildScnList, ElfuScn) childScnList; - CIRCLEQ_HEAD(ChildPhdrList, ElfuPhdr) childPhdrList; - - CIRCLEQ_ENTRY(ElfuPhdr) elemChildPhdr; - CIRCLEQ_ENTRY(ElfuPhdr) elem; -} ElfuPhdr; - - -typedef struct { - int elfclass; - GElf_Ehdr ehdr; - - CIRCLEQ_HEAD(ScnList, ElfuScn) scnList; - CIRCLEQ_HEAD(PhdrList, ElfuPhdr) phdrList; - CIRCLEQ_HEAD(OrphanScnList, ElfuScn) orphanScnList; - - ElfuScn *shstrtab; -} ElfuElf; - - -#endif diff --git a/include/libelfu/types.h b/include/libelfu/types.h index a59efca..a06d470 100644 --- a/include/libelfu/types.h +++ b/include/libelfu/types.h @@ -1,6 +1,47 @@ #ifndef __LIBELFU_TYPES_H__ #define __LIBELFU_TYPES_H__ +#include + +#include +#include + + +typedef struct ElfuScn { + GElf_Shdr shdr; + + Elf_Data data; + + struct ElfuScn *linkptr; + struct ElfuScn *infoptr; + + struct ElfuScn *oldptr; + + CIRCLEQ_ENTRY(ElfuScn) elemChildScn; + CIRCLEQ_ENTRY(ElfuScn) elem; +} ElfuScn; + + +typedef struct ElfuPhdr { + GElf_Phdr phdr; + + CIRCLEQ_HEAD(ChildScnList, ElfuScn) childScnList; + CIRCLEQ_HEAD(ChildPhdrList, ElfuPhdr) childPhdrList; + + CIRCLEQ_ENTRY(ElfuPhdr) elemChildPhdr; + CIRCLEQ_ENTRY(ElfuPhdr) elem; +} ElfuPhdr; + + +typedef struct { + int elfclass; + GElf_Ehdr ehdr; + + CIRCLEQ_HEAD(PhdrList, ElfuPhdr) phdrList; + CIRCLEQ_HEAD(OrphanScnList, ElfuScn) orphanScnList; + + ElfuScn *shstrtab; +} ElfuElf; #endif diff --git a/src/main.c b/src/main.c index 0eb6e00..a179fcd 100644 --- a/src/main.c +++ b/src/main.c @@ -59,18 +59,6 @@ int main(int argc, char **argv) if (!opts.fnOutput) { printf("No output file specified - no further operations performed.\n"); } else { - if (opts.expandNobitsOffs) { - elfu_mExpandNobits(me, opts.expandNobitsOffs); - } - - if (opts.insertBeforeSz) { - elfu_mInsertSpaceBefore(me, opts.insertBeforeOffs, opts.insertBeforeSz); - } - - if (opts.insertAfterSz) { - elfu_mInsertSpaceAfter(me, opts.insertAfterOffs, opts.insertAfterSz); - } - if (opts.fnReladd) { ELFHandles hRel = { 0 }; ElfuElf *mrel = NULL; @@ -90,12 +78,12 @@ int main(int argc, char **argv) elfu_mReladd(me, mrel); } } - } elfu_mCheck(me); printf("Output model checked.\n"); + elfu_mDumpElf(me); openElf(&hOut, opts.fnOutput, ELF_C_WRITE); if (!hOut.e) { diff --git a/src/model/check.c b/src/model/check.c index 6dc0694..5234bef 100644 --- a/src/model/check.c +++ b/src/model/check.c @@ -4,66 +4,16 @@ #include - -static int isOverlapping(size_t off1, size_t sz1, size_t off2, size_t sz2) -{ - size_t end1 = off1 + sz1; - size_t end2 = off2 + sz2; - - if (off2 >= off1 && off2 < end1) { - return 1; - } else if (off1 >= off2 && off1 < end2) { - return 1; - } else { - return 0; - } -} - - -static int cmpScnOffs(const void *ms1, const void *ms2) -{ - assert(ms1); - assert(ms2); - - ElfuScn *s1 = *(ElfuScn**)ms1; - ElfuScn *s2 = *(ElfuScn**)ms2; - - assert(s1); - assert(s2); - - - if (s1->shdr.sh_offset < s2->shdr.sh_offset) { - return -1; - } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) { - return 0; - } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ { - return 1; - } -} - - int elfu_mCheck(ElfuElf *me) { - ElfuScn *ms; size_t numSecs; ElfuScn **sortedSecs; size_t i; - /* Sort sections by offset in file */ - numSecs = elfu_mCountScns(me); - sortedSecs = malloc(numSecs * sizeof(*sortedSecs)); + sortedSecs = elfu_mScnSortedByOffset(me, &numSecs); if (!sortedSecs) { - ELFU_WARN("elfu_check: Failed to allocate memory.\n"); - } - - i = 0; - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - sortedSecs[i] = ms; - i++; + return -1; } - assert(i == numSecs); - - qsort(sortedSecs, numSecs, sizeof(*sortedSecs), cmpScnOffs); /* Check for overlapping sections */ @@ -88,15 +38,16 @@ int elfu_mCheck(ElfuElf *me) /* Check for sections overlapping with PHDRs */ for (i = 0; i < numSecs; i++) { - if (isOverlapping(sortedSecs[i]->shdr.sh_offset, - SCNFILESIZE(&sortedSecs[i]->shdr), - me->ehdr.e_phoff, - me->ehdr.e_phentsize * me->ehdr.e_phnum)) { + if (OVERLAPPING(sortedSecs[i]->shdr.sh_offset, + SCNFILESIZE(&sortedSecs[i]->shdr), + me->ehdr.e_phoff, + me->ehdr.e_phentsize * me->ehdr.e_phnum)) { ELFU_WARN("elfu_check: Found section overlapping with PHDRs: %s.\n", elfu_mScnName(me, sortedSecs[i])); } } + free(sortedSecs); return 0; diff --git a/src/model/clone.c b/src/model/clone.c index 3dbbc54..644647c 100644 --- a/src/model/clone.c +++ b/src/model/clone.c @@ -15,8 +15,6 @@ ElfuScn* elfu_mCloneScn(ElfuScn *ms) return NULL; } - - newscn->shdr = ms->shdr; newscn->data = ms->data; if (ms->data.d_buf) { @@ -31,5 +29,10 @@ ElfuScn* elfu_mCloneScn(ElfuScn *ms) newscn->data.d_buf = newbuf; } + newscn->linkptr = NULL; + newscn->infoptr = NULL; + + newscn->oldptr = ms; + return newscn; } diff --git a/src/model/count.c b/src/model/count.c deleted file mode 100644 index 671767e..0000000 --- a/src/model/count.c +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include - - -size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms) -{ - ElfuScn *ms2; - size_t i = 1; - - assert(me); - assert(ms); - - CIRCLEQ_FOREACH(ms2, &me->scnList, elem) { - if (ms2 == ms) { - return i; - } - - i++; - } - - /* Section is not in ELF model. This means the calling code is broken. */ - assert(0); -} - - -/* NULL section is not counted! */ -size_t elfu_mCountScns(ElfuElf *me) -{ - ElfuScn *ms; - size_t i = 0; - - assert(me); - - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - i++; - } - - return i; -} - - -size_t elfu_mCountPhdrs(ElfuElf *me) -{ - ElfuPhdr *mp; - size_t i = 0; - - assert(me); - - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - i++; - } - - return i; -} diff --git a/src/model/dump.c b/src/model/dump.c index 67a498a..5f0642a 100644 --- a/src/model/dump.c +++ b/src/model/dump.c @@ -184,6 +184,19 @@ void elfu_mDumpEhdr(ElfuElf *me) } + +static int subScnDump(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + (void) aux1; + (void) aux2; + + printf(" [%4d] ", elfu_mScnIndex(me, ms)); + elfu_mDumpScn(me, ms); + + return 0; +} + + void elfu_mDumpElf(ElfuElf *me) { ElfuPhdr *mp; @@ -221,11 +234,6 @@ void elfu_mDumpElf(ElfuElf *me) ELFU_INFO("Sections:\n"); ELFU_INFO(" # Name sh_type sh_addr sh_offset sh_size ES Fl Al sh_link sh_info \n"); ELFU_INFO(" | | | | | | | | | | \n"); - i = 1; - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - printf(" [%4d] ", i); - elfu_mDumpScn(me, ms); - i++; - } + elfu_mScnForall(me, subScnDump, &i, NULL); ELFU_INFO("\n"); } diff --git a/src/model/expandNobits.c b/src/model/expandNobits.c deleted file mode 100644 index d12990e..0000000 --- a/src/model/expandNobits.c +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include -#include -#include -#include -#include - - -void elfu_mExpandNobits(ElfuElf *me, GElf_Off off) -{ - ElfuScn *ms; - ElfuPhdr *mp; - GElf_Xword expansionSize; - - assert(me); - - expansionSize = 0; - - /* Find the maximum amount we need to expand by. Check PHDRs first */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - GElf_Off off2 = mp->phdr.p_offset; - GElf_Off end2 = mp->phdr.p_offset + mp->phdr.p_filesz; - if (end2 == off) { - GElf_Xword size2 = mp->phdr.p_memsz - mp->phdr.p_filesz; - if (size2 > expansionSize) { - expansionSize = size2; - } - } else if (end2 > off) { - if (off2 < off) { - /* - * Found a PHDR whose file contents overlaps with the section - * to be filled. This means that it relies on the NOBITS area - * being actually 0 bytes, and the expansion would ruin it. - */ - ELFU_WARN("mExpandNobits: Found PHDR spanning expansion offset. Aborting.\n"); - return; - } - } else { - // end2 < off, and the PHDR is unaffected. - continue; - } - } - - /* Now check SHDRs */ - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - if (ms->shdr.sh_offset == off) { - if (ms->shdr.sh_type == SHT_NOBITS) { - if (ms->shdr.sh_size > expansionSize) { - expansionSize = ms->shdr.sh_size; - } - } - } - } - - - - /* Expand! */ - - - /* Move all following PHDR offsets further down the file. */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - GElf_Off off2 = mp->phdr.p_offset; - GElf_Off end2 = mp->phdr.p_offset + mp->phdr.p_filesz; - - if (off2 >= off) { - mp->phdr.p_offset += expansionSize; - } else { - if (end2 == off) { - /* This PHDR now has corresponding bytes in the file for every - * byte in memory. */ - mp->phdr.p_filesz = mp->phdr.p_memsz; - } - } - } - - /* Move the following sections */ - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - if (ms->shdr.sh_offset >= off) { - if (ms->shdr.sh_offset > off - || ms->shdr.sh_type != SHT_NOBITS) { - ms->shdr.sh_offset += expansionSize; - } - } - } - - /* Move SHDR/PHDR tables */ - if (me->ehdr.e_shoff >= off) { - me->ehdr.e_shoff += expansionSize; - } - - if (me->ehdr.e_phoff >= off) { - me->ehdr.e_phoff += expansionSize; - } - - - /* Convert any NOBITS at off to PROGBITS */ - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - if (ms->shdr.sh_offset == off) { - if (ms->shdr.sh_type == SHT_NOBITS) { - 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.\n", ms->shdr.sh_size); - } - - 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; - } - } - } -} diff --git a/src/model/fromFile.c b/src/model/fromFile.c index cb09352..ee0536d 100644 --- a/src/model/fromFile.c +++ b/src/model/fromFile.c @@ -132,6 +132,8 @@ static ElfuScn* modelFromSection(Elf_Scn *scn) ms->linkptr = NULL; ms->infoptr = NULL; + ms->oldptr = NULL; + return ms; @@ -165,7 +167,6 @@ ElfuElf* elfu_mFromElf(Elf *e) /* General stuff */ - CIRCLEQ_INIT(&me->scnList); CIRCLEQ_INIT(&me->phdrList); CIRCLEQ_INIT(&me->orphanScnList); me->shstrtab = NULL; @@ -291,12 +292,6 @@ ElfuElf* elfu_mFromElf(Elf *e) CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn); } } - - - /* Put sections into list of all sections */ - for (i = 0; i < numShdr - 1; i++) { - CIRCLEQ_INSERT_TAIL(&me->scnList, secArray[i], elem); - } } diff --git a/src/model/insert.c b/src/model/insert.c deleted file mode 100644 index b18aa8a..0000000 --- a/src/model/insert.c +++ /dev/null @@ -1,289 +0,0 @@ -#include -#include -#include -#include - - -// TODO: Take p_align into account - - - -/* - * Insert space at a given position in the file by moving everything - * after it towards the end of the file, and everything before it - * towards lower memory regions where it is mapped. - * - * off must not be in the middle of any data structure, such as - * PHDRs, SHDRs, or sections. Behaviour is undefined if it is. - * - * PHDRs will be patched such that everything AFTER off is mapped to - * the same address in memory, and everything BEFORE it is shifted to - * lower addresses, making space for the new data in-between. - */ -GElf_Xword elfu_mInsertSpaceBefore(ElfuElf *me, GElf_Off off, GElf_Xword size) -{ - ElfuScn *ms; - ElfuPhdr *mp; - - assert(me); - - /* Round up size to 4096 bytes to keep page alignment on x86 when - * remapping existing data to lower addresses. */ - size += (4096 - (size % 4096)) % 4096; - // TODO: Find alignment size by checking p_align in PHDRs - - /* Move SHDRs and PHDRs */ - if (me->ehdr.e_shoff >= off) { - me->ehdr.e_shoff += size; - } - - if (me->ehdr.e_phoff >= off) { - me->ehdr.e_phoff += size; - } - - /* Patch PHDRs to include new data */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - GElf_Off end = mp->phdr.p_offset + mp->phdr.p_filesz; - - if (mp->phdr.p_offset >= off) { - /* Insertion before PHDR's content, so it's just shifted */ - mp->phdr.p_offset += size; - } else { - /* mp->phdr.p_offset < off */ - - if (off < end) { - /* Insertion in the middle of PHDR, so let it span the new data */ - mp->phdr.p_filesz += size; - mp->phdr.p_memsz += size; - mp->phdr.p_vaddr -= size; - mp->phdr.p_paddr -= size; - } else { - /* Insertion after PHDR's content, so it may need to be - remapped. This will happen in a second pass. - */ - } - } - } - - /* For each LOAD header, find clashing headers that need to be - remapped to lower memory areas. - */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - if (mp->phdr.p_type == PT_LOAD) { - ElfuPhdr *mp2; - - CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) { - if (mp2->phdr.p_type != PT_LOAD - && mp2->phdr.p_offset + mp2->phdr.p_filesz <= off) { - /* The PHDR ends in the file before the injection site */ - GElf_Off vend1 = mp->phdr.p_vaddr + mp->phdr.p_memsz; - GElf_Off pend1 = mp->phdr.p_paddr + mp->phdr.p_memsz; - GElf_Off vend2 = mp2->phdr.p_vaddr + mp2->phdr.p_memsz; - GElf_Off pend2 = mp2->phdr.p_paddr + mp2->phdr.p_memsz; - - /* If mp and mp2 now overlap in memory */ - if ((mp2->phdr.p_vaddr < vend1 && vend2 > mp->phdr.p_vaddr) - || (mp2->phdr.p_paddr < pend1 && pend2 > mp->phdr.p_paddr)) { - /* Move mp2 down in memory, as mp has been resized. - Maintaining the relative offset between them is the best - guess at maintaining consistency. - */ - mp2->phdr.p_vaddr -= size; - mp2->phdr.p_paddr -= size; - } - } - } - } - } - - /* Move the sections themselves */ - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - if (ms->shdr.sh_offset >= off) { - ms->shdr.sh_offset += size; - } else { - /* sh_offset < off */ - - /* If this was in a LOAD segment, it has been adjusted there - and this synchronises it. - If not, it doesn't matter anyway. - */ - ms->shdr.sh_addr -= size; - } - } - - return size; -} - - - -/* - * Insert space at a given position in the file by moving everything - * after it towards the end of the file, and towards higher memory - * regions where it is mapped. - * - * off must not be in the middle of any data structure, such as - * PHDRs, SHDRs, or sections. Behaviour is undefined if it is. - * - * PHDRs will be patched such that everything AFTER off is shifted to - * higher addresses, making space for the new data in-between. - * - * CAUTION: This also moves NOBITS sections. If such are present, - * use mExpandNobits() first and then inject at the end of - * the expansion site. - */ -GElf_Xword elfu_mInsertSpaceAfter(ElfuElf *me, GElf_Off off, GElf_Xword size) -{ - ElfuScn *ms; - ElfuPhdr *mp; - - assert(me); - -/* Move SHDRs and PHDRs */ - if (me->ehdr.e_shoff >= off) { - me->ehdr.e_shoff += size; - } - - if (me->ehdr.e_phoff >= off) { - me->ehdr.e_phoff += size; - } - - /* Patch PHDRs to include new data */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - GElf_Off end = mp->phdr.p_offset + mp->phdr.p_filesz; - - if (mp->phdr.p_offset >= off) { - /* Insertion before PHDR's content, so it's shifted. */ - mp->phdr.p_offset += size; - - /* It may also need to be remapped. See second pass below. */ - } else { - /* mp->phdr.p_offset < off */ - - if (off < end) { - /* Insertion in the middle of PHDR, so let it span the new data */ - mp->phdr.p_filesz += size; - mp->phdr.p_memsz += size; - } else { - /* Insertion after PHDR's content. Nothing to do. */ - } - } - } - - /* For each LOAD header, find clashing headers that need to be - remapped to higher memory areas. - */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - if (mp->phdr.p_type == PT_LOAD) { - ElfuPhdr *mp2; - - CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) { - if (mp2->phdr.p_type != PT_LOAD - && mp2->phdr.p_offset + mp2->phdr.p_filesz > off) { - /* The PHDR now ends in the file after the injection site */ - GElf_Off vend1 = mp->phdr.p_vaddr + mp->phdr.p_memsz; - GElf_Off pend1 = mp->phdr.p_paddr + mp->phdr.p_memsz; - GElf_Off vend2 = mp2->phdr.p_vaddr + mp2->phdr.p_memsz; - GElf_Off pend2 = mp2->phdr.p_paddr + mp2->phdr.p_memsz; - - /* If mp and mp2 now overlap in memory */ - if ((mp2->phdr.p_vaddr < vend1 && vend2 > mp->phdr.p_vaddr) - || (mp2->phdr.p_paddr < pend1 && pend2 > mp->phdr.p_paddr)) { - /* Move mp2 up in memory, as mp has been resized. - Maintaining the relative offset between them is the best - guess at maintaining consistency. - */ - - mp2->phdr.p_vaddr += size; - mp2->phdr.p_paddr += size; - } - } - } - } - } - - /* Move the sections themselves */ - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - if (ms->shdr.sh_offset >= off) { - ms->shdr.sh_offset += size; - - /* If this was in a LOAD segment, it has been adjusted there - and this synchronises it. - If not, it doesn't matter anyway. - */ - ms->shdr.sh_addr += size; - } - } - - return size; -} - - - - - -/* Update cross-references */ -static void shiftSections(ElfuElf *me, ElfuScn *first) -{ - ElfuScn *ms = first; - size_t firstIndex = elfu_mScnIndex(me, first); - - do { - if (ms == me->shstrtab) { - me->ehdr.e_shstrndx++; - } - - ms = CIRCLEQ_LOOP_NEXT(&me->scnList, ms, elem); - } while (ms != CIRCLEQ_FIRST(&me->scnList)); - - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - switch (ms->shdr.sh_type) { - case SHT_REL: - case SHT_RELA: - if (ms->shdr.sh_info >= firstIndex) { - ms->shdr.sh_info++; - } - case SHT_DYNAMIC: - case SHT_HASH: - case SHT_SYMTAB: - case SHT_DYNSYM: - case SHT_GNU_versym: - case SHT_GNU_verdef: - case SHT_GNU_verneed: - if (ms->shdr.sh_link >= firstIndex) { - ms->shdr.sh_link++; - } - } - } -} - - -/* - * Insert a section into an ELF model, /before/ a given other section - */ -void elfu_mInsertScnInChainBefore(ElfuElf *me, ElfuScn *oldscn, ElfuScn *newscn) -{ - assert(me); - assert(oldscn); - assert(newscn); - - shiftSections(me, oldscn); - - CIRCLEQ_INSERT_BEFORE(&me->scnList, oldscn, newscn, elem); -} - - -/* - * Insert a section into an ELF model, /after/ a given other section - */ -void elfu_mInsertScnInChainAfter(ElfuElf *me, ElfuScn *oldscn, ElfuScn *newscn) -{ - assert(me); - assert(oldscn); - assert(newscn); - - if (oldscn != CIRCLEQ_LAST(&me->scnList)) { - shiftSections(me, CIRCLEQ_NEXT(oldscn, elem)); - } - - CIRCLEQ_INSERT_AFTER(&me->scnList, oldscn, newscn, elem); -} diff --git a/src/model/phdr.c b/src/model/phdr.c new file mode 100644 index 0000000..26a26e3 --- /dev/null +++ b/src/model/phdr.c @@ -0,0 +1,17 @@ +#include +#include + + +size_t elfu_mPhdrCount(ElfuElf *me) +{ + ElfuPhdr *mp; + size_t i = 0; + + assert(me); + + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + i++; + } + + return i; +} diff --git a/src/model/reladd.c b/src/model/reladd.c index 549afcf..bc13b97 100644 --- a/src/model/reladd.c +++ b/src/model/reladd.c @@ -1,177 +1,292 @@ #include #include +#include #include #include #include -typedef enum Destsegment { - DS_UNKNOWN, - DS_TEXT, - DS_DATA, -} Destsegment; -static Destsegment destsegment(ElfuScn *ms) +static GElf_Word makeSpaceAtOffset(ElfuElf *me, GElf_Off offset, GElf_Word size) { - if (!(ms->shdr.sh_flags & SHF_ALLOC)) { - return DS_UNKNOWN; + 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; + } + } } - if (!(ms->shdr.sh_flags & SHF_WRITE) - && (ms->shdr.sh_flags & SHF_EXECINSTR)) { - return DS_TEXT; - } else if ((ms->shdr.sh_flags & SHF_WRITE) - && !(ms->shdr.sh_flags & SHF_EXECINSTR)) { - return DS_DATA; + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + if (ms->shdr.sh_offset >= offset) { + if (ms->shdr.sh_addralign > align) { + align = ms->shdr.sh_addralign; + } + } } - return DS_UNKNOWN; + 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; } -static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *ms) +/* 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 *mp; ElfuPhdr *first = NULL; ElfuPhdr *last = NULL; - ElfuScn *newscn = NULL; - ElfuPhdr *injAnchor; - int searchForCode = 0; - - switch (destsegment(ms)) { - case DS_TEXT: - searchForCode = 1; - case DS_DATA: - newscn = elfu_mCloneScn(ms); - if (!newscn) { - return NULL; - } + ElfuPhdr *mp; - /* Find first and last LOAD PHDRs. */ - CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { - if (mp->phdr.p_type != PT_LOAD) { - continue; - } + assert(!(w && x)); - if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) { - first = mp; - } - if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) { - /* No need to check p_memsz as segments may not overlap in memory. */ - last = mp; + /* 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; } } - if (searchForCode) { - if ((first->phdr.p_flags & PF_X) && !(first->phdr.p_flags & PF_W)) { - injAnchor = first; - } else if ((last->phdr.p_flags & PF_X) && !(last->phdr.p_flags & PF_W)) { - injAnchor = last; - } else { - injAnchor = NULL; - } - } else { - if ((first->phdr.p_flags & PF_W) && !(first->phdr.p_flags & PF_X)) { - injAnchor = first; - } else if ((last->phdr.p_flags & PF_W) && !(last->phdr.p_flags & PF_X)) { - injAnchor = last; - } else { - injAnchor = NULL; - } + 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; } + } - if (!injAnchor) { - ELFU_WARN("insertSection: Could not find injection anchor.\n" - " It has to be the first or last segment in the memory image.\n"); + /* Move our PHDRs */ + CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) { + if (mp->phdr.p_offset >= injOffset) { + mp->phdr.p_offset += size; } else { - GElf_Off injOffset; - - /* If the anchor is first or last, insert before or after */ - if (injAnchor == first) { - /* Find first section and inject before it */ - ElfuScn *firstScn = elfu_mScnFirstInSegment(me, injAnchor); - if (!firstScn) { - ELFU_WARN("insertSection: mScnFirstInSegment failed.\n"); + mp->phdr.p_vaddr -= size; + mp->phdr.p_paddr -= size; + } + } - // TODO: Error handling - } else { - injOffset = firstScn->shdr.sh_offset; + /* Move other PHDRs and sections */ + assert(size <= makeSpaceAtOffset(me, injOffset, size)); - ELFU_INFO("Inserting %s at offset 0x%jx...\n", - elfu_mScnName(mrel, ms), - injOffset); + /* Remap ourselves */ + first->phdr.p_vaddr -= size; + first->phdr.p_paddr -= size; + first->phdr.p_filesz += size; + first->phdr.p_memsz += size; - /* Make space */ - elfu_mInsertSpaceBefore(me, injOffset, ms->shdr.sh_size); + injOffset = ROUNDUP(injOffset, align); - /* Update memory offset */ - newscn->shdr.sh_addr = injAnchor->phdr.p_vaddr; + if (injPhdr) { + *injPhdr = first; + } + return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset); + } - /* Insert into chain of sections */ - elfu_mInsertScnInChainBefore(me, firstScn, newscn); - } - } else { - /* Find last section and inject after it */ - ElfuScn *lastScn = elfu_mScnLastInSegment(me, injAnchor); - if (!lastScn) { - ELFU_WARN("insertSection: mScnLastInSegment failed.\n"); + ERROR: + if (injPhdr) { + *injPhdr = NULL; + } + return 0; +} - // TODO: Error handling - } else { - injOffset = lastScn->shdr.sh_offset + SCNFILESIZE(&lastScn->shdr); - ELFU_INFO("Expanding at offset 0x%jx...\n", - injOffset); - /* Expand NOBITS sections at injection site, if any. */ - elfu_mExpandNobits(me, injOffset); - /* Recalculate injOffset in case we expanded a NOBITS section */ - lastScn = elfu_mScnLastInSegment(me, injAnchor); - injOffset = lastScn->shdr.sh_offset + SCNFILESIZE(&lastScn->shdr); +static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn) +{ + ElfuScn *newscn = NULL; + GElf_Addr injAddr; + GElf_Off injOffset; + ElfuPhdr *injPhdr; - ELFU_INFO("Inserting %s at offset 0x%jx...\n", - elfu_mScnName(mrel, ms), - injOffset); + if (oldscn->shdr.sh_flags & SHF_ALLOC) { + newscn = elfu_mCloneScn(oldscn); + if (!newscn) { + return NULL; + } - /* Make space */ - elfu_mInsertSpaceAfter(me, injOffset, ms->shdr.sh_size); - /* Update memory offset */ - newscn->shdr.sh_addr = injAnchor->phdr.p_vaddr + (injOffset - injAnchor->phdr.p_offset); + injAddr = getSpaceInPhdr(me, newscn->shdr.sh_size, + newscn->shdr.sh_addralign, + newscn->shdr.sh_flags & SHF_WRITE, + newscn->shdr.sh_flags & SHF_EXECINSTR, + &injPhdr); - /* Insert into chain of sections */ - elfu_mInsertScnInChainAfter(me, lastScn, newscn); - } - } + if (!injPhdr) { + ELFU_WARN("insertSection: Could not find a place to insert section.\n"); + goto ERROR; + } - /* Update file offset in new section BEFORE we do anything else */ - newscn->shdr.sh_offset = injOffset; + ELFU_INFO("Inserting %s at address 0x%jx...\n", + elfu_mScnName(mrel, oldscn), + injAddr); - /* Inject name */ - // TODO - newscn->shdr.sh_name = 0; + injOffset = injAddr - injPhdr->phdr.p_vaddr + injPhdr->phdr.p_offset; - // TODO: Relocate + newscn->shdr.sh_addr = injAddr; + newscn->shdr.sh_offset = injOffset; - return newscn; + if (CIRCLEQ_EMPTY(&injPhdr->childScnList) + || CIRCLEQ_LAST(&injPhdr->childScnList)->shdr.sh_offset < injOffset) { + CIRCLEQ_INSERT_TAIL(&injPhdr->childScnList, newscn, elemChildScn); + } else { + ElfuScn *ms; + CIRCLEQ_FOREACH(ms, &injPhdr->childScnList, elemChildScn) { + if (injOffset < ms->shdr.sh_offset) { + CIRCLEQ_INSERT_BEFORE(&injPhdr->childScnList, ms, newscn, elemChildScn); + break; + } } - break; + } - case DS_UNKNOWN: - ELFU_WARN("insertSection: Don't know where to insert ' %s with flags %jd (type %d).\n", - elfu_mScnName(mrel, ms), - ms->shdr.sh_flags, - ms->shdr.sh_type); - default: + /* Inject name */ + // TODO + newscn->shdr.sh_name = 0; + + // TODO: Relocate + + return newscn; + } else { ELFU_WARN("insertSection: Skipping section %s with flags %jd (type %d).\n", - elfu_mScnName(mrel, ms), - ms->shdr.sh_flags, - ms->shdr.sh_type); - return NULL; + elfu_mScnName(mrel, oldscn), + oldscn->shdr.sh_flags, + oldscn->shdr.sh_type); + goto ERROR; } + ERROR: if (newscn) { // TODO: Destroy newscn } @@ -179,66 +294,87 @@ static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *ms) } +int subScnAdd1(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2) +{ + (void)aux2; + ElfuElf *me = (ElfuElf*)aux1; -void elfu_mReladd(ElfuElf *me, ElfuElf *mrel) + ElfuScn *newscn; + + switch(ms->shdr.sh_type) { + case SHT_PROGBITS: /* 1 */ + /* Find a place where it belongs and shove it in. */ + newscn = insertSection(me, mrel, ms); + if (!newscn) { + ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n", + elfu_mScnName(mrel, ms), + ms->shdr.sh_type); + } + break; + } + + return 0; +} + + +int subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2) { - ElfuScn *ms; + (void)aux2; + ElfuElf *me = (ElfuElf*)aux1; + (void)me; + + switch(ms->shdr.sh_type) { + case SHT_NULL: /* 0 */ + case SHT_PROGBITS: /* 1 */ + break; + case SHT_SYMTAB: /* 2 */ + case SHT_DYNSYM: /* 11 */ + /* Merge with the existing table. Take care of string tables also. */ + + case SHT_STRTAB: /* 3 */ + /* May have to be merged with the existing string table for + * the symbol table. */ + + case SHT_RELA: /* 4 */ + case SHT_REL: /* 9 */ + /* Possibly append this in memory to the section model + * that it describes. */ + + case SHT_NOBITS: /* 8 */ + /* Expand this to SHT_PROGBITS, then insert as such. */ + + case SHT_HASH: /* 5 */ + case SHT_DYNAMIC: /* 6 */ + case SHT_SHLIB: /* 10 */ + case SHT_SYMTAB_SHNDX: /* 18 */ + + /* Don't care about the next ones yet. I've never seen + * them and they can be implemented when necessary. */ + case SHT_NOTE: /* 7 */ + case SHT_INIT_ARRAY: /* 14 */ + case SHT_FINI_ARRAY: /* 15 */ + case SHT_PREINIT_ARRAY: /* 16 */ + case SHT_GROUP: /* 17 */ + case SHT_NUM: /* 19 */ + default: + ELFU_WARN("mReladd: Skipping section %s (type %d).\n", + elfu_mScnName(mrel, ms), + ms->shdr.sh_type); + } + + return 0; +} + + +void elfu_mReladd(ElfuElf *me, ElfuElf *mrel) +{ assert(me); assert(mrel); - /* For each section in object file, guess how to insert it */ - CIRCLEQ_FOREACH(ms, &mrel->scnList, elem) { - ElfuScn *newscn; - - switch(ms->shdr.sh_type) { - case SHT_NULL: /* 0 */ - continue; - - case SHT_PROGBITS: /* 1 */ - /* Find a place where it belongs and shove it in. */ - newscn = insertSection(me, mrel, ms); - if (!newscn) { - ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n", - elfu_mScnName(mrel, ms), - ms->shdr.sh_type); - } - break; - - case SHT_SYMTAB: /* 2 */ - case SHT_DYNSYM: /* 11 */ - /* Merge with the existing table. Take care of string tables also. */ - - case SHT_STRTAB: /* 3 */ - /* May have to be merged with the existing string table for - * the symbol table. */ - - case SHT_RELA: /* 4 */ - case SHT_REL: /* 9 */ - /* Possibly append this in memory to the section model - * that it describes. */ - - case SHT_NOBITS: /* 8 */ - /* Expand this to SHT_PROGBITS, then insert as such. */ - - case SHT_HASH: /* 5 */ - case SHT_DYNAMIC: /* 6 */ - case SHT_SHLIB: /* 10 */ - case SHT_SYMTAB_SHNDX: /* 18 */ - - /* Don't care about the next ones yet. I've never seen - * them and they can be implemented when necessary. */ - case SHT_NOTE: /* 7 */ - case SHT_INIT_ARRAY: /* 14 */ - case SHT_FINI_ARRAY: /* 15 */ - case SHT_PREINIT_ARRAY: /* 16 */ - case SHT_GROUP: /* 17 */ - case SHT_NUM: /* 19 */ - default: - ELFU_WARN("mReladd: Skipping section %s (type %d).\n", - elfu_mScnName(mrel, ms), - ms->shdr.sh_type); - } - } + elfu_mScnForall(mrel, subScnAdd1, me, NULL); + + /* Do relocations and other stuff */ + elfu_mScnForall(mrel, subScnAdd2, me, NULL); } diff --git a/src/model/section-by-type.c b/src/model/section-by-type.c deleted file mode 100644 index e36865a..0000000 --- a/src/model/section-by-type.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include - - -/* - * Returns the first section with the given type. - */ -ElfuScn* elfu_mScnByType(ElfuElf *me, Elf32_Word type) -{ - ElfuScn *ms; - - assert(me); - - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - if (ms->shdr.sh_type == type) { - return ms; - } - } - - return NULL; -} diff --git a/src/model/section-in-segment.c b/src/model/section-in-segment.c deleted file mode 100644 index 0e513e2..0000000 --- a/src/model/section-in-segment.c +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include - - -/* - * Returns the first section beginning in the segment. - * - * Since sections have to be in the file in mapping order to load them - * as a continuous segment, we only have to search by start offset. - */ -ElfuScn* elfu_mScnFirstInSegment(ElfuElf *me, ElfuPhdr *mp) -{ - ElfuScn *ms; - - assert(me); - assert(mp); - - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - if ((ms->shdr.sh_offset >= mp->phdr.p_offset) - && (ms->shdr.sh_offset < mp->phdr.p_offset + mp->phdr.p_filesz)) { - return ms; - } - } - - return NULL; -} - - - -/* - * Returns the last section ending in the segment. - * - * Since sections have to be in the file in mapping order to load them - * as a continuous segment, we only have to search by end offset. - */ -ElfuScn* elfu_mScnLastInSegment(ElfuElf *me, ElfuPhdr *mp) -{ - ElfuScn *last = NULL; - ElfuScn *ms; - - assert(me); - assert(mp); - - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - /* Get section size on disk - for NOBITS sections that is 0 bytes. */ - size_t size = SCNFILESIZE(&ms->shdr); - - if (((ms->shdr.sh_offset + size >= mp->phdr.p_offset) - && (ms->shdr.sh_offset + size <= mp->phdr.p_offset + mp->phdr.p_filesz))) { - last = ms; - } - } - - return last; -} diff --git a/src/model/section-name.c b/src/model/section-name.c deleted file mode 100644 index 68d9986..0000000 --- a/src/model/section-name.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include -#include -#include -#include - - - -char* elfu_mScnName(ElfuElf *me, ElfuScn *ms) -{ - assert(me); - assert(ms); - - if (!me->shstrtab) { - return NULL; - } - - if (!me->shstrtab->data.d_buf) { - return NULL; - } - - return &((char*)me->shstrtab->data.d_buf)[ms->shdr.sh_name]; -} diff --git a/src/model/section.c b/src/model/section.c new file mode 100644 index 0000000..d7ddc19 --- /dev/null +++ b/src/model/section.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + + +/* Meta-functions */ + +int elfu_mScnForall(ElfuElf *me, SectionHandlerFunc f, void *aux1, void *aux2) +{ + ElfuPhdr *mp; + ElfuScn *ms; + + // TODO: Sort PHDRs by offset before interating + + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + + CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) { + int rv = f(me, ms, aux1, aux2); + + if (rv) { + return rv; + } + } + } + + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + int rv = f(me, ms, aux1, aux2); + + if (rv) { + return rv; + } + } + + return 0; +} + + + + +/* Counting */ + +static int subCounter(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + size_t *i = (size_t*)aux1; + ElfuScn *otherScn = (ElfuScn*)aux2; + + if (ms == otherScn) { + return 1; + } + + *i += 1; + + return 0; +} + + +size_t elfu_mScnCount(ElfuElf *me) +{ + /* NULL section *is not* counted */ + size_t i = 0; + + assert(me); + + elfu_mScnForall(me, subCounter, &i, NULL); + + return i; +} + + +/* Returns the section index equivalent to the model flattened to ELF */ +size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms) +{ + /* NULL section *is* counted */ + size_t i = 1; + + assert(me); + assert(ms); + + elfu_mScnForall(me, subCounter, &i, ms); + + /* If this assertion is broken then ms is not a section in me. */ + assert(i <= elfu_mScnCount(me)); + return i; +} + + + + +/* Convenience */ + +char* elfu_mScnName(ElfuElf *me, ElfuScn *ms) +{ + assert(me); + assert(ms); + + if (!me->shstrtab) { + return NULL; + } + + if (!me->shstrtab->data.d_buf) { + return NULL; + } + + return &((char*)me->shstrtab->data.d_buf)[ms->shdr.sh_name]; +} + + +static int subScnsToArray(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + ElfuScn **arr = (ElfuScn**)aux1; + size_t *i = (size_t*)aux2; + + arr[(*i)] = ms; + *i += 1; + + return 0; +} + +static int cmpScnOffs(const void *ms1, const void *ms2) +{ + assert(ms1); + assert(ms2); + + ElfuScn *s1 = *(ElfuScn**)ms1; + ElfuScn *s2 = *(ElfuScn**)ms2; + + assert(s1); + assert(s2); + + + if (s1->shdr.sh_offset < s2->shdr.sh_offset) { + return -1; + } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) { + return 0; + } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ { + return 1; + } +} + +ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count) +{ + assert(me); + + size_t numSecs; + ElfuScn **sortedSecs; + size_t i; + + /* Sort sections by offset in file */ + numSecs = elfu_mScnCount(me); + sortedSecs = malloc(numSecs * sizeof(*sortedSecs)); + if (!sortedSecs) { + ELFU_WARN("elfu_mScnSortedByOffset: Failed to allocate memory.\n"); + return NULL; + } + + i = 0; + elfu_mScnForall(me, subScnsToArray, sortedSecs, &i); + assert(i == numSecs); + + qsort(sortedSecs, numSecs, sizeof(*sortedSecs), cmpScnOffs); + + *count = numSecs; + + return sortedSecs; +} diff --git a/src/model/toFile.c b/src/model/toFile.c index 729a60e..9410f13 100644 --- a/src/model/toFile.c +++ b/src/model/toFile.c @@ -33,18 +33,27 @@ static void modelToPhdrs(ElfuElf *me, Elf *e) -static void modelToSection(ElfuScn *ms, Elf *e) +static int modelToSection(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) { + (void) me; + (void) aux2; + Elf *e = (Elf*)aux1; Elf_Scn *scnOut; scnOut = elf_newscn(e); if (!scnOut) { ELFU_WARNELF("elf_newscn"); - return; + return 1; } /* SHDR */ + if (ms->linkptr) { + ms->shdr.sh_link = elfu_mScnIndex(me, ms->linkptr); + } + if (ms->infoptr) { + ms->shdr.sh_info = elfu_mScnIndex(me, ms->infoptr); + } if (!gelf_update_shdr(scnOut, &ms->shdr)) { ELFU_WARNELF("gelf_update_shdr"); } @@ -64,6 +73,8 @@ static void modelToSection(ElfuScn *ms, Elf *e) dataOut->d_size = ms->data.d_size; dataOut->d_version = ms->data.d_version; } + + return 0; } @@ -72,10 +83,11 @@ static void modelToSection(ElfuScn *ms, Elf *e) void elfu_mToElf(ElfuElf *me, Elf *e) { - ElfuScn *ms; - /* We control the ELF file's layout now. */ - /* tired's libelf also offers ELF_F_LAYOUT_OVERLAP for overlapping sections. */ + /* tired's libelf also offers ELF_F_LAYOUT_OVERLAP for overlapping sections, + * but we don't want that since we filtered it out in the reading stage + * already. It would be too mind-blowing to handle the dependencies between + * the PHDRs and sections then... */ elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT); @@ -84,15 +96,17 @@ void elfu_mToElf(ElfuElf *me, Elf *e) ELFU_WARNELF("gelf_newehdr"); } + if (me->shstrtab) { + me->ehdr.e_shstrndx = elfu_mScnIndex(me, me->shstrtab); + } + if (!gelf_update_ehdr(e, &me->ehdr)) { ELFU_WARNELF("gelf_update_ehdr"); } /* Sections */ - CIRCLEQ_FOREACH(ms, &me->scnList, elem) { - modelToSection(ms, e); - } + elfu_mScnForall(me, modelToSection, e, NULL); /* PHDRs */ diff --git a/src/options.c b/src/options.c index 198f01b..b5018d3 100644 --- a/src/options.c +++ b/src/options.c @@ -19,16 +19,16 @@ static void printUsage(char *progname) // " --print-segments Print program headers\n" // " --print-sections Print sections\n" // "\n" - "Space insertion:\n" - " off: File offset, not within any structure (headers or sections).\n" - " sz: A multiple of the maximum alignment of all PHDRs.\n" - "\n" - " --expand-nobits off Expand virtual areas (NOBITS sections and similar PHDRs).\n" - " --insert-before off,sz Insert spacing at given offset,\n" - " mapping everything before it to lower mem addresses.\n" - " --insert-after off,sz Insert spacing at given offset,\n" - " mapping everything after it to higher mem addresses.\n" - "\n" +// "Space insertion:\n" +// " off: File offset, not within any structure (headers or sections).\n" +// " sz: A multiple of the maximum alignment of all PHDRs.\n" +// "\n" +// " --expand-nobits off Expand virtual areas (NOBITS sections and similar PHDRs).\n" +// " --insert-before off,sz Insert spacing at given offset,\n" +// " mapping everything before it to lower mem addresses.\n" +// " --insert-after off,sz Insert spacing at given offset,\n" +// " mapping everything after it to higher mem addresses.\n" +// "\n" "High-level insertion:\n" " --reladd obj.o Automatically insert object file contents\n" "\n"); -- 2.30.2