1 /* PLT trampoline. MIPS version.
2 Copyright (C) 1996-2018 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Kazumoto Kojima <kkojima@info.kanagawa-u.ac.jp>.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library. If not, see
18 <http://www.gnu.org/licenses/>. */
20 /* FIXME: Profiling of shared libraries is not implemented yet. */
26 #include <dl-machine.h>
27 #include <sysdep-cancel.h>
29 /* Get link map for callers object containing STUB_PC. */
30 static inline struct link_map
*
31 elf_machine_runtime_link_map (ElfW(Addr
) gpreg
, ElfW(Addr
) stub_pc
)
33 extern int _dl_mips_gnu_objects
;
35 /* got[1] is reserved to keep its link map address for the shared
36 object generated by the gnu linker. If all are such objects, we
37 can find the link map from current GPREG simply. If not so, get
38 the link map for caller's object containing STUB_PC. */
40 if (_dl_mips_gnu_objects
)
42 ElfW(Addr
) *got
= elf_mips_got_from_gpreg (gpreg
);
45 g1
= ((ElfW(Word
) *) got
)[1];
47 if ((g1
& ELF_MIPS_GNU_GOT1_MASK
) != 0)
50 (struct link_map
*) (g1
& ~ELF_MIPS_GNU_GOT1_MASK
);
51 ElfW(Addr
) base
, limit
;
52 const ElfW(Phdr
) *p
= l
->l_phdr
;
53 ElfW(Half
) this, nent
= l
->l_phnum
;
55 /* For the common case of a stub being called from the containing
56 object, STUB_PC will point to somewhere within the object that
57 is described by the link map fetched via got[1]. Otherwise we
58 have to scan all maps. */
59 for (this = 0; this < nent
; this++)
61 if (p
[this].p_type
== PT_LOAD
)
63 base
= p
[this].p_vaddr
+ l
->l_addr
;
64 limit
= base
+ p
[this].p_memsz
;
65 if (stub_pc
>= base
&& stub_pc
< limit
)
75 for (nsid
= 0; nsid
< DL_NNS
; ++nsid
)
76 for (l
= GL(dl_ns
)[nsid
]._ns_loaded
; l
!= NULL
; l
= l
->l_next
)
78 ElfW(Addr
) base
, limit
;
79 const ElfW(Phdr
) *p
= l
->l_phdr
;
80 ElfW(Half
) this, nent
= l
->l_phnum
;
82 for (this = 0; this < nent
; ++this)
84 if (p
[this].p_type
== PT_LOAD
)
86 base
= p
[this].p_vaddr
+ l
->l_addr
;
87 limit
= base
+ p
[this].p_memsz
;
88 if (stub_pc
>= base
&& stub_pc
< limit
)
94 _dl_signal_error (0, NULL
, NULL
, "cannot find runtime link map");
98 /* Define mips specific runtime resolver. The function __dl_runtime_resolve
99 is called from assembler function _dl_runtime_resolve which converts
100 special argument registers t7 ($15) and t8 ($24):
101 t7 address to return to the caller of the function
102 t8 index for this function symbol in .dynsym
103 to usual c arguments.
105 Other architectures call fixup from dl-runtime.c in
106 _dl_runtime_resolve. MIPS instead calls __dl_runtime_resolve. We
107 have to use our own version because of the way the got section is
108 treated on MIPS (we've also got ELF_MACHINE_PLT defined). */
110 /* The flag _dl_mips_gnu_objects is set if all dynamic objects are
111 generated by the gnu linker. */
112 int _dl_mips_gnu_objects
= 1;
114 /* This is called from assembly stubs below which the compiler can't see. */
116 __dl_runtime_resolve (ElfW(Word
), ElfW(Word
), ElfW(Addr
), ElfW(Addr
))
120 __dl_runtime_resolve (ElfW(Word
) sym_index
,
121 ElfW(Word
) return_address
,
122 ElfW(Addr
) old_gpreg
,
125 struct link_map
*l
= elf_machine_runtime_link_map (old_gpreg
, stub_pc
);
126 const ElfW(Sym
) *const symtab
127 = (const ElfW(Sym
) *) D_PTR (l
, l_info
[DT_SYMTAB
]);
128 const char *strtab
= (const void *) D_PTR (l
, l_info
[DT_STRTAB
]);
130 = (ElfW(Addr
) *) D_PTR (l
, l_info
[DT_PLTGOT
]);
131 const ElfW(Word
) local_gotno
132 = (const ElfW(Word
)) l
->l_info
[DT_MIPS (LOCAL_GOTNO
)]->d_un
.d_val
;
133 const ElfW(Word
) gotsym
134 = (const ElfW(Word
)) l
->l_info
[DT_MIPS (GOTSYM
)]->d_un
.d_val
;
135 const ElfW(Sym
) *sym
= &symtab
[sym_index
];
136 struct link_map
*sym_map
;
139 /* FIXME: The symbol versioning stuff is not tested yet. */
140 if (__builtin_expect (ELFW(ST_VISIBILITY
) (sym
->st_other
), 0) == 0)
142 switch (l
->l_info
[VERSYMIDX (DT_VERSYM
)] != NULL
? 1 : 0)
146 const ElfW(Half
) *vernum
=
147 (const void *) D_PTR (l
, l_info
[VERSYMIDX (DT_VERSYM
)]);
148 ElfW(Half
) ndx
= vernum
[sym_index
] & 0x7fff;
149 const struct r_found_version
*version
= &l
->l_versions
[ndx
];
151 if (version
->hash
!= 0)
153 /* We need to keep the scope around so do some locking. This is
154 not necessary for objects which cannot be unloaded or when
155 we are not using any threads (yet). */
156 if (!RTLD_SINGLE_THREAD_P
)
157 THREAD_GSCOPE_SET_FLAG ();
159 sym_map
= _dl_lookup_symbol_x (strtab
+ sym
->st_name
, l
,
160 &sym
, l
->l_scope
, version
,
161 ELF_RTYPE_CLASS_PLT
, 0, 0);
163 /* We are done with the global scope. */
164 if (!RTLD_SINGLE_THREAD_P
)
165 THREAD_GSCOPE_RESET_FLAG ();
173 /* We need to keep the scope around so do some locking. This is
174 not necessary for objects which cannot be unloaded or when
175 we are not using any threads (yet). */
176 int flags
= DL_LOOKUP_ADD_DEPENDENCY
;
177 if (!RTLD_SINGLE_THREAD_P
)
179 THREAD_GSCOPE_SET_FLAG ();
180 flags
|= DL_LOOKUP_GSCOPE_LOCK
;
183 sym_map
= _dl_lookup_symbol_x (strtab
+ sym
->st_name
, l
, &sym
,
184 l
->l_scope
, 0, ELF_RTYPE_CLASS_PLT
,
187 /* We are done with the global scope. */
188 if (!RTLD_SINGLE_THREAD_P
)
189 THREAD_GSCOPE_RESET_FLAG ();
193 /* Currently value contains the base load address of the object
194 that defines sym. Now add in the symbol offset. */
195 value
= SYMBOL_ADDRESS (sym_map
, sym
, true);
198 /* We already found the symbol. The module (and therefore its load
199 address) is also known. */
200 value
= SYMBOL_ADDRESS (l
, sym
, true);
202 /* Apply the relocation with that value. */
203 *(got
+ local_gotno
+ sym_index
- gotsym
) = value
;
208 #if _MIPS_SIM == _ABIO32
209 #define ELF_DL_FRAME_SIZE 40
211 #define ELF_DL_SAVE_ARG_REGS "\
219 #define ELF_DL_RESTORE_ARG_REGS "\
227 /* The PLT resolver should also save and restore $2 and $3, which are used
228 as arguments to MIPS16 stub functions. */
229 #define ELF_DL_PLT_FRAME_SIZE 48
231 #define ELF_DL_PLT_SAVE_ARG_REGS \
232 ELF_DL_SAVE_ARG_REGS "\
237 #define ELF_DL_PLT_RESTORE_ARG_REGS \
238 ELF_DL_RESTORE_ARG_REGS "\
243 #define IFABIO32(X) X
246 #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
248 #define ELF_DL_FRAME_SIZE 80
250 #define ELF_DL_SAVE_ARG_REGS "\
262 #define ELF_DL_RESTORE_ARG_REGS "\
274 /* The PLT resolver should also save and restore $2 and $3, which are used
275 as arguments to MIPS16 stub functions. */
276 #define ELF_DL_PLT_FRAME_SIZE 96
278 #define ELF_DL_PLT_SAVE_ARG_REGS \
279 ELF_DL_SAVE_ARG_REGS "\
284 #define ELF_DL_PLT_RESTORE_ARG_REGS \
285 ELF_DL_RESTORE_ARG_REGS "\
291 #define IFNEWABI(X) X
300 .globl _dl_runtime_resolve\n\
301 .type _dl_runtime_resolve,@function\n\
302 .ent _dl_runtime_resolve\n\
303 _dl_runtime_resolve:\n\
304 .frame $29, " STRINGXP(ELF_DL_FRAME_SIZE
) ", $31\n\
308 # Save arguments and sp value in stack.\n\
309 " STRINGXP(PTR_SUBIU
) " $29, " STRINGXP(ELF_DL_FRAME_SIZE
) "\n\
310 # Modify t9 ($25) so as to point .cpload instruction.\n\
311 " IFABIO32(STRINGXP(PTR_ADDIU
) " $25, (2f-1b)\n") "\
313 2: " STRINGXP(SETUP_GP
) "\n\
314 " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve
)) "\n\
316 # Save slot call pc.\n\
318 " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
319 " ELF_DL_SAVE_ARG_REGS
"\
324 jal __dl_runtime_resolve\n\
325 " ELF_DL_RESTORE_ARG_REGS
"\
326 " STRINGXP(RESTORE_GP64
) "\n\
327 " STRINGXP(PTR_ADDIU
) " $29, " STRINGXP(ELF_DL_FRAME_SIZE
) "\n\
330 .end _dl_runtime_resolve\n\
334 /* Assembler veneer called from the PLT header code when using PLTs.
336 Code in each PLT entry and the PLT header fills in the arguments to
339 - $15 (o32 t7, n32/n64 t3) - caller's return address
340 - $24 (t8) - PLT entry index
341 - $25 (t9) - address of _dl_runtime_pltresolve
342 - o32 $28 (gp), n32/n64 $14 (t2) - address of .got.plt
344 Different registers are used for .got.plt because the ABI was
345 originally designed for o32, where gp was available (call
346 clobbered). On n32/n64 gp is call saved.
350 - $4 (a0) - link map address
351 - $5 (a1) - .rel.plt offset (== PLT entry index * 8) */
357 .globl _dl_runtime_pltresolve\n\
358 .type _dl_runtime_pltresolve,@function\n\
359 .ent _dl_runtime_pltresolve\n\
360 _dl_runtime_pltresolve:\n\
361 .frame $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE
) ", $31\n\
363 # Save arguments and sp value in stack.\n\
364 1: " STRINGXP(PTR_SUBIU
) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE
) "\n\
365 " IFABIO32(STRINGXP(PTR_L
) " $13, " STRINGXP(PTRSIZE
) "($28)") "\n\
366 " IFNEWABI(STRINGXP(PTR_L
) " $13, " STRINGXP(PTRSIZE
) "($14)") "\n\
367 # Modify t9 ($25) so as to point .cpload instruction.\n\
368 " IFABIO32(STRINGXP(PTR_ADDIU
) " $25, (2f-1b)\n") "\
370 2: " STRINGXP(SETUP_GP
) "\n\
371 " STRINGXV(SETUP_GP64 (0, _dl_runtime_pltresolve
)) "\n\
373 " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
374 " ELF_DL_PLT_SAVE_ARG_REGS
"\
376 sll $5, $24, " STRINGXP(PTRLOG
) " + 1\n\
379 " ELF_DL_PLT_RESTORE_ARG_REGS
"\
380 " STRINGXP(RESTORE_GP64
) "\n\
381 " STRINGXP(PTR_ADDIU
) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE
) "\n\
383 .end _dl_runtime_pltresolve\n\
387 #elif _MIPS_SIM == _ABIO32 /* __mips16 */
388 /* MIPS16 version, O32 only. */
393 .globl _dl_runtime_resolve\n\
394 .type _dl_runtime_resolve,@function\n\
395 .ent _dl_runtime_resolve\n\
396 _dl_runtime_resolve:\n\
397 .frame $29, " STRINGXP (ELF_DL_FRAME_SIZE
) ", $31\n\
398 # Save arguments and sp value in stack.\n\t"
399 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
400 "save " STRINGXP (ELF_DL_FRAME_SIZE
) ", $4-$7, $ra\n\t"
402 "addiu $sp, -" STRINGXP (ELF_DL_FRAME_SIZE
) "\n\
408 "# Preserve caller's $ra, for RESTORE instruction below.\n\
411 # Compute GP into $2.\n\
412 li $2, %hi(_gp_disp)\n\
413 addiu $3, $pc, %lo(_gp_disp)\n\
416 lw $3, %got(__dl_runtime_resolve)($2)\n\
418 addiu $3, %lo(__dl_runtime_resolve)\n\
423 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
424 "restore " STRINGXP(ELF_DL_FRAME_SIZE
) ", $4-$7, $ra\n\t"
426 "# Restore $ra, move placed further down to hide latency.\n\
433 addiu $sp, " STRINGXP(ELF_DL_FRAME_SIZE
) "\n\t"
437 .end _dl_runtime_resolve\n\
445 .globl _dl_runtime_pltresolve\n\
446 .type _dl_runtime_pltresolve,@function\n\
447 .ent _dl_runtime_pltresolve\n\
448 _dl_runtime_pltresolve:\n\
449 .frame $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE
) ", $31\n\
450 # Save arguments and sp value in stack.\n\t"
451 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
452 "save " STRINGXP(ELF_DL_PLT_FRAME_SIZE
) ", $4-$7, $ra\n\t"
454 "addiu $sp, -" STRINGXP(ELF_DL_PLT_FRAME_SIZE
) "\n\
460 "# Preserve MIPS16 stub function arguments.\n\
463 # Preserve caller's $ra, for RESTORE instruction below.\n\
466 # Compute GP into $2.\n\
467 li $2, %hi(_gp_disp)\n\
468 addiu $3, $pc, %lo(_gp_disp)\n\
471 # Save GP value in slot.\n\
473 # Load _dl_fixup address.\n\
474 lw $6, %call16(_dl_fixup)($2)\n\
475 # Load link map address.\n\
477 lw $4, " STRINGXP (PTRSIZE
) "($3)\n\
479 sll $5, " STRINGXP (PTRLOG
) " + 1\n\
484 # Reload GP value into $28.\n\
490 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
491 "restore " STRINGXP (ELF_DL_PLT_FRAME_SIZE
) ", $4-$7, $ra\n\t"
493 "# Restore $ra, move placed further down to hide latency.\n\
500 addiu $sp, " STRINGXP (ELF_DL_PLT_FRAME_SIZE
) "\n\t"
506 .end _dl_runtime_pltresolve\n\
510 #else /* __mips16 && _MIPS_SIM != _ABIO32 */
511 # error "MIPS16 support for N32/N64 not implemented"
513 #endif /* __mips16 */