summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libelfu/types.h1
-rw-r--r--src/model/fromFile.c3
-rw-r--r--src/model/layout.c6
-rw-r--r--src/model/reladd.c6
-rw-r--r--src/model/relocate.c96
5 files changed, 103 insertions, 9 deletions
diff --git a/include/libelfu/types.h b/include/libelfu/types.h
index 2a0fcf3..b216e53 100644
--- a/include/libelfu/types.h
+++ b/include/libelfu/types.h
@@ -61,6 +61,7 @@ typedef struct ElfuSym {
unsigned char other;
ElfuScn *scnptr;
+ int shndx;
CIRCLEQ_ENTRY(ElfuSym) elem;
} ElfuSym;
diff --git a/src/model/fromFile.c b/src/model/fromFile.c
index 84ec143..23105e6 100644
--- a/src/model/fromFile.c
+++ b/src/model/fromFile.c
@@ -52,12 +52,14 @@ static ElfuSymtab* symtabFromScn32(ElfuScn *ms, ElfuScn**origScnArr)
case SHN_ABS:
case SHN_COMMON:
sym->scnptr = NULL;
+ sym->shndx = cursym->st_shndx;
break;
default:
sym->scnptr = origScnArr[cursym->st_shndx - 1];
break;
}
+
CIRCLEQ_INSERT_TAIL(&st->syms, sym, elem);
}
@@ -404,6 +406,7 @@ ElfuElf* elfu_mFromElf(Elf *e)
switch (ms->shdr.sh_type) {
case SHT_SYMTAB:
+ case SHT_DYNSYM:
if (me->elfclass == ELFCLASS32) {
ms->symtab = symtabFromScn32(ms, secArray);
} else if (me->elfclass == ELFCLASS64) {
diff --git a/src/model/layout.c b/src/model/layout.c
index d5d49d8..e4b3fb1 100644
--- a/src/model/layout.c
+++ b/src/model/layout.c
@@ -82,6 +82,12 @@ GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
assert(!(w && x));
+ /* Treat read-only data as executable.
+ * That's what the GNU toolchain does on x86. */
+ if (!w && !x) {
+ x = 1;
+ }
+
/* Find first and last LOAD PHDRs.
* Don't compare p_memsz - segments don't overlap in memory. */
CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
diff --git a/src/model/reladd.c b/src/model/reladd.c
index a3ccc99..80f106f 100644
--- a/src/model/reladd.c
+++ b/src/model/reladd.c
@@ -111,10 +111,10 @@ static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
return newscn;
} else {
- ELFU_WARN("insertSection: Skipping section %s with flags %jd (type %d).\n",
+ ELFU_WARN("insertSection: Skipping non-memory section %s (type %d flags %jd).\n",
elfu_mScnName(mrel, oldscn),
- oldscn->shdr.sh_flags,
- oldscn->shdr.sh_type);
+ oldscn->shdr.sh_type,
+ oldscn->shdr.sh_flags);
goto ERROR;
}
diff --git a/src/model/relocate.c b/src/model/relocate.c
index 1b4f9ff..972bda3 100644
--- a/src/model/relocate.c
+++ b/src/model/relocate.c
@@ -1,8 +1,86 @@
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
#include <libelfu/libelfu.h>
+static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ char *name = (char*)aux1;
+ (void)aux2;
+
+ if (elfu_mScnName(me, ms)) {
+ if (!strcmp(elfu_mScnName(me, ms), name)) {
+ return ms;
+ }
+ }
+
+ /* Continue */
+ return NULL;
+}
+
+/* Hazard a guess where a function may be found in the PLT */
+static GElf_Word pltLookupVal(ElfuElf *metarget, char *name)
+{
+ ElfuScn *relplt;
+ ElfuScn *plt;
+ ElfuRel *rel;
+ GElf_Word j;
+
+ relplt = elfu_mScnForall(metarget, subFindByName, ".rel.plt", NULL);
+ if (!relplt) {
+ ELFU_WARN("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n");
+ return 0;
+ }
+
+ plt = elfu_mScnForall(metarget, subFindByName, ".plt", NULL);
+ if (!plt) {
+ ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n");
+ return 0;
+ }
+
+
+ /* Look up name */
+ assert(relplt->reltab);
+ assert(relplt->linkptr);
+ assert(relplt->linkptr->symtab);
+ j = 0;
+ CIRCLEQ_FOREACH(rel, &relplt->reltab->rels, elem) {
+ GElf_Word i;
+ ElfuSym *sym;
+
+ j++;
+
+ if (rel->type != R_386_JMP_SLOT) {
+ continue;
+ }
+
+ sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab->syms);
+ for (i = 1; i < rel->sym; i++) {
+ sym = CIRCLEQ_NEXT(sym, elem);
+ }
+
+ if (!sym->name) {
+ continue;
+ }
+
+ if (!strcmp(sym->name, name)) {
+ /* If this is the symbol we are looking for, then in an x86 binary
+ * the jump to the dynamic symbol is probably at offset (j * 16)
+ * from the start of the PLT, where j is the PLT entry and 16 is
+ * the number of bytes the machine code in a PLT entry take. */
+ GElf_Addr addr = plt->shdr.sh_addr + (16 * j);
+ ELFU_DEBUG("dynsymLookupVal: Guessing symbol '%s' is in destination memory at %jx (PLT entry #%d).\n", name, addr, j);
+ return addr;
+ }
+ }
+
+ ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name);
+
+ return 0;
+}
+
+
static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word entry)
{
GElf_Word i;
@@ -26,9 +104,16 @@ static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word ent
if (sym->scnptr) {
assert(elfu_mScnByOldscn(metarget, sym->scnptr));
return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr + sym->value;
+ } else if (sym->shndx == SHN_UNDEF) {
+ /* Look the symbol up in .dyn.plt. If it cannot be found there then
+ * .rel.dyn may need to be expanded with a COPY relocation so the
+ * dynamic linker fixes up the (TODO). */
+ return pltLookupVal(metarget, sym->name);
+ } else if (sym->shndx == SHN_ABS) {
+ return sym->value;
} else {
- // TODO: UNDEF, ABS, or COMMON
- ELFU_WARN("symtabLookupVal: Returning 0 for UNDEF, ABS, or COMMON symbol.\n");
+ ELFU_WARN("symtabLookupVal: Symbol binding COMMON is not supported, using 0.\n");
+ return 0;
}
break;
case STT_SECTION:
@@ -36,11 +121,10 @@ static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word ent
assert(elfu_mScnByOldscn(metarget, sym->scnptr));
return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr;
case STT_FILE:
- // TODO
- ELFU_WARN("symtabLookupVal: Returning 0 for FILE symbol.\n");
- break;
+ ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n");
+ return 0;
default:
- ELFU_WARN("symtabLookupVal: Unknown symbol type %d.\n", sym->type);
+ ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, sym->name);
return 0;
}
}