summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornorly <ny-git@enpas.org>2013-03-21 18:23:57 +0000
committernorly <ny-git@enpas.org>2013-03-21 18:23:57 +0000
commitb05f85ea8599327918640f5ee2bb3f422559b357 (patch)
tree604e83d87e47a4928b5a5609ea8a6842a882d536
parente2b6e201992b9e4d458dd469d286db3dca46e75f (diff)
NOBITS expansion, for .bss etc
GNU binutils' readelf gets confused with symbol versions. More analysis needed on that.
-rw-r--r--include/libelfu/modelops.h2
-rw-r--r--include/options.h1
-rw-r--r--src/main.c4
-rw-r--r--src/model/expandNobits.c116
-rw-r--r--src/model/insert.c4
-rw-r--r--src/options.c8
6 files changed, 135 insertions, 0 deletions
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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <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.
+ */
+ 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;