summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authornorly <ny-git@enpas.org>2013-06-26 19:57:39 +0100
committernorly <ny-git@enpas.org>2013-06-26 21:33:14 +0100
commit95516f950cf67ee154f362425c1472637d9f3e22 (patch)
treea60fbab2d4f19ac4178f00b4f23926cc6afe5d75 /src
parent9ebc757b5592844e47bcddfba7335d2e7c590a8b (diff)
Separate PLT lookup
Diffstat (limited to 'src')
-rw-r--r--src/elfucli.c10
-rw-r--r--src/libelfu/model/symtab.c157
-rw-r--r--src/libelfu/modelops/dynlookup.c93
-rw-r--r--src/libelfu/modelops/reladd.c13
-rw-r--r--src/libelfu/modelops/relocate.c114
5 files changed, 236 insertions, 151 deletions
diff --git a/src/elfucli.c b/src/elfucli.c
index b3ea29f..d5f55be 100644
--- a/src/elfucli.c
+++ b/src/elfucli.c
@@ -149,8 +149,14 @@ int main(int argc, char **argv)
goto ERR;
} else {
printf("--reladd: Injecting %s...\n", optarg);
- elfu_mCheck(mrel);
- elfu_mReladd(me, mrel);
+ if (elfu_mCheck(mrel)) {
+ printf("--reladd: Check for input file failed.\n");
+ goto ERR;
+ }
+ if (elfu_mReladd(me, mrel)) {
+ printf("--reladd: Failed.\n");
+ goto ERR;
+ }
printf("--reladd: Injected %s.\n", optarg);
}
}
diff --git a/src/libelfu/model/symtab.c b/src/libelfu/model/symtab.c
index 1dd778e..0cf107b 100644
--- a/src/libelfu/model/symtab.c
+++ b/src/libelfu/model/symtab.c
@@ -4,103 +4,62 @@
#include <libelfu/libelfu.h>
-static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+int elfu_mSymtabLookupSymToAddr(ElfuElf *me, ElfuScn *msst, ElfuSym *sym, GElf_Addr *result)
{
- char *name = (char*)aux1;
- (void)aux2;
+ char *symname = ELFU_SYMSTR(msst, sym->name);
- 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 *me, char *name)
-{
- 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("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n");
- return 0;
- }
+ switch (sym->type) {
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ case STT_FUNC:
+ if (sym->shndx == SHN_UNDEF) {
+ return -1;
+ } else if (sym->shndx == SHN_ABS) {
+ *result = sym->value;
+ return 0;
+ } else if (sym->shndx == SHN_COMMON) {
+ return -1;
+ } else {
+ ElfuScn *newscn;
- plt = elfu_mScnForall(me, subFindByName, ".plt", NULL);
- if (!plt) {
- ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n");
- return 0;
+ assert (sym->scnptr);
+ newscn = elfu_mScnByOldscn(me, sym->scnptr);
+ assert(newscn);
+ *result = newscn->shdr.sh_addr + sym->value;
+ return 0;
+ }
+ break;
+ case STT_SECTION:
+ assert(sym->scnptr);
+ assert(elfu_mScnByOldscn(me, sym->scnptr));
+ *result = elfu_mScnByOldscn(me, sym->scnptr)->shdr.sh_addr;
+ return 0;
+ case STT_FILE:
+ ELFU_WARN("elfu_mSymtabLookupSymToAddr: Symbol type FILE is not supported.\n");
+ return -1;
+ default:
+ ELFU_WARN("elfu_mSymtabLookupSymToAddr: Unknown symbol type %d for %s.\n", sym->type, symname);
+ 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("dynsymLookupVal: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
- return addr;
- }
- }
-
- ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name);
+char* elfu_mSymtabSymToName(ElfuScn *msst, ElfuSym *sym)
+{
+ assert(msst);
+ assert(sym);
- return 0;
+ return ELFU_SYMSTR(msst, sym->name);
}
-/* Look up a value in the symbol table section *msst which is in *me.
- * If it is not found there, see if we can find it in *me's PLT. */
-GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry)
+ElfuSym* elfu_mSymtabIndexToSym(ElfuScn *msst, GElf_Word entry)
{
GElf_Word i;
ElfuSym *sym;
- char *symname;
- assert(me);
assert(msst);
assert(entry > 0);
assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
@@ -109,44 +68,14 @@ GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry)
for (i = 1; i < entry; i++) {
sym = CIRCLEQ_NEXT(sym, elem);
}
- symname = ELFU_SYMSTR(msst, sym->name);
- switch (sym->type) {
- case STT_NOTYPE:
- case STT_OBJECT:
- case STT_FUNC:
- if (sym->scnptr) {
- ElfuScn *newscn = elfu_mScnByOldscn(me, sym->scnptr);
- assert(newscn);
- return newscn->shdr.sh_addr + sym->value;
- } else if (sym->shndx == SHN_UNDEF) {
- /* Look the symbol up in .rel.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(me, symname);
- } 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(me, sym->scnptr));
- return elfu_mScnByOldscn(me, 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, symname);
- return 0;
- }
+ return sym;
}
+
/* Look up a value in the symbol table section *msst which is in *me. */
-GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
+GElf_Addr elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
{
ElfuSym *sym;
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;
+
}