summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornorly <ny-git@enpas.org>2013-05-24 07:23:41 +0100
committernorly <ny-git@enpas.org>2013-05-24 07:23:41 +0100
commit264965b9323f90f1a834fb2d80c362aa91fbb5e5 (patch)
tree09fbfa898d674060970f5658b0c93b9a502d1693
parentb7ab2674931f1bb9a7e6da594f587ae84c293238 (diff)
Object file injection, first part
-rw-r--r--include/libelfu/modelops.h19
-rw-r--r--include/options.h1
-rw-r--r--src/main.c26
-rw-r--r--src/model/clone.c35
-rw-r--r--src/model/count.c20
-rw-r--r--src/model/insert.c75
-rw-r--r--src/model/reladd.c233
-rw-r--r--src/model/section-by-type.c22
-rw-r--r--src/model/section-in-segment.c56
-rw-r--r--src/options.c7
10 files changed, 488 insertions, 6 deletions
diff --git a/include/libelfu/modelops.h b/include/libelfu/modelops.h
index 3ce9a90..1cfe0b6 100644
--- a/include/libelfu/modelops.h
+++ b/include/libelfu/modelops.h
@@ -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
diff --git a/include/options.h b/include/options.h
index e634e0b..2210c85 100644
--- a/include/options.h
+++ b/include/options.h
@@ -13,6 +13,7 @@ typedef struct {
unsigned insertAfterOffs;
unsigned insertAfterSz;
unsigned expandNobitsOffs;
+ char *fnReladd;
} CLIOpts;
diff --git a/src/main.c b/src/main.c
index a3bf271..2c6268c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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
index 0000000..3dbbc54
--- /dev/null
+++ b/src/model/clone.c
@@ -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;
+}
diff --git a/src/model/count.c b/src/model/count.c
index b7c8013..671767e 100644
--- a/src/model/count.c
+++ b/src/model/count.c
@@ -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)
diff --git a/src/model/insert.c b/src/model/insert.c
index 492f06a..312be26 100644
--- a/src/model/insert.c
+++ b/src/model/insert.c
@@ -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
index 0000000..782d245
--- /dev/null
+++ b/src/model/reladd.c
@@ -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
index 0000000..e36865a
--- /dev/null
+++ b/src/model/section-by-type.c
@@ -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
index 0000000..ad7f037
--- /dev/null
+++ b/src/model/section-in-segment.c
@@ -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;
+}
diff --git a/src/options.c b/src/options.c
index 1a191a5..928d080 100644
--- a/src/options.c
+++ b/src/options.c
@@ -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;