diff options
-rw-r--r-- | include/libelfu/generic.h | 4 | ||||
-rw-r--r-- | include/libelfu/libelfu.h | 1 | ||||
-rw-r--r-- | include/libelfu/modelops.h | 26 | ||||
-rw-r--r-- | include/libelfu/modeltypes.h | 46 | ||||
-rw-r--r-- | include/libelfu/types.h | 41 | ||||
-rw-r--r-- | src/main.c | 14 | ||||
-rw-r--r-- | src/model/check.c | 63 | ||||
-rw-r--r-- | src/model/clone.c | 7 | ||||
-rw-r--r-- | src/model/count.c | 55 | ||||
-rw-r--r-- | src/model/dump.c | 20 | ||||
-rw-r--r-- | src/model/expandNobits.c | 116 | ||||
-rw-r--r-- | src/model/fromFile.c | 9 | ||||
-rw-r--r-- | src/model/insert.c | 289 | ||||
-rw-r--r-- | src/model/phdr.c | 17 | ||||
-rw-r--r-- | src/model/reladd.c | 494 | ||||
-rw-r--r-- | src/model/section-by-type.c | 22 | ||||
-rw-r--r-- | src/model/section-in-segment.c | 56 | ||||
-rw-r--r-- | src/model/section-name.c | 23 | ||||
-rw-r--r-- | src/model/section.c | 170 | ||||
-rw-r--r-- | src/model/toFile.c | 30 | ||||
-rw-r--r-- | src/options.c | 20 |
21 files changed, 616 insertions, 907 deletions
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 <libelf/gelf.h> + +#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 <libelfu/generic.h> #include <libelfu/elfops.h> -#include <libelfu/modeltypes.h> #include <libelfu/modelops.h> #include <libelfu/debug.h> 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 <elf.h> #include <libelf/gelf.h> -#include <libelfu/modeltypes.h> +#include <libelfu/types.h> -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 <sys/queue.h> - -#include <elf.h> -#include <libelf/gelf.h> - - -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 <sys/queue.h> + +#include <elf.h> +#include <libelf/gelf.h> + + +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 @@ -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 <libelfu/libelfu.h> - -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 <assert.h> -#include <sys/types.h> -#include <libelfu/libelfu.h> - - -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 <assert.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <libelf/gelf.h> -#include <libelfu/libelfu.h> - - -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 <assert.h> -#include <sys/types.h> -#include <libelf/gelf.h> -#include <libelfu/libelfu.h> - - -// 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 <assert.h> +#include <libelfu/libelfu.h> + + +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 <assert.h> #include <stdlib.h> +#include <string.h> #include <sys/types.h> #include <libelf/gelf.h> #include <libelfu/libelfu.h> -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 <assert.h> -#include <libelf/gelf.h> -#include <libelfu/libelfu.h> - - -/* - * 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 <assert.h> -#include <libelf/gelf.h> -#include <libelfu/libelfu.h> - - -/* - * 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 <assert.h> -#include <stdlib.h> -#include <libelf/libelf.h> -#include <libelf/gelf.h> -#include <libelfu/libelfu.h> - - - -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 <assert.h> +#include <stdlib.h> +#include <libelf/libelf.h> +#include <libelf/gelf.h> +#include <libelfu/libelfu.h> + + +/* 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"); |