From: norly Date: Thu, 21 Mar 2013 18:23:57 +0000 (+0000) Subject: NOBITS expansion, for .bss etc X-Git-Url: https://git.enpas.org/?p=centaur.git;a=commitdiff_plain;h=b05f85ea8599327918640f5ee2bb3f422559b357 NOBITS expansion, for .bss etc GNU binutils' readelf gets confused with symbol versions. More analysis needed on that. --- diff --git a/include/libelfu/modelops.h b/include/libelfu/modelops.h index d256b07..305a3e3 100644 --- a/include/libelfu/modelops.h +++ b/include/libelfu/modelops.h @@ -18,6 +18,8 @@ ElfuElf* elfu_mFromElf(Elf *e); void elfu_mToElf(ElfuElf *me, Elf *e); + void elfu_mExpandNobits(ElfuElf *me, GElf_Off off); + GElf_Xword elfu_mInsertBefore(ElfuElf *me, GElf_Off off, GElf_Xword size); GElf_Xword elfu_mInsertAfter(ElfuElf *me, GElf_Off off, GElf_Xword size); diff --git a/include/options.h b/include/options.h index f92298f..e634e0b 100644 --- a/include/options.h +++ b/include/options.h @@ -12,6 +12,7 @@ typedef struct { unsigned insertBeforeSz; unsigned insertAfterOffs; unsigned insertAfterSz; + unsigned expandNobitsOffs; } CLIOpts; diff --git a/src/main.c b/src/main.c index 1e06d29..34e7570 100644 --- a/src/main.c +++ b/src/main.c @@ -70,6 +70,10 @@ int main(int argc, char **argv) elfu_mCheck(me); printf("Input model checked.\n"); + if (opts.expandNobitsOffs) { + elfu_mExpandNobits(me, opts.expandNobitsOffs); + } + if (opts.insertBeforeSz) { elfu_mInsertBefore(me, opts.insertBeforeOffs, opts.insertBeforeSz); } diff --git a/src/model/expandNobits.c b/src/model/expandNobits.c new file mode 100644 index 0000000..7c14668 --- /dev/null +++ b/src/model/expandNobits.c @@ -0,0 +1,116 @@ +#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. + */ + fprintf(stderr, "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) { + fprintf(stderr, "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/insert.c b/src/model/insert.c index d4e19d3..edfe153 100644 --- a/src/model/insert.c +++ b/src/model/insert.c @@ -121,6 +121,10 @@ GElf_Xword elfu_mInsertBefore(ElfuElf *me, GElf_Off off, GElf_Xword size) * * 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_mInsertAfter(ElfuElf *me, GElf_Off off, GElf_Xword size) { diff --git a/src/options.c b/src/options.c index c728d40..1a191a5 100644 --- a/src/options.c +++ b/src/options.c @@ -23,6 +23,7 @@ static void printUsage(char *progname) " 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" @@ -47,6 +48,7 @@ void parseOptions(CLIOpts *opts, int argc, char **argv) {"print-sections", 0, 0, 10003}, {"insert-before", 1, 0, 10004}, {"insert-after", 1, 0, 10005}, + {"expand-nobits", 1, 0, 10006}, {NULL, 0, NULL, 0} }; @@ -88,6 +90,12 @@ void parseOptions(CLIOpts *opts, int argc, char **argv) goto USAGE; } break; + case 10006: + opts->expandNobitsOffs = strtoul(optarg, &endptr, 0); + if (endptr[0] != '\0' || opts->expandNobitsOffs < 1) { + goto USAGE; + } + break; case '?': default: goto USAGE;