summaryrefslogtreecommitdiff
path: root/src/modelops/relocate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modelops/relocate.c')
-rw-r--r--src/modelops/relocate.c169
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;
+ }
+}