diff options
Diffstat (limited to 'src/modelops/relocate.c')
-rw-r--r-- | src/modelops/relocate.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/modelops/relocate.c b/src/modelops/relocate.c new file mode 100644 index 0000000..972bda3 --- /dev/null +++ b/src/modelops/relocate.c @@ -0,0 +1,169 @@ +#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; + ElfuSym *sym; + + assert(metarget); + assert(msst); + assert(msst->symtab); + assert(entry > 0); + assert(!CIRCLEQ_EMPTY(&msst->symtab->syms)); + + sym = CIRCLEQ_FIRST(&msst->symtab->syms); + for (i = 1; i < entry; i++) { + sym = CIRCLEQ_NEXT(sym, elem); + } + + switch (sym->type) { + case STT_NOTYPE: + case STT_OBJECT: + case STT_FUNC: + 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 { + ELFU_WARN("symtabLookupVal: Symbol binding COMMON is not supported, using 0.\n"); + return 0; + } + break; + case STT_SECTION: + assert(sym->scnptr); + assert(elfu_mScnByOldscn(metarget, sym->scnptr)); + return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr; + case STT_FILE: + ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n"); + return 0; + default: + ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, sym->name); + return 0; + } +} + +void elfu_mRelocate32(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt) +{ + ElfuRel *rel; + + assert(mstarget); + assert(msrt); + + ELFU_DEBUG("Relocating in section of type %d size %jx\n", + mstarget->shdr.sh_type, + mstarget->shdr.sh_size); + + CIRCLEQ_FOREACH(rel, &msrt->reltab->rels, elem) { + Elf32_Word *dest = (Elf32_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset); + Elf32_Word a = rel->addendUsed ? rel->addend : *dest; + Elf32_Addr p = mstarget->shdr.sh_addr + rel->offset; + Elf32_Addr s = symtabLookupVal(metarget, msrt->linkptr, rel->sym); + Elf32_Word newval = *dest; + + switch(rel->type) { + case R_386_NONE: + ELFU_DEBUG("Skipping relocation: R_386_NONE"); + break; + case R_386_32: + ELFU_DEBUG("Relocation: R_386_32"); + newval = s + a; + break; + case R_386_PC32: + ELFU_DEBUG("Relocation: R_386_PC32"); + newval = s + a - p; + break; + + default: + ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type); + } + ELFU_DEBUG(", overwriting %x with %x.\n", *dest, newval); + *dest = newval; + } +} |