Object file injection, first part
authornorly <ny-git@enpas.org>
Fri, 24 May 2013 06:23:41 +0000 (07:23 +0100)
committernorly <ny-git@enpas.org>
Fri, 24 May 2013 06:23:41 +0000 (07:23 +0100)
include/libelfu/modelops.h
include/options.h
src/main.c
src/model/clone.c [new file with mode: 0644]
src/model/count.c
src/model/insert.c
src/model/reladd.c [new file with mode: 0644]
src/model/section-by-type.c [new file with mode: 0644]
src/model/section-in-segment.c [new file with mode: 0644]
src/options.c

index 3ce9a9059c35923bb5b7902e37ddc80af32ad21f..1cfe0b676c89142f57d9c230bf85bcc50b13064f 100644 (file)
@@ -7,20 +7,35 @@
 #include <libelfu/modeltypes.h>
 
 
+size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms);
 size_t elfu_mCountScns(ElfuElf *me);
 size_t elfu_mCountPhdrs(ElfuElf *me);
 
 char* elfu_mScnName(ElfuElf *me, ElfuScn *ms);
 
+ElfuScn* elfu_mScnFirstInSegment(ElfuElf *me, ElfuPhdr *mp);
+ElfuScn* elfu_mScnLastInSegment(ElfuElf *me, ElfuPhdr *mp);
+
+ElfuScn* elfu_mScnByType(ElfuElf *me, Elf32_Word type);
+
 int elfu_mCheck(ElfuElf *me);
 
+ElfuScn* elfu_mCloneScn(ElfuScn *ms);
+
+
 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);
+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
index e634e0ba3bbc8b39a59a9d7a85fe149666017711..2210c854786535524cc2a6ae2738aa25ea723cf5 100644 (file)
@@ -13,6 +13,7 @@ typedef struct {
   unsigned insertAfterOffs;
   unsigned insertAfterSz;
   unsigned expandNobitsOffs;
+  char *fnReladd;
 } CLIOpts;
 
 
index a3bf2711adb08132b912ffa72a7525778ad31dce..2c6268c6cd9e5a56ae0f1aba511fa60a59f9e219 100644 (file)
@@ -76,11 +76,33 @@ int main(int argc, char **argv)
     }
 
     if (opts.insertBeforeSz) {
-      elfu_mInsertBefore(me, opts.insertBeforeOffs, opts.insertBeforeSz);
+      elfu_mInsertSpaceBefore(me, opts.insertBeforeOffs, opts.insertBeforeSz);
     }
 
     if (opts.insertAfterSz) {
-      elfu_mInsertAfter(me, opts.insertAfterOffs, opts.insertAfterSz);
+      elfu_mInsertSpaceAfter(me, opts.insertAfterOffs, opts.insertAfterSz);
+    }
+
+    if (opts.fnReladd) {
+      ELFHandles hRel = { 0 };
+      ElfuElf *mrel = NULL;
+
+      openElf(&hRel, opts.fnReladd, ELF_C_READ);
+      if (!hRel.e) {
+        printf("--reladd: Failed to open file for --reladd, skipping operation.\n");
+      } else {
+        mrel = elfu_mFromElf(hRel.e);
+        closeElf(&hRel);
+        if (!me) {
+          printf("--reladd: Failed to load model for --reladd, skipping operation.\n");
+        } else {
+          printf("--reladd: Model successfully loaded.\n");
+          elfu_mCheck(mrel);
+          printf("--reladd: Input model checked.\n");
+          elfu_mReladd(me, mrel);
+        }
+      }
+
     }
 
     elfu_mCheck(me);
diff --git a/src/model/clone.c b/src/model/clone.c
new file mode 100644 (file)
index 0000000..3dbbc54
--- /dev/null
@@ -0,0 +1,35 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelfu/libelfu.h>
+
+ElfuScn* elfu_mCloneScn(ElfuScn *ms)
+{
+  ElfuScn *newscn;
+
+  assert(ms);
+
+  newscn = malloc(sizeof(ElfuScn));
+  if (!newscn) {
+    ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new ElfuScn.\n");
+    return NULL;
+  }
+
+
+
+  newscn->shdr = ms->shdr;
+  newscn->data = ms->data;
+  if (ms->data.d_buf) {
+    void *newbuf = malloc(ms->data.d_size);
+    if (!newbuf) {
+      ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new data buffer.\n");
+      free(newscn);
+      return NULL;
+    }
+
+    memcpy(newbuf, ms->data.d_buf, ms->data.d_size);
+    newscn->data.d_buf = newbuf;
+  }
+
+  return newscn;
+}
index b7c80130d63778186bfdbde824d13f3afeea0d3a..671767e04b1b0fa01967a09bfaec1865a8d806a5 100644 (file)
@@ -3,6 +3,26 @@
 #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)
