From 6618d5aa2feedded6358ba9c3d50cb8ee08fbd20 Mon Sep 17 00:00:00 2001 From: Christophe Lyon Date: Thu, 28 Mar 2013 10:46:55 +0100 Subject: rtld: Add lazy binding support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for R_ARM_FUNCDESC_VALUE and implement _dl_linux_resolver for FDPIC on ARM. * ldso/ldso/arm/elfinterp.c (_dl_linux_resolver): Support __FDPIC__. (_dl_do_lazy_reloc): Likewise. * ldso/ldso/arm/resolve.S (_dl_linux_resolve): Likewise. Signed-off-by: Mickaël Guêné Signed-off-by: Christophe Lyon --- ldso/ldso/arm/elfinterp.c | 83 ++++++++++++++++++++++++++++++++++++++++++----- ldso/ldso/arm/resolve.S | 22 +++++++++++++ 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/ldso/ldso/arm/elfinterp.c b/ldso/ldso/arm/elfinterp.c index 402ba9618..1d79d925b 100644 --- a/ldso/ldso/arm/elfinterp.c +++ b/ldso/ldso/arm/elfinterp.c @@ -34,13 +34,69 @@ extern int _dl_linux_resolve(void); -unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) -{ #if __FDPIC__ - /* FIXME: implement. */ - while(1) ; - return 0; +unsigned long _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_offet) +{ + ELF_RELOC *this_reloc; + char *strtab; + ElfW(Sym) *symtab; + int symtab_index; + char *rel_addr; + char *new_addr; + struct funcdesc_value funcval; + struct funcdesc_value volatile *got_entry; + char *symname; + struct symbol_ref sym_ref; + + rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL]; + + this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_offet); + symtab_index = ELF_R_SYM(this_reloc->r_info); + + symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *) tpnt->dynamic_info[DT_STRTAB]; + sym_ref.sym = &symtab[symtab_index]; + sym_ref.tpnt = NULL; + symname= strtab + symtab[symtab_index].st_name; + + /* Address of GOT entry fix up */ + got_entry = (struct funcdesc_value *) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset); + + /* Get the address to be used to fill in the GOT entry. */ + new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, NULL, 0, &sym_ref); + if (!new_addr) { + new_addr = _dl_find_hash(symname, NULL, NULL, 0, &sym_ref); + if (!new_addr) { + _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname); + _dl_exit(1); + } + } + + funcval.entry_point = new_addr; + funcval.got_value = sym_ref.tpnt->loadaddr.got_value; + +#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,%x) ==> (%x,%x) @ %x\n", + got_entry->entry_point, got_entry->got_value, + funcval.entry_point, funcval.got_value, + got_entry); + } + if (1 || !_dl_debug_nofixups) { + *got_entry = funcval; + } +#else + *got_entry = funcval; +#endif + + return got_entry; +} #else +unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) +{ ELF_RELOC *this_reloc; char *strtab; char *symname; @@ -93,8 +149,8 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) #endif return new_addr; -#endif } +#endif static int _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope, @@ -348,7 +404,7 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, int reloc_type; unsigned long *reloc_addr; - reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset); + reloc_addr = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset); reloc_type = ELF_R_TYPE(rpnt->r_info); #if defined (__SUPPORT_LD_DEBUG__) @@ -358,9 +414,20 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, switch (reloc_type) { case R_ARM_NONE: break; + case R_ARM_JUMP_SLOT: - *reloc_addr += (unsigned long) tpnt->loadaddr; + *reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, *reloc_addr); + break; +#ifdef __FDPIC__ + case R_ARM_FUNCDESC_VALUE: + { + struct funcdesc_value *dst = (struct funcdesc_value *) reloc_addr; + + dst->entry_point = DL_RELOC_ADDR(tpnt->loadaddr, dst->entry_point); + dst->got_value = tpnt->loadaddr.got_value; + } break; +#endif default: return -1; /*call _dl_exit(1) */ } diff --git a/ldso/ldso/arm/resolve.S b/ldso/ldso/arm/resolve.S index 2a516436e..039a6b788 100644 --- a/ldso/ldso/arm/resolve.S +++ b/ldso/ldso/arm/resolve.S @@ -107,6 +107,27 @@ .type _dl_linux_resolve,%function .align 4; +#if __FDPIC__ +/* + * _dl_linux_resolve() FDPIC version receives the following parameters from + * lazy PLT entry: + * R12: GOT address for the resolver GOT + * SP[0]: funcdesc_value_reloc_offset(foo) + * R9: GOT address for the caller GOT + * _dl_linux_resolver() will return a function descriptor address in R0. + */ +_dl_linux_resolve: + push {r0, r1, r2, r3, r14} + ldr r0, [r9, #8] + ldr r1, [sp, #20] + mov r9, r12 + blx _dl_linux_resolver + ldr r9, [r0, #4] + ldr r12, [r0] + pop {r0, r1, r2, r3, r14} + add sp, sp, #4 + bx r12 +#else _dl_linux_resolve: @ _dl_linux_resolver is a standard subroutine call, therefore it @ preserves everything except r0-r3 (a1-a4), ip and lr. This @@ -129,6 +150,7 @@ _dl_linux_resolve: ldmia sp!, {r0, r1, r2, r3, r4, lr} BX(ip) +#endif /* __FDPIC__ */ #else @ In the thumb case _dl_linux_resolver is thumb. If a bl is used @ from arm code the linker will insert a stub call which, with -- cgit v1.2.3