Pull out existing re-layouting code
authornorly <ny-git@enpas.org>
Fri, 31 May 2013 00:43:22 +0000 (01:43 +0100)
committernorly <ny-git@enpas.org>
Fri, 31 May 2013 00:43:22 +0000 (01:43 +0100)
include/libelfu/modelops.h
src/model/layout.c [new file with mode: 0644]
src/model/phdr.c
src/model/reladd.c

index 9495aca5ef3eb88c25dedcaf370576d38a91e9e0..8374b02b26377f13d6f442d6758869350b7a9a33 100644 (file)
@@ -8,6 +8,7 @@
 
 
 size_t elfu_mPhdrCount(ElfuElf *me);
+void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp);
 
 
 typedef int (SectionHandlerFunc)(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2);
@@ -18,6 +19,11 @@ char* elfu_mScnName(ElfuElf *me, ElfuScn *ms);
 ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count);
 
 
+GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
+                                     GElf_Word align, int w, int x,
+                                     ElfuPhdr **injPhdr);
+
+
 int elfu_mCheck(ElfuElf *me);
 
 ElfuScn* elfu_mCloneScn(ElfuScn *ms);
diff --git a/src/model/layout.c b/src/model/layout.c
new file mode 100644 (file)
index 0000000..2febed1
--- /dev/null
@@ -0,0 +1,217 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelfu/libelfu.h>
+
+
+
+static GElf_Word shiftStuffAtAfterOffset(ElfuElf *me,
+                                         GElf_Off offset,
+                                         GElf_Word size)
+{
+  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;
+      }
+    }
+  }
+
+  CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
+    if (ms->shdr.sh_offset >= offset) {
+      if (ms->shdr.sh_addralign > align) {
+        align = ms->shdr.sh_addralign;
+      }
+    }
+  }
+
+  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) {
+      mp->phdr.p_offset += size;
+
+      elfu_mPhdrUpdateChildOffsets(mp);
+    }
+  }
+
+  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;
+}
+
+
+
+
+
+/* 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. */
+GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
+                                     GElf_Word align, int w, int x,
+                                     ElfuPhdr **injPhdr)
+{
+  ElfuPhdr *first = NULL;
+  ElfuPhdr *last = NULL;
+  ElfuPhdr *mp;
+
+  assert(!(w && x));
+
+  /* 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;
+        }
+      }
+
+      injSpace += shiftStuffAtAfterOffset(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 += shiftStuffAtAfterOffset(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;
+      }
+    }
+
+    /* Move our PHDRs */
+    CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) {
+      if (mp->phdr.p_offset >= injOffset) {
+        mp->phdr.p_offset += size;
+      } else {
+        mp->phdr.p_vaddr -= size;
+        mp->phdr.p_paddr -= size;
+      }
+    }
+
+    /* Move other PHDRs and sections */
+    assert(size <= shiftStuffAtAfterOffset(me, injOffset, size));
+
+    /* Remap ourselves */
+    first->phdr.p_vaddr -= size;
+    first->phdr.p_paddr -= size;
+    first->phdr.p_filesz += size;
+    first->phdr.p_memsz += size;
+
+    injOffset = ROUNDUP(injOffset, align);
+
+    if (injPhdr) {
+      *injPhdr = first;
+    }
+    return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset);
+  }
+
+  ERROR:
+  if (injPhdr) {
+    *injPhdr = NULL;
+  }
+  return 0;
+}
index 26a26e301a968b4481190af63e00a15dd0f9a769..d26eb772a3c5f1c10fe037f46ce160d2faaa29fe 100644 (file)
@@ -15,3 +15,22 @@ size_t elfu_mPhdrCount(ElfuElf *me)
 
   return i;
 }
+
+
+
+void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp)
+{
+  ElfuScn *ms;
+  ElfuPhdr *mpc;
+
+  assert(mp);
+  assert(mp->phdr.p_type == PT_LOAD);
+
+  CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) {
+    mpc->phdr.p_offset = mp->phdr.p_offset + (mpc->phdr.p_vaddr - mp->phdr.p_vaddr);
+  }
+
+  CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
+    ms->shdr.sh_offset = mp->phdr.p_offset + (ms->shdr.sh_addr - mp->phdr.p_vaddr);
+  }
+}
index bc13b97b35e355a9a77ac726d75ef784434fdb0a..ffd02f48e03cddd01d66dca90d68f0e08bcbdbb2 100644 (file)
@@ -6,224 +6,6 @@
 #include <libelfu/libelfu.h>
 
 
-
-static GElf_Word makeSpaceAtOffset(ElfuElf *me, GElf_Off offset, GElf_Word size)
-{
-  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;
-      }
-    }
-  }
-
-  CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
-    if (ms->shdr.sh_offset >= offset) {
-      if (ms->shdr.sh_addralign > align) {
-        align = ms->shdr.sh_addralign;
-      }
-    }
-  }
-
-  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;
-}
-
-
-
-/* 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 *first = NULL;
-  ElfuPhdr *last = NULL;
-  ElfuPhdr *mp;
-
-  assert(!(w && x));
-
-  /* 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;
-        }
-      }
-
-      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;
-      }
-    }
-
-    /* Move our PHDRs */
-    CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) {
-      if (mp->phdr.p_offset >= injOffset) {
-        mp->phdr.p_offset += size;
-      } else {
-        mp->phdr.p_vaddr -= size;
-        mp->phdr.p_paddr -= size;
-      }
-    }
-
-    /* Move other PHDRs and sections */
-    assert(size <= makeSpaceAtOffset(me, injOffset, size));
-
-    /* Remap ourselves */
-    first->phdr.p_vaddr -= size;
-    first->phdr.p_paddr -= size;
-    first->phdr.p_filesz += size;
-    first->phdr.p_memsz += size;
-
-    injOffset = ROUNDUP(injOffset, align);
-
-    if (injPhdr) {
-      *injPhdr = first;
-    }
-    return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset);
-  }
-
-  ERROR:
-  if (injPhdr) {
-    *injPhdr = NULL;
-  }
-  return 0;
-}
-
-
-
-
 static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
 {
   ElfuScn *newscn = NULL;
@@ -238,11 +20,12 @@ static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
     }
 
 
-    injAddr = getSpaceInPhdr(me, newscn->shdr.sh_size,
-                             newscn->shdr.sh_addralign,
-                             newscn->shdr.sh_flags & SHF_WRITE,
-                             newscn->shdr.sh_flags & SHF_EXECINSTR,
-                             &injPhdr);
+    injAddr = elfu_mLayoutGetSpaceInPhdr(me,
+                                         newscn->shdr.sh_size,
+                                         newscn->shdr.sh_addralign,
+                                         newscn->shdr.sh_flags & SHF_WRITE,
+                                         newscn->shdr.sh_flags & SHF_EXECINSTR,
+                                         &injPhdr);
 
     if (!injPhdr) {
       ELFU_WARN("insertSection: Could not find a place to insert section.\n");