diff options
author | Andreas Jaeger <aj@suse.de> | 2005-03-28 07:49:19 +0000 |
---|---|---|
committer | Andreas Jaeger <aj@suse.de> | 2005-03-28 07:49:19 +0000 |
commit | abc85e9fea35a8e2eedfc9796c8d81ae5364bcf7 (patch) | |
tree | 72402c0e8ba665627669885b93db713c5d9b969a /sysdeps/mips | |
parent | 12403b32054ad3f46390f7e537f24ccf7b153620 (diff) | |
download | glibc-abc85e9fea35a8e2eedfc9796c8d81ae5364bcf7.tar.gz glibc-abc85e9fea35a8e2eedfc9796c8d81ae5364bcf7.tar.xz glibc-abc85e9fea35a8e2eedfc9796c8d81ae5364bcf7.zip |
[BZ #783]
2005-03-28 Daniel Jacobowitz <dan@codesourcery.com> [BZ #783] * elf/tst-auditmod1.c: Add MIPS support. * sysdeps/generic/ldsodefs.h (La_mips_32_regs): New. (La_mips_32_retval): New. (La_mips_64_regs): New. (La_mips_64_retval): New. (struct audit_ifaces): Add MIPS entries. * sysdeps/mips/dl-machine.h: Check RESOLVE_MAP instead of RESOLVE. (elf_machine_runtime_link_map, ELF_DL_FRAME_SIZE, ELF_DL_SAVE_ARG_REGS, ELF_DL_RESTORE_ARG_REGS, ELF_MACHINE_RUNTIME_TRAMPOLINE): Move to dl-trampoline.c. (RTLD_START): Align the stack before calling _dl_init_internal. Use .ent for _dl_start_user. (ARCH_LA_PLTENTER, ARCH_LA_PLTEXIT): Define. (elf_machine_rel, elf_machine_rel_relative, elf_machine_lazy_rel) (elf_machine_runtime_setup): Use "auto inline". (elf_machine_rela, elf_machine_rela_relative): Provide empty versions. (elf_machine_got_rel): Likewise. Use RESOLVE_MAP. * sysdeps/mips/dl-trampoline.c: New file. * sysdeps/mips/bits/link.h: New file. * sysdeps/unix/sysv/linux/mips/mips32/sysdep.h (internal_syscall5): Use register operands instead of non-lvalue memory operands. (internal_syscall6): Likewise. (internal_syscall7): Likewise. 2005-03-28 Daniel Jacobowitz <dan@codesourcery.com> [BZ #783] * elf/tst-auditmod1.c: Add MIPS support. * sysdeps/generic/ldsodefs.h (La_mips_32_regs): New. (La_mips_32_retval): New. (La_mips_64_regs): New. (La_mips_64_retval): New. (struct audit_ifaces): Add MIPS entries. * sysdeps/mips/dl-machine.h: Check RESOLVE_MAP instead of RESOLVE. (elf_machine_runtime_link_map, ELF_DL_FRAME_SIZE, ELF_DL_SAVE_ARG_REGS, ELF_DL_RESTORE_ARG_REGS, ELF_MACHINE_RUNTIME_TRAMPOLINE): Move to dl-trampoline.c. (RTLD_START): Align the stack before calling _dl_init_internal. Use .ent for _dl_start_user. (ARCH_LA_PLTENTER, ARCH_LA_PLTEXIT): Define. (elf_machine_rel, elf_machine_rel_relative, elf_machine_lazy_rel) (elf_machine_runtime_setup): Use "auto inline". (elf_machine_rela, elf_machine_rela_relative): Provide empty versions. (elf_machine_got_rel): Likewise. Use RESOLVE_MAP. * sysdeps/mips/dl-trampoline.c: New file. * sysdeps/mips/bits/link.h: New file. * sysdeps/unix/sysv/linux/mips/mips32/sysdep.h (internal_syscall5): Use register operands instead of non-lvalue memory operands. (internal_syscall6): Likewise. (internal_syscall7): Likewise.
Diffstat (limited to 'sysdeps/mips')
-rw-r--r-- | sysdeps/mips/bits/link.h | 118 | ||||
-rw-r--r-- | sysdeps/mips/dl-machine.h | 319 | ||||
-rw-r--r-- | sysdeps/mips/dl-trampoline.c | 272 |
3 files changed, 445 insertions, 264 deletions
diff --git a/sysdeps/mips/bits/link.h b/sysdeps/mips/bits/link.h new file mode 100644 index 0000000000..3d77a4c4a5 --- /dev/null +++ b/sysdeps/mips/bits/link.h @@ -0,0 +1,118 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _LINK_H +# error "Never include <bits/link.h> directly; use <link.h> instead." +#endif + +#include <sgidefs.h> + +#if _MIPS_SIM == _ABIO32 + +/* Registers for entry into PLT on MIPS. */ +typedef struct La_mips_32_regs +{ + uint32_t lr_reg[4]; /* $a0 through $a3 */ + double lr_fpreg[2]; /* $f12 and $f14 */ + uint32_t lr_ra; + uint32_t lr_sp; +} La_mips_32_regs; + +/* Return values for calls from PLT on MIPS. */ +typedef struct La_mips_32_retval +{ + uint32_t lrv_v0; + uint32_t lrv_v1; + double lrv_f0; + double lrv_f2; +} La_mips_32_retval; + +#else + +typedef struct La_mips_64_regs +{ + uint64_t lr_reg[8]; /* $a0 through $a7 */ + double lr_fpreg[8]; /* $f12 throgh $f19 */ + uint64_t lr_ra; + uint64_t lr_sp; +} La_mips_64_regs; + +/* Return values for calls from PLT on MIPS. */ +typedef struct La_mips_64_retval +{ + uint64_t lrv_v0; + uint64_t lrv_v1; + double lrv_f0; + double lrv_f2; +} La_mips_64_retval; + +#endif + +__BEGIN_DECLS + +#if _MIPS_SIM == _ABIO32 + +extern Elf32_Addr la_mips_o32_gnu_pltenter (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + La_mips_32_regs *__regs, + unsigned int *__flags, + const char *__symname, + long int *__framesizep); +extern unsigned int la_mips_o32_gnu_pltexit (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + const La_mips_32_regs *__inregs, + La_mips_32_retval *__outregs, + const char *symname); + +#elif _MIPS_SIM == _ABIN32 + +extern Elf32_Addr la_mips_n32_gnu_pltenter (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + La_mips_64_regs *__regs, + unsigned int *__flags, + const char *__symname, + long int *__framesizep); +extern unsigned int la_mips_n32_gnu_pltexit (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + const La_mips_64_regs *__inregs, + La_mips_64_retval *__outregs, + const char *symname); + +#else + +extern Elf64_Addr la_mips_n64_gnu_pltenter (Elf64_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + La_mips_64_regs *__regs, + unsigned int *__flags, + const char *__symname, + long int *__framesizep); +extern unsigned int la_mips_n64_gnu_pltexit (Elf64_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + const La_mips_64_regs *__inregs, + La_mips_64_retval *__outregs, + const char *symname); + +#endif + +__END_DECLS diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h index 0d87b65691..a8a41fff02 100644 --- a/sysdeps/mips/dl-machine.h +++ b/sysdeps/mips/dl-machine.h @@ -188,248 +188,6 @@ do { \ } while(0) -/* Get link map for callers object containing STUB_PC. */ -static inline struct link_map * -elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc) -{ - extern int _dl_mips_gnu_objects; - - /* got[1] is reserved to keep its link map address for the shared - object generated by the gnu linker. If all are such objects, we - can find the link map from current GPREG simply. If not so, get - the link map for caller's object containing STUB_PC. */ - - if (_dl_mips_gnu_objects) - { - ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg); - ElfW(Word) g1; - - g1 = ((ElfW(Word) *) got)[1]; - - if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0) - { - struct link_map *l = - (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK); - ElfW(Addr) base, limit; - const ElfW(Phdr) *p = l->l_phdr; - ElfW(Half) this, nent = l->l_phnum; - - /* For the common case of a stub being called from the containing - object, STUB_PC will point to somewhere within the object that - is described by the link map fetched via got[1]. Otherwise we - have to scan all maps. */ - for (this = 0; this < nent; this++) - { - if (p[this].p_type == PT_LOAD) - { - base = p[this].p_vaddr + l->l_addr; - limit = base + p[this].p_memsz; - if (stub_pc >= base && stub_pc < limit) - return l; - } - } - } - } - - struct link_map *l; - Lmid_t nsid; - - for (nsid = 0; nsid < DL_NNS; ++nsid) - for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) - { - ElfW(Addr) base, limit; - const ElfW(Phdr) *p = l->l_phdr; - ElfW(Half) this, nent = l->l_phnum; - - for (this = 0; this < nent; ++this) - { - if (p[this].p_type == PT_LOAD) - { - base = p[this].p_vaddr + l->l_addr; - limit = base + p[this].p_memsz; - if (stub_pc >= base && stub_pc < limit) - return l; - } - } - } - - _dl_signal_error (0, NULL, NULL, "cannot find runtime link map"); - return NULL; -} - -#if _MIPS_SIM == _ABIO32 -#define ELF_DL_FRAME_SIZE 40 - -#define ELF_DL_SAVE_ARG_REGS "\ - sw $15, 36($29)\n \ - sw $4, 16($29)\n \ - sw $5, 20($29)\n \ - sw $6, 24($29)\n \ - sw $7, 28($29)\n \ -" - -#define ELF_DL_RESTORE_ARG_REGS "\ - lw $31, 36($29)\n \ - lw $4, 16($29)\n \ - lw $5, 20($29)\n \ - lw $6, 24($29)\n \ - lw $7, 28($29)\n \ -" - -#define IFABIO32(X) X - -#else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */ - -#define ELF_DL_FRAME_SIZE 80 - -#define ELF_DL_SAVE_ARG_REGS "\ - sd $15, 72($29)\n \ - sd $4, 8($29)\n \ - sd $5, 16($29)\n \ - sd $6, 24($29)\n \ - sd $7, 32($29)\n \ - sd $8, 40($29)\n \ - sd $9, 48($29)\n \ - sd $10, 56($29)\n \ - sd $11, 64($29)\n \ -" - -#define ELF_DL_RESTORE_ARG_REGS "\ - ld $31, 72($29)\n \ - ld $4, 8($29)\n \ - ld $5, 16($29)\n \ - ld $6, 24($29)\n \ - ld $7, 32($29)\n \ - ld $8, 40($29)\n \ - ld $9, 48($29)\n \ - ld $10, 56($29)\n \ - ld $11, 64($29)\n \ -" - -#define IFABIO32(X) - -#endif - -/* Define mips specific runtime resolver. The function __dl_runtime_resolve - is called from assembler function _dl_runtime_resolve which converts - special argument registers t7 ($15) and t8 ($24): - t7 address to return to the caller of the function - t8 index for this function symbol in .dynsym - to usual c arguments. - - Other architectures call fixup from dl-runtime.c in - _dl_runtime_resolve. MIPS instead calls __dl_runtime_resolve. We - have to use our own version because of the way the got section is - treated on MIPS (we've also got ELF_MACHINE_PLT defined). */ - -#define ELF_MACHINE_RUNTIME_TRAMPOLINE \ -/* The flag _dl_mips_gnu_objects is set if all dynamic objects are \ - generated by the gnu linker. */ \ -int _dl_mips_gnu_objects = 1; \ - \ -/* This is called from assembly stubs below which the compiler can't see. */ \ -static ElfW(Addr) \ -__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr)) \ - __attribute_used__; \ - \ -static ElfW(Addr) \ -__dl_runtime_resolve (ElfW(Word) sym_index, \ - ElfW(Word) return_address, \ - ElfW(Addr) old_gpreg, \ - ElfW(Addr) stub_pc) \ -{ \ - struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc); \ - const ElfW(Sym) *const symtab \ - = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]); \ - const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); \ - ElfW(Addr) *got \ - = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]); \ - const ElfW(Word) local_gotno \ - = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; \ - const ElfW(Word) gotsym \ - = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val; \ - const ElfW(Sym) *sym = &symtab[sym_index]; \ - ElfW(Addr) value; \ - \ - /* FIXME: The symbol versioning stuff is not tested yet. */ \ - if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) \ - { \ - switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) \ - { \ - default: \ - { \ - const ElfW(Half) *vernum = \ - (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); \ - ElfW(Half) ndx = vernum[sym_index] & 0x7fff; \ - const struct r_found_version *version = &l->l_versions[ndx]; \ - \ - if (version->hash != 0) \ - { \ - value = _dl_lookup_symbol_x (strtab + sym->st_name, l, \ - &sym, l->l_scope, version, \ - ELF_RTYPE_CLASS_PLT, 0, 0); \ - break; \ - } \ - /* Fall through. */ \ - } \ - case 0: \ - value = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, \ - l->l_scope, 0, ELF_RTYPE_CLASS_PLT, \ - DL_LOOKUP_ADD_DEPENDENCY, 0); \ - } \ - \ - /* Currently value contains the base load address of the object \ - that defines sym. Now add in the symbol offset. */ \ - value = (sym ? value + sym->st_value : 0); \ - } \ - else \ - /* We already found the symbol. The module (and therefore its load \ - address) is also known. */ \ - value = l->l_addr + sym->st_value; \ - \ - /* Apply the relocation with that value. */ \ - *(got + local_gotno + sym_index - gotsym) = value; \ - \ - return value; \ -} \ - \ -asm ("\n \ - .text\n \ - .align 2\n \ - .globl _dl_runtime_resolve\n \ - .type _dl_runtime_resolve,@function\n \ - .ent _dl_runtime_resolve\n \ -_dl_runtime_resolve:\n \ - .frame $29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n \ - .set noreorder\n \ - # Save GP.\n \ - move $3, $28\n \ - # Save arguments and sp value in stack.\n \ - " STRINGXP(PTR_SUBIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n \ - # Modify t9 ($25) so as to point .cpload instruction.\n \ - " IFABIO32(STRINGXP(PTR_ADDIU) " $25, 12\n") " \ - # Compute GP.\n \ - " STRINGXP(SETUP_GP) "\n \ - " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n \ - .set reorder\n \ - # Save slot call pc.\n \ - move $2, $31\n \ - " IFABIO32(STRINGXP(CPRESTORE(32))) "\n \ - " ELF_DL_SAVE_ARG_REGS " \ - move $4, $24\n \ - move $5, $15\n \ - move $6, $3\n \ - move $7, $2\n \ - jal __dl_runtime_resolve\n \ - " ELF_DL_RESTORE_ARG_REGS " \ - " STRINGXP(RESTORE_GP64) "\n \ - " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n \ - move $25, $2\n \ - jr $25\n \ - .end _dl_runtime_resolve\n \ - .previous\n \ -"); - /* Mask identifying addresses reserved for the user program, where the dynamic linker should not map anything. */ #define ELF_MACHINE_USER_ADDRESS_MASK 0x80000000UL @@ -451,8 +209,8 @@ _dl_runtime_resolve:\n \ and not just plain _start. */ #define RTLD_START asm (\ - ".text\n"\ - _RTLD_PROLOGUE(ENTRY_POINT) "\ + ".text\n\ + " _RTLD_PROLOGUE(ENTRY_POINT) "\ " STRINGXV(SETUP_GPX($25)) "\n\ " STRINGXV(SETUP_GPX64($18,$25)) "\n\ # i386 ABI book says that the first entry of GOT holds\n\ @@ -475,10 +233,10 @@ _dl_runtime_resolve:\n \ " STRINGXP(PTR_ADDIU) " $29, 16\n\ # Get the value of label '_dl_start_user' in t9 ($25).\n\ " STRINGXP(PTR_LA) " $25, _dl_start_user\n\ - .globl _dl_start_user\n\ - .type _dl_start_user,@function\n\ - .aent _dl_start_user\n\ -_dl_start_user:\n\ + " _RTLD_EPILOGUE(ENTRY_POINT) "\ + \n\ + \n\ + " _RTLD_PROLOGUE(_dl_start_user) "\ " STRINGXP(SETUP_GP) "\n\ " STRINGXV(SETUP_GP64($18,_dl_start_user)) "\n\ move $16, $28\n\ @@ -504,34 +262,48 @@ _dl_start_user:\n\ sll $7, $5, " STRINGXP (PTRLOG) "\n\ " STRINGXP(PTR_ADDU) " $7, $7, $6\n\ " STRINGXP(PTR_ADDU) " $7, $7, " STRINGXP (PTRSIZE) " \n\ - " STRINGXP(PTR_SUBIU) " $29, 32\n\ + # Make sure the stack pointer is aligned for _dl_init_internal.\n\ + and $2, $29, -2 * " STRINGXP(SZREG) "\n\ + " STRINGXP(PTR_S) " $29, -4($2)\n\ + " STRINGXP(PTR_SUBIU) " $29, $2, 32\n\ " STRINGXP(SAVE_GP(16)) "\n\ # Call the function to run the initializers.\n\ jal _dl_init_internal\n\ - " STRINGXP(PTR_ADDIU) " $29, 32\n\ + # Restore the stack pointer for _start.\n\ + " STRINGXP(PTR_L) " $29, 28($29)\n\ # Pass our finalizer function to the user in $2 as per ELF ABI.\n\ " STRINGXP(PTR_LA) " $2, _dl_fini\n\ # Jump to the user entry point.\n\ move $25, $17\n\ jr $25\n\t"\ - _RTLD_EPILOGUE(ENTRY_POINT)\ + _RTLD_EPILOGUE(_dl_start_user)\ ".previous"\ ); /* The MIPS never uses Elfxx_Rela relocations. */ #define ELF_MACHINE_NO_RELA 1 +/* Names of the architecture-specific auditing callback functions. */ +# if _MIPS_SIM == _ABIO32 +# define ARCH_LA_PLTENTER mips_o32_gnu_pltenter +# define ARCH_LA_PLTEXIT mips_o32_gnu_pltexit +# elif _MIPS_SIM == _ABIN32 +# define ARCH_LA_PLTENTER mips_n32_gnu_pltenter +# define ARCH_LA_PLTEXIT mips_n32_gnu_pltexit +# else +# define ARCH_LA_PLTENTER mips_n64_gnu_pltenter +# define ARCH_LA_PLTEXIT mips_n64_gnu_pltexit +# endif + #endif /* !dl_machine_h */ -#ifdef RESOLVE +#ifdef RESOLVE_MAP /* Perform the relocation specified by RELOC and SYM (which is fully resolved). MAP is the object containing the reloc. */ -static inline void -#ifdef RTLD_BOOTSTRAP - __attribute__ ((always_inline)) -#endif +auto inline void +__attribute__ ((always_inline)) elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc, const ElfW(Sym) *sym, const struct r_found_version *version, void *const reloc_addr) @@ -635,23 +407,41 @@ elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc, } } -static inline void +auto inline void +__attribute__((always_inline)) elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc, void *const reloc_addr) { /* XXX Nothing to do. There is no relative relocation, right? */ } -static inline void +auto inline void +__attribute__((always_inline)) elf_machine_lazy_rel (struct link_map *map, ElfW(Addr) l_addr, const ElfW(Rel) *reloc) { /* Do nothing. */ } +auto inline void +__attribute__ ((always_inline)) +elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc, + const ElfW(Sym) *sym, const struct r_found_version *version, + void *const reloc_addr) +{ +} + +auto inline void +__attribute__((always_inline)) +elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc, + void *const reloc_addr) +{ +} + #ifndef RTLD_BOOTSTRAP /* Relocate GOT. */ -static inline void +auto inline void +__attribute__((always_inline)) elf_machine_got_rel (struct link_map *map, int lazy) { ElfW(Addr) *got; @@ -664,9 +454,9 @@ elf_machine_got_rel (struct link_map *map, int lazy) const ElfW(Sym) *ref = sym; \ const struct r_found_version *version \ = vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL; \ - ElfW(Addr) value; \ - value = RESOLVE (&ref, version, R_MIPS_REL32); \ - (ref)? value + ref->st_value: 0; \ + struct link_map *sym_map; \ + sym_map = RESOLVE_MAP (&ref, version, R_MIPS_REL32); \ + ref ? sym_map->l_addr + ref->st_value : 0; \ }) if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL) @@ -738,7 +528,8 @@ elf_machine_got_rel (struct link_map *map, int lazy) /* Set up the loaded object described by L so its stub function will jump to the on-demand fixup code __dl_runtime_resolve. */ -static inline int +auto inline int +__attribute__((always_inline)) elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) { # ifndef RTLD_BOOTSTRAP @@ -774,4 +565,4 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) return lazy; } -#endif /* RESOLVE */ +#endif /* RESOLVE_MAP */ diff --git a/sysdeps/mips/dl-trampoline.c b/sysdeps/mips/dl-trampoline.c new file mode 100644 index 0000000000..c5ea59cdb2 --- /dev/null +++ b/sysdeps/mips/dl-trampoline.c @@ -0,0 +1,272 @@ +/* PLT trampoline. MIPS version. + Copyright (C) 1996-2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Kazumoto Kojima <kkojima@info.kanagawa-u.ac.jp>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* FIXME: Profiling of shared libraries is not implemented yet. */ + +#include <sysdep.h> +#include <link.h> +#include <elf.h> +#include <ldsodefs.h> +#include <dl-machine.h> + +/* Get link map for callers object containing STUB_PC. */ +static inline struct link_map * +elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc) +{ + extern int _dl_mips_gnu_objects; + + /* got[1] is reserved to keep its link map address for the shared + object generated by the gnu linker. If all are such objects, we + can find the link map from current GPREG simply. If not so, get + the link map for caller's object containing STUB_PC. */ + + if (_dl_mips_gnu_objects) + { + ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg); + ElfW(Word) g1; + + g1 = ((ElfW(Word) *) got)[1]; + + if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0) + { + struct link_map *l = + (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK); + ElfW(Addr) base, limit; + const ElfW(Phdr) *p = l->l_phdr; + ElfW(Half) this, nent = l->l_phnum; + + /* For the common case of a stub being called from the containing + object, STUB_PC will point to somewhere within the object that + is described by the link map fetched via got[1]. Otherwise we + have to scan all maps. */ + for (this = 0; this < nent; this++) + { + if (p[this].p_type == PT_LOAD) + { + base = p[this].p_vaddr + l->l_addr; + limit = base + p[this].p_memsz; + if (stub_pc >= base && stub_pc < limit) + return l; + } + } + } + } + + struct link_map *l; + Lmid_t nsid; + + for (nsid = 0; nsid < DL_NNS; ++nsid) + for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) + { + ElfW(Addr) base, limit; + const ElfW(Phdr) *p = l->l_phdr; + ElfW(Half) this, nent = l->l_phnum; + + for (this = 0; this < nent; ++this) + { + if (p[this].p_type == PT_LOAD) + { + base = p[this].p_vaddr + l->l_addr; + limit = base + p[this].p_memsz; + if (stub_pc >= base && stub_pc < limit) + return l; + } + } + } + + _dl_signal_error (0, NULL, NULL, "cannot find runtime link map"); + return NULL; +} + +/* Define mips specific runtime resolver. The function __dl_runtime_resolve + is called from assembler function _dl_runtime_resolve which converts + special argument registers t7 ($15) and t8 ($24): + t7 address to return to the caller of the function + t8 index for this function symbol in .dynsym + to usual c arguments. + + Other architectures call fixup from dl-runtime.c in + _dl_runtime_resolve. MIPS instead calls __dl_runtime_resolve. We + have to use our own version because of the way the got section is + treated on MIPS (we've also got ELF_MACHINE_PLT defined). */ + +/* The flag _dl_mips_gnu_objects is set if all dynamic objects are + generated by the gnu linker. */ +int _dl_mips_gnu_objects = 1; + +#define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym)) + +/* This is called from assembly stubs below which the compiler can't see. */ +static ElfW(Addr) +__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr)) + __attribute_used__; + +static ElfW(Addr) +__dl_runtime_resolve (ElfW(Word) sym_index, + ElfW(Word) return_address, + ElfW(Addr) old_gpreg, + ElfW(Addr) stub_pc) +{ + struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc); + const ElfW(Sym) *const symtab + = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]); + const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); + ElfW(Addr) *got + = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]); + const ElfW(Word) local_gotno + = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; + const ElfW(Word) gotsym + = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val; + const ElfW(Sym) *sym = &symtab[sym_index]; + struct link_map *sym_map; + ElfW(Addr) value; + + /* FIXME: The symbol versioning stuff is not tested yet. */ + if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) + { + switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) + { + default: + { + const ElfW(Half) *vernum = + (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); + ElfW(Half) ndx = vernum[sym_index] & 0x7fff; + const struct r_found_version *version = &l->l_versions[ndx]; + + if (version->hash != 0) + { + sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, + &sym, l->l_scope, version, + ELF_RTYPE_CLASS_PLT, 0, 0); + break; + } + /* Fall through. */ + } + case 0: + sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, + l->l_scope, 0, ELF_RTYPE_CLASS_PLT, + DL_LOOKUP_ADD_DEPENDENCY, 0); + } + + /* Currently value contains the base load address of the object + that defines sym. Now add in the symbol offset. */ + value = (sym ? sym_map->l_addr + sym->st_value : 0); + } + else + /* We already found the symbol. The module (and therefore its load + address) is also known. */ + value = l->l_addr + sym->st_value; + + /* Apply the relocation with that value. */ + *(got + local_gotno + sym_index - gotsym) = value; + + return value; +} + +#if _MIPS_SIM == _ABIO32 +#define ELF_DL_FRAME_SIZE 40 + +#define ELF_DL_SAVE_ARG_REGS "\ + sw $15, 36($29)\n \ + sw $4, 16($29)\n \ + sw $5, 20($29)\n \ + sw $6, 24($29)\n \ + sw $7, 28($29)\n \ +" + +#define ELF_DL_RESTORE_ARG_REGS "\ + lw $31, 36($29)\n \ + lw $4, 16($29)\n \ + lw $5, 20($29)\n \ + lw $6, 24($29)\n \ + lw $7, 28($29)\n \ +" + +#define IFABIO32(X) X + +#else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */ + +#define ELF_DL_FRAME_SIZE 80 + +#define ELF_DL_SAVE_ARG_REGS "\ + sd $15, 72($29)\n \ + sd $4, 8($29)\n \ + sd $5, 16($29)\n \ + sd $6, 24($29)\n \ + sd $7, 32($29)\n \ + sd $8, 40($29)\n \ + sd $9, 48($29)\n \ + sd $10, 56($29)\n \ + sd $11, 64($29)\n \ +" + +#define ELF_DL_RESTORE_ARG_REGS "\ + ld $31, 72($29)\n \ + ld $4, 8($29)\n \ + ld $5, 16($29)\n \ + ld $6, 24($29)\n \ + ld $7, 32($29)\n \ + ld $8, 40($29)\n \ + ld $9, 48($29)\n \ + ld $10, 56($29)\n \ + ld $11, 64($29)\n \ +" + +#define IFABIO32(X) + +#endif + +asm ("\n\ + .text\n\ + .align 2\n\ + .globl _dl_runtime_resolve\n\ + .type _dl_runtime_resolve,@function\n\ + .ent _dl_runtime_resolve\n\ +_dl_runtime_resolve:\n\ + .frame $29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n\ + .set noreorder\n\ + # Save GP.\n\ + move $3, $28\n\ + # Save arguments and sp value in stack.\n\ + " STRINGXP(PTR_SUBIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\ + # Modify t9 ($25) so as to point .cpload instruction.\n\ + " IFABIO32(STRINGXP(PTR_ADDIU) " $25, 12\n") "\ + # Compute GP.\n\ + " STRINGXP(SETUP_GP) "\n\ + " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\ + .set reorder\n\ + # Save slot call pc.\n\ + move $2, $31\n\ + " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\ + " ELF_DL_SAVE_ARG_REGS "\ + move $4, $24\n\ + move $5, $15\n\ + move $6, $3\n\ + move $7, $2\n\ + jal __dl_runtime_resolve\n\ + " ELF_DL_RESTORE_ARG_REGS "\ + " STRINGXP(RESTORE_GP64) "\n\ + " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\ + move $25, $2\n\ + jr $25\n\ + .end _dl_runtime_resolve\n\ + .previous\n\ +"); |