diff options
Diffstat (limited to 'src/model/relocate.c')
-rw-r--r-- | src/model/relocate.c | 96 |
1 files changed, 90 insertions, 6 deletions
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; } } |