diff options
Diffstat (limited to 'ldso/ldso/arc/elfinterp.c')
-rw-r--r-- | ldso/ldso/arc/elfinterp.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/ldso/ldso/arc/elfinterp.c b/ldso/ldso/arc/elfinterp.c new file mode 100644 index 000000000..a3d741b65 --- /dev/null +++ b/ldso/ldso/arc/elfinterp.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * Lots of code copied from ../i386/elfinterp.c, so: + * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald, + * David Engel, Hongjiu Lu and Mitch D'Souza + * Copyright (C) 2001-2002, Erik Andersen + * All rights reserved. + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ +#include "ldso.h" + +#define ARC_PLT_SIZE 12 + +unsigned long +_dl_linux_resolver(struct elf_resolve *tpnt, unsigned int plt_pc) +{ + ELF_RELOC *this_reloc, *rel_base; + char *strtab, *symname, *new_addr; + ElfW(Sym) *symtab; + int symtab_index; + unsigned int *got_addr; + unsigned long plt_base; + int plt_idx; + + /* start of .rela.plt */ + rel_base = (ELF_RELOC *)(tpnt->dynamic_info[DT_JMPREL]); + + /* starts of .plt (addr of PLT0) */ + plt_base = tpnt->dynamic_info[DT_PLTGOT]; + + /* + * compute the idx of the yet-unresolved PLT entry in .plt + * Same idx will be used to find the relo entry in .rela.plt + */ + plt_idx = (plt_pc - plt_base)/ARC_PLT_SIZE - 2; /* ignoring 2 dummy PLTs */ + + this_reloc = rel_base + plt_idx; + + symtab_index = ELF_R_SYM(this_reloc->r_info); + symtab = (ElfW(Sym) *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB]); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]); + symname= strtab + symtab[symtab_index].st_name; + + /* relo-offset to fixup, shd be a .got entry */ + got_addr = (unsigned int *)(this_reloc->r_offset + tpnt->loadaddr); + + /* Get the address of the GOT entry */ + new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, tpnt, + ELF_RTYPE_CLASS_PLT, NULL); + + if (unlikely(!new_addr)) { + _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname); + _dl_exit(1); + } + + +#if defined __SUPPORT_LD_DEBUG__ + if (_dl_debug_bindings) { + _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname); + if(_dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\n\tpatched %x ==> %pc @ %pl\n", + *got_addr, new_addr, got_addr); + } + + if (!_dl_debug_nofixups) + *got_addr = (unsigned int)new_addr; +#else + /* Update the .got entry with the runtime address of symbol */ + *got_addr = (unsigned int)new_addr; +#endif + + /* + * Return the new addres, where the asm trampoline will jump to + * after re-setting up the orig args + */ + return (unsigned long) new_addr; +} + + +static int +_dl_do_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope, + ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab) +{ + int reloc_type; + int symtab_index; + char *symname; + unsigned long *reloc_addr; + unsigned long symbol_addr; +#if defined __SUPPORT_LD_DEBUG__ + unsigned long old_val = 0; +#endif + struct symbol_ref sym_ref; + + reloc_addr = (unsigned long *)(tpnt->loadaddr + rpnt->r_offset); + reloc_type = ELF_R_TYPE(rpnt->r_info); + symtab_index = ELF_R_SYM(rpnt->r_info); + symbol_addr = 0; + + sym_ref.sym = &symtab[symtab_index]; + sym_ref.tpnt = NULL; + +#if defined __SUPPORT_LD_DEBUG__ + if (reloc_addr) + old_val = *reloc_addr; +#endif + + if (symtab_index) { + symname = strtab + symtab[symtab_index].st_name; + symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt, + elf_machine_type_class(reloc_type), &sym_ref); + + /* + * We want to allow undefined references to weak symbols, + * this might have been intentional. We should not be linking + * local symbols here, so all bases should be covered. + */ + + if (unlikely(!symbol_addr + && ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) { + /* Non-fatal if called from dlopen, hence different ret code */ + return 1; + } + } else if (reloc_type == R_ARC_RELATIVE ) { + *reloc_addr += tpnt->loadaddr; + goto log_entry; + } + + switch (reloc_type) { + case R_ARC_32: + *reloc_addr += symbol_addr + rpnt->r_addend; + break; + case R_ARC_PC32: + *reloc_addr += symbol_addr + rpnt->r_addend - (unsigned long) reloc_addr; + break; + case R_ARC_GLOB_DAT: + case R_ARC_JMP_SLOT: + *reloc_addr = symbol_addr; + break; + case R_ARC_COPY: + _dl_memcpy((void *) reloc_addr,(void *) symbol_addr, + symtab[symtab_index].st_size); + break; + default: + return -1; + } + +log_entry: +#if defined __SUPPORT_LD_DEBUG__ + if(_dl_debug_detail) + _dl_dprintf(_dl_debug_file,"\tpatched: %lx ==> %lx @ %pl: addend %x ", + old_val, *reloc_addr, reloc_addr, rpnt->r_addend); +#endif + + return 0; +} + +static int +_dl_do_lazy_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope, + ELF_RELOC *rpnt) +{ + int reloc_type; + unsigned long *reloc_addr; +#if defined __SUPPORT_LD_DEBUG__ + unsigned long old_val; +#endif + + reloc_addr = (unsigned long *)(tpnt->loadaddr + rpnt->r_offset); + reloc_type = ELF_R_TYPE(rpnt->r_info); + +#if defined __SUPPORT_LD_DEBUG__ + old_val = *reloc_addr; +#endif + + switch (reloc_type) { + case R_ARC_JMP_SLOT: + *reloc_addr += tpnt->loadaddr; + break; + default: + return -1; + } + +#if defined __SUPPORT_LD_DEBUG__ + if(_dl_debug_reloc && _dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\tpatched: %lx ==> %lx @ %pl\n", + old_val, *reloc_addr, reloc_addr); +#endif + + return 0; +} + +#define ___DO_LAZY 1 +#define ___DO_NOW 2 + +static int _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope, + unsigned long rel_addr, unsigned long rel_size, int type) +{ + unsigned int i; + char *strtab; + ElfW(Sym) *symtab; + ELF_RELOC *rpnt; + int symtab_index; + int res = 0; + + /* Now parse the relocation information */ + rpnt = (ELF_RELOC *)(intptr_t) (rel_addr); + rel_size = rel_size / sizeof(ELF_RELOC); + + symtab = (ElfW(Sym) *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB]); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]); + + for (i = 0; i < rel_size; i++, rpnt++) { + + symtab_index = ELF_R_SYM(rpnt->r_info); + + debug_sym(symtab,strtab,symtab_index); + debug_reloc(symtab,strtab,rpnt); + + /* constant propagation subsumes the 'if' */ + if (type == ___DO_LAZY) + res = _dl_do_lazy_reloc(tpnt, scope, rpnt); + else + res = _dl_do_reloc(tpnt, scope, rpnt, symtab, strtab); + + if (res != 0) + break; + } + + if (unlikely(res != 0)) { + if (res < 0) { + int reloc_type = ELF_R_TYPE(rpnt->r_info); +#if defined __SUPPORT_LD_DEBUG__ + _dl_dprintf(2, "can't handle reloc type %s\n ", + _dl_reltypes(reloc_type)); +#else + _dl_dprintf(2, "can't handle reloc type %x\n", + reloc_type); +#endif + _dl_exit(-res); + } else { + _dl_dprintf(2, "can't resolve symbol\n"); + /* Fall thru to return res */ + } + } + + return res; +} + +void +_dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, + unsigned long rel_addr, + unsigned long rel_size) +{ + /* This func is called for processing .rela.plt of loaded module(s) + * The relo entries handled are JMP_SLOT type for fixing up .got slots + * for external function calls. + * This function doesn't resolve the slots: that is done lazily at + * runtime. The build linker (at least thats what happens for ARC) had + * pre-init the .got slots to point to PLT0. All that is done here is + * to fix them up to point to load value of PLT0 (as opposed to the + * build value). + * On ARC, the loadaddr of dyn exec is zero, thus elfaddr == loadaddr + * Thus there is no point in adding "0" to values and un-necessarily + * stir up the caches and TLB. + * For lsdo processing busybox binary, this skips over 380 relo entries + */ + if (rpnt->dyn->loadaddr != 0) + _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, ___DO_LAZY); +} + +int +_dl_parse_relocation_information(struct dyn_elf *rpnt, + struct r_scope_elem *scope, + unsigned long rel_addr, + unsigned long rel_size) +{ + return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, ___DO_NOW); +} |