diff options
author | norly <ny-git@enpas.org> | 2013-06-26 19:57:39 +0100 |
---|---|---|
committer | norly <ny-git@enpas.org> | 2013-06-26 21:33:14 +0100 |
commit | 95516f950cf67ee154f362425c1472637d9f3e22 (patch) | |
tree | a60fbab2d4f19ac4178f00b4f23926cc6afe5d75 /src/libelfu/modelops | |
parent | 9ebc757b5592844e47bcddfba7335d2e7c590a8b (diff) |
Separate PLT lookup
Diffstat (limited to 'src/libelfu/modelops')
-rw-r--r-- | src/libelfu/modelops/dynlookup.c | 93 | ||||
-rw-r--r-- | src/libelfu/modelops/reladd.c | 13 | ||||
-rw-r--r-- | src/libelfu/modelops/relocate.c | 114 |
3 files changed, 185 insertions, 35 deletions
diff --git a/src/libelfu/modelops/dynlookup.c b/src/libelfu/modelops/dynlookup.c new file mode 100644 index 0000000..a2cd3ae --- /dev/null +++ b/src/libelfu/modelops/dynlookup.c @@ -0,0 +1,93 @@ +#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 */ +int elfu_mDynLookupPltAddrByName(ElfuElf *me, char *name, GElf_Addr *result) +{ + ElfuScn *relplt; + ElfuScn *plt; + ElfuRel *rel; + GElf_Word j; + + relplt = elfu_mScnForall(me, subFindByName, ".rel.plt", NULL); + if (!relplt) { + /* x86-64 uses .rela.plt instead */ + relplt = elfu_mScnForall(me, subFindByName, ".rela.plt", NULL); + } + if (!relplt) { + ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .rel.plt section in destination ELF.\n"); + return -1; + } + + plt = elfu_mScnForall(me, subFindByName, ".plt", NULL); + if (!plt) { + ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .plt section in destination ELF.\n"); + return -1; + } + + + /* Look up name. If the j-th entry in .rel.plt has the name we are + * looking for, we assume that the (j+1)-th entry in .plt is machine + * code to jump to the function. + * Your mileage may vary, but it works on my GNU binaries. */ + assert(relplt->linkptr); + j = 0; + CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) { + GElf_Word i; + ElfuSym *sym; + char *symname; + + j++; + + /* We only consider runtime relocations for functions. + * Technically, these relocations write the functions' addresses + * to the GOT, not the PLT, after the dynamic linker has found + * them. */ + if ((me->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT) + || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) { + continue; + } + + /* Get the (rel->sym)-th symbol from the symbol table that + * .rel.plt points to. */ + sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab.syms); + for (i = 1; i < rel->sym; i++) { + sym = CIRCLEQ_NEXT(sym, elem); + } + + symname = ELFU_SYMSTR(relplt->linkptr, sym->name); + if (!strcmp(symname, 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("elfu_mDynLookupPltAddr: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j); + *result = addr; + return 0; + } + } + + ELFU_WARN("elfu_mDynLookupPltAddr: Could not find symbol '%s' in destination ELF.\n", name); + + return -1; +} diff --git a/src/libelfu/modelops/reladd.c b/src/libelfu/modelops/reladd.c index 55438db..bf3b8ca 100644 --- a/src/libelfu/modelops/reladd.c +++ b/src/libelfu/modelops/reladd.c @@ -210,7 +210,9 @@ static void* subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2) case SHT_RELA: /* 4 */ case SHT_REL: /* 9 */ /* Relocate. */ - elfu_mRelocate(me, elfu_mScnByOldscn(me, ms->infoptr), ms); + if (elfu_mRelocate(me, elfu_mScnByOldscn(me, ms->infoptr), ms)) { + return (void*)-1; + } break; /* The next section types either do not occur in .o files, or are @@ -328,7 +330,7 @@ static void mergeSymtab(ElfuElf *me, const ElfuElf *mrel) -void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel) +int elfu_mReladd(ElfuElf *me, const ElfuElf *mrel) { assert(me); assert(mrel); @@ -339,8 +341,13 @@ void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel) mergeSymtab(me, mrel); /* Do relocations and other stuff */ - elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL); + if (elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL)) { + ELFU_WARN("elfu_mReladd: Reladd aborted. Target model is unclean.\n"); + return -1; + } /* Re-layout to accommodate new contents */ elfu_mLayoutAuto(me); + + return 0; } diff --git a/src/libelfu/modelops/relocate.c b/src/libelfu/modelops/relocate.c index 2eada04..7f78524 100644 --- a/src/libelfu/modelops/relocate.c +++ b/src/libelfu/modelops/relocate.c @@ -6,9 +6,10 @@ /* Apply relocation information from section *msrt to data in * section *mstarget (which is stored in *metarget). */ -void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt) +int elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt) { ElfuRel *rel; + ElfuSym *sym; assert(mstarget); assert(msrt); @@ -20,51 +21,100 @@ void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt) CIRCLEQ_FOREACH(rel, &msrt->reltab.rels, elem) { Elf32_Word *dest32 = (Elf32_Word*)(mstarget->databuf + rel->offset); Elf64_Word *dest64 = (Elf64_Word*)(mstarget->databuf + rel->offset); + GElf_Addr s; + int haveSymval = 0; + sym = elfu_mSymtabIndexToSym(msrt->linkptr, rel->sym); + assert(sym); + + haveSymval = !elfu_mSymtabLookupSymToAddr(metarget, + msrt->linkptr, + sym, + &s); + if (!haveSymval) { + if (sym->shndx == SHN_UNDEF) { + haveSymval = !elfu_mDynLookupPltAddrByName(metarget, + elfu_mSymtabSymToName(msrt->linkptr, sym), + &s); + } else if (sym->shndx == SHN_COMMON) { + // TODO: Lookup in .rel.dyn + } + } if (metarget->elfclass == ELFCLASS32) { Elf32_Word a32 = rel->addendUsed ? rel->addend : *dest32; Elf32_Addr p32 = mstarget->shdr.sh_addr + rel->offset; - Elf32_Addr s32 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym); - switch(rel->type) { - case R_386_NONE: - ELFU_DEBUG("Skipping relocation: R_386_NONE\n"); - break; - case R_386_32: - *dest32 = s32 + a32; - break; - case R_386_PC32: - *dest32 = s32 + a32 - p32; - break; + Elf32_Addr s32 = (Elf32_Addr)s; + switch(metarget->ehdr.e_machine) { + case EM_386: + switch(rel->type) { + case R_386_NONE: + ELFU_DEBUG("Skipping relocation: R_386_NONE\n"); + break; + case R_386_32: + if (!haveSymval) { + goto MISSINGSYM; + } + *dest32 = s32 + a32; + break; + case R_386_PC32: + if (!haveSymval) { + goto MISSINGSYM; + } + *dest32 = s32 + a32 - p32; + break; + default: + ELFU_DEBUG("elfu_mRelocate: Skipping unknown relocation type %d\n", rel->type); + } + break; default: - ELFU_DEBUG("Skipping relocation: Unknown type %d\n", rel->type); + ELFU_WARN("elfu_mRelocate: Unknown machine type. Aborting.\n"); } } else if (metarget->elfclass == ELFCLASS64) { - Elf64_Word a64 = rel->addend; + Elf64_Word a64 = rel->addendUsed ? rel->addend : *dest64; Elf64_Addr p64 = mstarget->shdr.sh_addr + rel->offset; - Elf64_Addr s64 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym); - - /* x86-64 only uses RELA with explicit addend. */ - assert(rel->addendUsed); + Elf64_Addr s64 = (Elf64_Addr)s; - switch(rel->type) { - case R_X86_64_NONE: - ELFU_DEBUG("Skipping relocation: R_386_NONE\n"); + switch(metarget->ehdr.e_machine) { + case EM_X86_64: + switch(rel->type) { + case R_X86_64_NONE: + ELFU_DEBUG("Skipping relocation: R_386_NONE\n"); + break; + case R_X86_64_64: + if (!haveSymval) { + goto MISSINGSYM; + } + *dest64 = s64 + a64; + break; + case R_X86_64_PC32: + if (!haveSymval) { + goto MISSINGSYM; + } + *dest32 = s64 + a64 - p64; + break; + case R_X86_64_32: + if (!haveSymval) { + goto MISSINGSYM; + } + *dest32 = s64 + a64; + break; + default: + ELFU_DEBUG("elfu_mRelocate: Skipping unknown relocation type %d", rel->type); + } break; - case R_X86_64_64: - *dest64 = s64 + a64; - break; - case R_X86_64_PC32: - *dest32 = s64 + a64 - p64; - break; - case R_X86_64_32: - *dest32 = s64 + a64; - break; - default: - ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type); + ELFU_WARN("elfu_mRelocate: Unknown machine type. Aborting.\n"); } } } + + return 0; + + MISSINGSYM: + ELFU_WARN("elfu_mRelocate: Could not resolve symbol %s. Aborting.\n", + elfu_mSymtabSymToName(msrt->linkptr, sym)); + return -1; + } |