index 492f06a8adf66b06cdfd2969243d6337a8550ebc..312be26c79393d2723e15a115feb37071d7206c7 100644 (file)
@@ -20,7 +20,7 @@
  * 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_mInsertBefore(ElfuElf *me, GElf_Off off, GElf_Xword size)
+GElf_Xword elfu_mInsertSpaceBefore(ElfuElf *me, GElf_Off off, GElf_Xword size)
 {
   ElfuScn *ms;
   ElfuPhdr *mp;
@@ -126,7 +126,7 @@ GElf_Xword elfu_mInsertBefore(ElfuElf *me, GElf_Off off, GElf_Xword size)
  *          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)
+GElf_Xword elfu_mInsertSpaceAfter(ElfuElf *me, GElf_Off off, GElf_Xword size)
 {
   ElfuScn *ms;
   ElfuPhdr *mp;
@@ -211,3 +211,74 @@ GElf_Xword elfu_mInsertAfter(ElfuElf *me, GElf_Off off, GElf_Xword 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/reladd.c b/src/model/reladd.c
new file mode 100644 (file)
index 0000000..782d245
--- /dev/null
@@ -0,0 +1,233 @@
+#include <assert.h>
+#include <stdlib.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)
+{
+  if (!(ms->shdr.sh_flags & SHF_ALLOC)) {
+    return DS_UNKNOWN;
+  }
+
+  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;
+  }
+
+  return DS_UNKNOWN;
+}
+
+
+
+static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *ms)
+{
+  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;
+      }
+
+      /* Find first and last LOAD PHDRs. */
+      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) {
+          /* No need to check p_memsz as segments may not overlap in memory. */
+          last = mp;
+        }
+      }
+
+      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;
+        }
+      }
+
+      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");
+      } 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");
+
+            // TODO: Error handling
+          } else {
+            injOffset = firstScn->shdr.sh_offset;
+
+            /* Make space */
+            elfu_mInsertSpaceBefore(me, injOffset, ms->shdr.sh_size);
+
+            /* Update memory offset */
+            newscn->shdr.sh_addr = injAnchor->phdr.p_vaddr;
+
+            /* 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");
+
+            // TODO: Error handling
+          } else {
+            injOffset = lastScn->shdr.sh_offset + elfu_gScnSizeFile(&lastScn->shdr);
+
+            /* 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 + elfu_gScnSizeFile(&lastScn->shdr);
+
+            /* 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);
+
+            /* Insert into chain of sections */
+            elfu_mInsertScnInChainAfter(me, lastScn, newscn);
+          }
+        }
+
+        /* Update file offset in new section BEFORE we do anything else */
+        newscn->shdr.sh_offset = injOffset;
+
+        /* Inject name */
+        // TODO
+        newscn->shdr.sh_name = 0;
+
+        // TODO: Relocate
+
+        return newscn;
+      }
+      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:
+      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;
+  }
+
+  if (newscn) {
+    // TODO: Destroy newscn
+  }
+  return NULL;
+}
+
+
+
+void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
+{
+  ElfuScn *ms;
+
+  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);
+    }
+  }
+}
diff --git a/src/model/section-by-type.c b/src/model/section-by-type.c
new file mode 100644 (file)
index 0000000..e36865a
--- /dev/null
@@ -0,0 +1,22 @@
+#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
new file mode 100644 (file)
index 0000000..ad7f037
--- /dev/null
@@ -0,0 +1,56 @@
+#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 = elfu_gScnSizeFile(&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;
+}
index 1a191a513e5c4cd2aec3560fd11a21e5e8e8591d..928d080d60703aeb4ce9461010d5b1976dc0dde3 100644 (file)
@@ -28,6 +28,9 @@ static void printUsage(char *progname)
           "                                 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");
 }
 
@@ -49,6 +52,7 @@ void parseOptions(CLIOpts *opts, int argc, char **argv)
     {"insert-before", 1, 0, 10004},
     {"insert-after", 1, 0, 10005},
     {"expand-nobits", 1, 0, 10006},
+    {"reladd", 1, 0, 10007},
     {NULL, 0, NULL, 0}
   };
 
@@ -96,6 +100,9 @@ void parseOptions(CLIOpts *opts, int argc, char **argv)
           goto USAGE;
         }
         break;
+      case 10007:
+        opts->fnReladd = optarg;
+        break;
       case '?':
       default:
         goto USAGE;