NOBITS expansion, for .bss etc
authornorly <ny-git@enpas.org>
Thu, 21 Mar 2013 18:23:57 +0000 (18:23 +0000)
committernorly <ny-git@enpas.org>
Thu, 21 Mar 2013 18:23:57 +0000 (18:23 +0000)
GNU binutils' readelf gets confused with symbol versions. More analysis
needed on that.

include/libelfu/modelops.h
include/options.h
src/main.c
src/model/expandNobits.c [new file with mode: 0644]
src/model/insert.c
src/options.c

index d256b07c8c387ed7ece085e44f41d848dd94ebb7..305a3e32191777c030172a8356836bf40fcfeeaf 100644 (file)
@@ -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);
 
index f92298f788c1937a29ac11dea0d9e382c239aee4..e634e0ba3bbc8b39a59a9d7a85fe149666017711 100644 (file)
@@ -12,6 +12,7 @@ typedef struct {
   unsigned insertBeforeSz;
   unsigned insertAfterOffs;
   unsigned insertAfterSz;
+  unsigned expandNobitsOffs;
 } CLIOpts;
 
 
index 1e06d2998bca0642143bbb2deee1a51aaab14ed5..34e757010e7d26c43fba6aac2c8076374805d073 100644 (file)
@@ -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 (file)
index 0000000..7c14668
--- /dev/null
@@ -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;
+      }
+    }
+  }
+}
index d4e19d3a0dd5d0629afe599ea076eea4c31e859b..edfe153ecd8bd71316bf4834d2772c3b1bd59f5c 100644 (file)
@@ -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)
 {
index c728d40ac2cf12ee6807cdd5a43dcdd835320d60..1a191a513e5c4cd2aec3560fd11a21e5e8e8591d 100644 (file)
@@ -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;