/* This file is part of centaur.
*
* centaur is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License 2 as
* published by the Free Software Foundation.
* centaur is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with centaur. If not, see .
*/
#include
#include
#include
#include
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;
}
/* Hazard a guess where a global variable may be found in .bss,
* based on dynamic linking information in .rel.dyn */
int elfu_mDynLookupReldynAddrByName(ElfuElf *me, char *name, GElf_Addr *result)
{
ElfuScn *reldyn;
ElfuRel *rel;
GElf_Word j;
reldyn = elfu_mScnForall(me, subFindByName, ".rel.dyn", NULL);
if (!reldyn) {
/* x86-64 uses .rela.dyn instead */
reldyn = elfu_mScnForall(me, subFindByName, ".rela.dyn", NULL);
}
if (!reldyn) {
ELFU_WARN("elfu_mDynLookupReldynAddrByName: Could not find .rel.dyn section in destination ELF.\n");
return -1;
}
assert(reldyn->linkptr);
j = 0;
CIRCLEQ_FOREACH(rel, &reldyn->reltab.rels, elem) {
ElfuSym *sym;
char *symname;
j++;
/* We only consider COPY relocations for global variables here.
* Technically, these relocations write the variables' contents
* to .bss. */
if ((me->elfclass == ELFCLASS32 && rel->type != R_386_COPY)
|| (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_COPY)) {
continue;
}
/* Get the (rel->sym)-th symbol from the symbol table that
* .rel.dyn points to. */
sym = elfu_mSymtabIndexToSym(reldyn->linkptr, rel->sym);
assert(sym);
symname = elfu_mSymtabSymToName(reldyn->linkptr, sym);
if (!strcmp(symname, name)) {
GElf_Addr addr = rel->offset;
ELFU_DEBUG("elfu_mDynLookupReldynAddrByName: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
*result = addr;
return 0;
}
}
ELFU_WARN("elfu_mDynLookupReldynAddrByName: Could not find or use symbol '%s' in destination ELF.\n", name);
ELFU_WARN(" NOTE: Only R_*_COPY relocations are resolved to global variables.\n");
return -1;
}