Redesign data structures, make basic reladd work. newparser
authornorly <ny-git@enpas.org>
Thu, 30 May 2013 03:01:51 +0000 (04:01 +0100)
committernorly <ny-git@enpas.org>
Thu, 30 May 2013 03:36:39 +0000 (04:36 +0100)
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.

21 files changed:
include/libelfu/generic.h
include/libelfu/libelfu.h
include/libelfu/modelops.h
include/libelfu/modeltypes.h [deleted file]
include/libelfu/types.h
src/main.c
src/model/check.c
src/model/clone.c
src/model/count.c [deleted file]
src/model/dump.c
src/model/expandNobits.c [deleted file]
src/model/fromFile.c
src/model/insert.c [deleted file]
src/model/phdr.c [new file with mode: 0644]
src/model/reladd.c
src/model/section-by-type.c [deleted file]
src/model/section-in-segment.c [deleted file]
src/model/section-name.c [deleted file]
src/model/section.c [new file with mode: 0644]
src/model/toFile.c
src/options.c

index 6a066348c637fe8586ad9e05ea9ba9e4f4fc8d7d..c8a9c79df9d9bf2ae80ded9428772c61750599a9 100644 (file)
@@ -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) \
index cc55282f906fe8b6a721655ae532f63cb28edcad..328a75d912af4d3d36791acec7ac382457b089ff 100644 (file)
@@ -6,7 +6,6 @@
 
 #include <libelfu/generic.h>
 #include <libelfu/elfops.h>
-#include <libelfu/modeltypes.h>
 #include <libelfu/modelops.h>
 
 #include <libelfu/debug.h>
index 31a4ed9ac942d89c9db19784ccbf4dd2e8eac1a2..9495aca5ef3eb88c25dedcaf370576d38a91e9e0 100644 (file)
@@ -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 (file)
index bea9322..0000000
+++ /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
index a59efca7e78ace74d7dfb139442f1d6604a262e6..a06d4706d0036c638807d6fe52eebbe3e3958521 100644 (file)
@@ -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
index 0eb6e00b3aee7468f12f851a9b0597a4ce4e6ffc..a179fcd82a0097e57a0e960d31390d917757025f 100644 (file)
@@ -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) {
index 6dc06948f0143143625a7e0c30c4b3b0d28e87bd..5234befa1c9fa0d9a22e9c1d6cc5aa430409d9c3 100644 (file)
@@ -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;
index 3dbbc542e94c869203bfff4d3d9f88280a39a37c..644647cc7aa6bfecd67b2ed46ee64f6f5580f795 100644 (file)
@@ -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 (file)
index 671767e..0000000
+++ /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;
-}
index 67a498a184e7a2e531cf6b6624e64b21b9f24168..5f0642a8fc53717a98161a12f0286d809973011a 100644 (file)
@@ -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 (file)
index d12990e..0000000
+++ /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;
-      }
-    }
-  }
-}
index cb0935270d355a26f01f369ad348fa2d4ce14c99..ee0536de26a630c876d3ec65f86464c12549ef7c 100644 (file)
@@ -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 (file)
index b18aa8a..0000000
+++ /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 (file)
index 0000000..26a26e3
--- /dev/null
@@ -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;
+}
index 549afcfe46c42013851016f7f46468cffda2dfc4..bc13b97b35e355a9a77ac726d75ef784434fdb0a 100644 (file)
 #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 (file)
index e36865a..0000000
+++ /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 (file)
index 0e513e2..0000000
+++ /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 (file)
index 68d9986..0000000
+++ /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 (file)
index 0000000..d7ddc19
--- /dev/null
@@ -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;
+}
index 729a60e9a5313a6f5ddf1e9233751b4e01a724e0..9410f134c117ad1fa6b472c7564e93860848b2b4 100644 (file)
@@ -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 */
index 198f01baa5bd6cbfd8037179d4f21caa1345b38b..b5018d3dec2a9259d9722ae69bedb2046ddbcf1b 100644 (file)
@@ -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");