Insert section names (with fixed prefix)
[centaur.git] / src / model / reladd.c
index bc13b97b35e355a9a77ac726d75ef784434fdb0a..632c7c368ecb9d605747fdfac86fbe62d88a76c9 100644 (file)
 #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)
+static int appendData(ElfuScn *ms, void *buf, size_t len)
 {
-  ElfuPhdr *first = NULL;
-  ElfuPhdr *last = NULL;
-  ElfuPhdr *mp;
+  void *newbuf;
 
-  assert(!(w && x));
+  assert(ms);
+  assert(ms->shdr.sh_type != SHT_NOBITS);
+  assert(ms->data.d_buf);
 
-  /* 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;
-    }
+  newbuf = realloc(ms->data.d_buf, ms->shdr.sh_size + len);
+  if (!newbuf) {
+    ELFU_WARN("appendData: malloc() failed for newbuf.\n");
+    return 1;
   }
 
-  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);
-  }
+  ms->data.d_buf = newbuf;
+  memcpy(newbuf + ms->shdr.sh_size, buf, len);
+  ms->shdr.sh_size += len;
+  ms->data.d_size += len;
+  assert(ms->shdr.sh_size == ms->data.d_size);
 
-  ERROR:
-  if (injPhdr) {
-    *injPhdr = NULL;
-  }
   return 0;
 }
 
 
-
-
 static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
 {
   ElfuScn *newscn = NULL;
@@ -238,11 +44,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");
@@ -271,9 +78,35 @@ static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
       }
     }
 
+
     /* Inject name */
-    // TODO
-    newscn->shdr.sh_name = 0;
+    if (me->shstrtab) {
+      char *newname;
+      size_t newnamelen;
+
+      newnamelen = strlen("reladd") + 1;
+      if (elfu_mScnName(mrel, oldscn)) {
+        newnamelen += strlen(elfu_mScnName(mrel, oldscn));
+      }
+
+      newname = malloc(newnamelen);
+      strcpy(newname, "reladd");
+      strcat(newname, elfu_mScnName(mrel, oldscn));
+
+      if (!newname) {
+        ELFU_WARN("insertSection: malloc() failed for newname. Leaving section name empty.\n");
+        newscn->shdr.sh_name = 0;
+      } else {
+        size_t offset = me->shstrtab->shdr.sh_size;
+
+        if (!appendData(me->shstrtab, newname, newnamelen)) {
+          newscn->shdr.sh_name = offset;
+        }
+
+        free(newname);
+      }
+    }
+
 
     // TODO: Relocate
 
@@ -377,4 +210,7 @@ void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
 
   /* Do relocations and other stuff */
   elfu_mScnForall(mrel, subScnAdd2, me, NULL);
+
+  /* Re-layout to accommodate new contents */
+  elfu_mLayoutAuto(me);
 }