diff options
Diffstat (limited to 'src/model/relocate.c')
-rw-r--r-- | src/model/relocate.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/model/relocate.c b/src/model/relocate.c new file mode 100644 index 0000000..1b4f9ff --- /dev/null +++ b/src/model/relocate.c @@ -0,0 +1,85 @@ +#include <assert.h> +#include <stdlib.h> +#include <libelfu/libelfu.h> + + +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 { + // TODO: UNDEF, ABS, or COMMON + ELFU_WARN("symtabLookupVal: Returning 0 for UNDEF, ABS, or COMMON symbol.\n"); + } + 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: + // TODO + ELFU_WARN("symtabLookupVal: Returning 0 for FILE symbol.\n"); + break; + default: + ELFU_WARN("symtabLookupVal: Unknown symbol type %d.\n", sym->type); + 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; + } +} |