diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-12-13 14:49:22 +0530 |
---|---|---|
committer | Bernhard Reutner-Fischer <rep.dot.nop@gmail.com> | 2013-12-20 14:38:23 +0100 |
commit | 706d131f720b2a0d674a7efa88be80488cb0c66f (patch) | |
tree | 6a552e50efbd2a62a8c047babd4ac19e06547165 /ldso | |
parent | 49904049066daf66e2937fcde860addc6ba23929 (diff) |
ldso: Add ARC support
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Diffstat (limited to 'ldso')
-rw-r--r-- | ldso/ldso/arc/dl-debug.h | 68 | ||||
-rw-r--r-- | ldso/ldso/arc/dl-startup.h | 89 | ||||
-rw-r--r-- | ldso/ldso/arc/dl-syscalls.h | 5 | ||||
-rw-r--r-- | ldso/ldso/arc/dl-sysdep.h | 150 | ||||
-rw-r--r-- | ldso/ldso/arc/elfinterp.c | 279 | ||||
-rw-r--r-- | ldso/ldso/arc/resolve.S | 57 |
6 files changed, 648 insertions, 0 deletions
diff --git a/ldso/ldso/arc/dl-debug.h b/ldso/ldso/arc/dl-debug.h new file mode 100644 index 000000000..ff559f27f --- /dev/null +++ b/ldso/ldso/arc/dl-debug.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ +static const char *_dl_reltypes_tab[] = +{ + "R_ARC_NONE", /* 0 */ + "R_ARC_8", + "R_ARC_16", + "R_ARC_24", + "R_ARC_32", + "R_ARC_B26", /* 5 */ + "R_ARC_B22_PCREL", + "R_ARC_H30", + "R_ARC_N8", + "R_ARC_N16", + "R_ARC_N24", /* 10 */ + "R_ARC_N32", + "R_ARC_SDA", + "R_ARC_SECTOFF", + "R_ARC_S21H_PCREL", + "R_ARC_S21W_PCREL", /* 15 */ + "R_ARC_S25H_PCREL", + "R_ARC_S25W_PCREL", + "R_ARC_SDA32", + "R_ARC_SDA_LDST", + "R_ARC_SDA_LDST1", /* 20 */ + "R_ARC_SDA_LDST2", + "R_ARC_SDA16_LD", + "R_ARC_SDA16_LD1", + "R_ARC_SDA16_LD2", + "R_ARC_S13_PCREL", /* 25 */ + "R_ARC_W", + "R_ARC_32_ME", + "R_ARC_N32_ME", + "R_ARC_SECTOFF_ME", + "R_ARC_SDA32_ME", /* 30 */ + "R_ARC_W_ME", + "R_ARC_H30_ME", + "R_ARC_SECTOFF_U8", + "R_ARC_SECTOFF_S9", + "R_AC_SECTOFF_U8", /* 35 */ + "R_AC_SECTOFF_U8_1", + "R_AC_SECTOFF_U8_2", + "R_AC_SECTOFF_S9", + "R_AC_SECTOFF_S9_1", + "R_AC_SECTOFF_S9_2", /* 40 */ + "R_ARC_SECTOFF_ME_1", + "R_ARC_SECTOFF_ME_2", + "R_ARC_SECTOFF_1", + "R_ARC_SECTOFF_2", + "", /* 45 */ + "", + "", + "", + "", + "R_ARC_PC32", /* 50 */ + "R_ARC_GOTPC32", + "R_ARC_PLT32", + "R_ARC_COPY", + "R_ARC_GLOB_DAT", + "R_ARC_JMP_SLOT", /* 55 */ + "R_ARC_RELATIVE", + "R_ARC_GOTOFF", + "R_ARC_GOTPC", + "R_ARC_GOT32", +}; diff --git a/ldso/ldso/arc/dl-startup.h b/ldso/ldso/arc/dl-startup.h new file mode 100644 index 000000000..4a8d5d786 --- /dev/null +++ b/ldso/ldso/arc/dl-startup.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ + +/* + * vineetg: Refactoring/cleanup of loader entry point + * Removed 6 useless insns + * Joern Improved it even further: + * -better insn scheduling + * -no need for conditional code for _dl_skip_args + * -use of assembler .&2 expressions vs. @gotpc refs (avoids need for GP) + * + * What this code does: + * -ldso starts execution here when kernel returns from execve() + * -calls into generic ldso entry point _dl_start( ) + * -optionally adjusts argc for executable if exec passed as cmd + * -calls into app main with address of finaliser + */ +__asm__( + ".section .text \n" + ".align 4 \n" + ".global _start \n" + ".hidden _start \n" + ".type _start,@function \n" + + "_start: \n" + " ; ldso entry point, returns app entry point \n" + " bl.d _dl_start \n" + " mov_s r0, sp ; pass ptr to aux vector tbl \n" + + " ; If ldso ran as cmd with executable file nm as arg \n" + " ; as arg, skip the extra args calc by dl_start() \n" + " ld_s r1, [sp] ; orig argc from aux-vec Tbl \n" +#ifdef STAR_9000535888_FIXED + " ld r12, [pcl, _dl_skip_args-.+(.&2)] \n" +#else + " add r12, pcl, _dl_skip_args-.+(.&2) \n" + " ld r12, [r12] \n" +#endif + + " add r2, pcl, _dl_fini-.+(.&2) ; finalizer \n" + + " add2 sp, sp, r12 ; discard argv entries from stack\n" + " sub_s r1, r1, r12 ; adjusted argc, on stack \n" + " st_s r1, [sp] \n" + + " j_s.d [r0] ; app entry point \n" + " mov_s r0, r2 ; ptr to finalizer _dl_fini \n" + + ".size _start,.-_start \n" + ".previous \n" +); + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. + */ +#define GET_ARGV(ARGVP, ARGS) ARGVP = ((unsigned long*) ARGS + 1) + +/* + * Dynamic loader bootstrapping: + * Since we don't modify text at runtime, these can only be data relos + * (so safe to assume that they are word aligned). + * And also they HAVE to be RELATIVE relos only + * @RELP is the relo entry being processed + * @REL is the pointer to the address we are relocating. + * @SYMBOL is the symbol involved in the relocation + * @LOAD is the load address. + */ + +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD,SYMTAB) \ +do { \ + int type = ELF32_R_TYPE((RELP)->r_info); \ + if (likely(type == R_ARC_RELATIVE)) \ + *REL += (unsigned long) LOAD; \ + else \ + _dl_exit(1); \ +}while(0) + +/* + * This will go away once we have DT_RELACOUNT + */ +#define ARCH_NEEDS_BOOTSTRAP_RELOCS + +/* we dont need to spit out argc, argv etc for debugging */ +#define NO_EARLY_SEND_STDERR 1 diff --git a/ldso/ldso/arc/dl-syscalls.h b/ldso/ldso/arc/dl-syscalls.h new file mode 100644 index 000000000..a0b5afc9e --- /dev/null +++ b/ldso/ldso/arc/dl-syscalls.h @@ -0,0 +1,5 @@ +/* + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ diff --git a/ldso/ldso/arc/dl-sysdep.h b/ldso/ldso/arc/dl-sysdep.h new file mode 100644 index 000000000..dfde33233 --- /dev/null +++ b/ldso/ldso/arc/dl-sysdep.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ + +#include "elf.h" + +/* + * Define this if the system uses RELOCA. + */ +#define ELF_USES_RELOCA + +/* + * Dynamic Linking ABI for ARCompact ISA + * + * PLT + * -------------------------------- + * | ld r11, [pcl, off-to-GOT[1] | 0 (20 bytes) + * | | 4 + * plt0 | ld r10, [pcl, off-to-GOT[2] | 8 + * | | 12 + * | j [r10] | 16 + * -------------------------------- + * | Base address of GOT | 20 + * -------------------------------- + * | ld r12, [pcl, off-to-GOT[3] | 24 (12 bytes each) + * plt1 | | + * | j_s.d [r12] | 32 + * | mov_s r12, pcl | 34 + * -------------------------------- + * | | 36 + * ~ ~ + * ~ ~ + * | | + * -------------------------------- + * + * GOT + * -------------- + * | [0] | + * -------------- + * | [1] | Module info - setup by ldso + * -------------- + * | [2] | resolver entry point + * -------------- + * | [3] | + * | ... | Runtime address for function symbols + * | [f] | + * -------------- + * | [f+1] | + * | ... | Runtime address for data symbols + * | [last] | + * -------------- + */ + +/* + * Initialization sequence for a GOT. + * Caller elf_resolve() seeds @GOT_BASE from DT_PLTGOT - which essentially is + * pointer to first PLT entry. The actual GOT base is 5th word in PLT + * + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +do { \ + unsigned long *__plt_base = (unsigned long *)GOT_BASE; \ + GOT_BASE = (unsigned long *)(__plt_base[5] + \ + (unsigned long)MODULE->loadaddr); \ + GOT_BASE[1] = (unsigned long) MODULE; \ + GOT_BASE[2] = (unsigned long) _dl_linux_resolve; \ +} while(0) + +/* Here we define the magic numbers that this dynamic loader should accept */ +#define MAGIC1 EM_ARCOMPACT +#undef MAGIC2 + +/* Used for error messages */ +#define ELF_TARGET "ARC" + +struct elf_resolve; +extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, + unsigned int plt_pc); + +extern unsigned __udivmodsi4(unsigned, unsigned) attribute_hidden; + +#define do_rem(result, n, base) ((result) = \ + \ + __builtin_constant_p (base) ? (n) % (unsigned) (base) : \ + __extension__ ({ \ + register unsigned r1 __asm__ ("r1") = (base); \ + \ + __asm("bl.d @__udivmodsi4` mov r0,%1" \ + : "=r" (r1) \ + : "r" (n), "r" (r1) \ + : "r0", "r2", "r3", "r4", "lp_count", "blink", "cc"); \ + \ + r1; \ + }) \ +) + +/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so + PLT entries should not be allowed to define the value. + ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one + of the main executable's symbols, as for a COPY reloc. */ +#define elf_machine_type_class(type) \ + ((((type) == R_ARC_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_ARC_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* + * Get the runtime address of GOT[0] + */ +static __always_inline Elf32_Addr elf_machine_dynamic(void) +{ + Elf32_Addr dyn; + + __asm__("ld %0,[pcl,_DYNAMIC@gotpc]\n\t" : "=r" (dyn)); + return dyn; + +/* + * Another way would have been to simply return GP, which due to some + * PIC reference would be automatically setup by gcc in caller + * register Elf32_Addr *got __asm__ ("gp"); return *got; + */ +} + +/* Return the run-time load address of the shared object. */ +static __always_inline Elf32_Addr elf_machine_load_address(void) +{ + /* To find the loadaddr we subtract the runtime addr of any symbol + * say _dl_start from it's build-time addr. + */ + Elf32_Addr addr, tmp; + __asm__ ( + "ld %1, [pcl, _dl_start@gotpc] ;build addr of _dl_start \n" + "add %0, pcl, _dl_start-.+(.&2) ;runtime addr of _dl_start \n" + "sub %0, %0, %1 ;delta \n" + : "=&r" (addr), "=r"(tmp) + ); + return addr; +} + +static __always_inline void +elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + Elf32_Rel * rpnt = (void *) rel_addr; + --rpnt; + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + *reloc_addr += load_off; + } while (--relative_count); +} 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); +} diff --git a/ldso/ldso/arc/resolve.S b/ldso/ldso/arc/resolve.S new file mode 100644 index 000000000..891f66b97 --- /dev/null +++ b/ldso/ldso/arc/resolve.S @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ + +#include <sysdep.h> +#include <sys/syscall.h> + +; Save the registers which resolver could possibly clobber +; r0-r9: args to the function - symbol being resolved +; r10-r12 are already clobbered by PLTn, PLT0 thus neednot be saved + +.macro SAVE_CALLER_SAVED + push_s r0 + push_s r1 + push_s r2 + push_s r3 + st.a r4, [sp, -4] + st.a r5, [sp, -4] + st.a r6, [sp, -4] + st.a r7, [sp, -4] + st.a r8, [sp, -4] + st.a r9, [sp, -4] + push_s blink +.endm + +.macro RESTORE_CALLER_SAVED_BUT_R0 + ld.ab blink,[sp, 4] + ld.ab r9, [sp, 4] + ld.ab r8, [sp, 4] + ld.ab r7, [sp, 4] + ld.ab r6, [sp, 4] + ld.ab r5, [sp, 4] + ld.ab r4, [sp, 4] + pop_s r3 + pop_s r2 + pop_s r1 +.endm + +; Upon entry, PLTn, which led us here, sets up the following regs +; r11 = Module info (tpnt pointer as expected by resolver) +; r12 = PC of the PLTn itself - needed by resolver to find +; corresponding .rela.plt entry + +ENTRY(_dl_linux_resolve) + ; args to func being resolved, which resolver might clobber + SAVE_CALLER_SAVED + + mov_s r1, r12 + bl.d _dl_linux_resolver + mov r0, r11 + + RESTORE_CALLER_SAVED_BUT_R0 + j_s.d [r0] ; r0 has resolved function addr + pop_s r0 ; restore first arg to resolved call +END(_dl_linux_resolve) |