1/* PLT trampoline. MIPS version.
2 Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* FIXME: Profiling of shared libraries is not implemented yet. */
20
21#include <sysdep.h>
22#include <link.h>
23#include <elf.h>
24#include <ldsodefs.h>
25#include <dl-machine.h>
26#include <sysdep-cancel.h>
27
28/* Get link map for callers object containing STUB_PC. */
29static inline struct link_map *
30elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
31{
32 extern int _dl_mips_gnu_objects;
33
34 /* got[1] is reserved to keep its link map address for the shared
35 object generated by the gnu linker. If all are such objects, we
36 can find the link map from current GPREG simply. If not so, get
37 the link map for caller's object containing STUB_PC. */
38
39 if (_dl_mips_gnu_objects)
40 {
41 ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
42 ElfW(Word) g1;
43
44 g1 = ((ElfW(Word) *) got)[1];
45
46 if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
47 {
48 struct link_map *l =
49 (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK);
50 ElfW(Addr) base, limit;
51 const ElfW(Phdr) *p = l->l_phdr;
52 ElfW(Half) this, nent = l->l_phnum;
53
54 /* For the common case of a stub being called from the containing
55 object, STUB_PC will point to somewhere within the object that
56 is described by the link map fetched via got[1]. Otherwise we
57 have to scan all maps. */
58 for (this = 0; this < nent; this++)
59 {
60 if (p[this].p_type == PT_LOAD)
61 {
62 base = p[this].p_vaddr + l->l_addr;
63 limit = base + p[this].p_memsz;
64 if (stub_pc >= base && stub_pc < limit)
65 return l;
66 }
67 }
68 }
69 }
70
71 struct link_map *l;
72 Lmid_t nsid;
73
74 for (nsid = 0; nsid < DL_NNS; ++nsid)
75 for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
76 {
77 ElfW(Addr) base, limit;
78 const ElfW(Phdr) *p = l->l_phdr;
79 ElfW(Half) this, nent = l->l_phnum;
80
81 for (this = 0; this < nent; ++this)
82 {
83 if (p[this].p_type == PT_LOAD)
84 {
85 base = p[this].p_vaddr + l->l_addr;
86 limit = base + p[this].p_memsz;
87 if (stub_pc >= base && stub_pc < limit)
88 return l;
89 }
90 }
91 }
92
93 _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
94 return NULL;
95}
96
97/* Define mips specific runtime resolver. The function __dl_runtime_resolve
98 is called from assembler function _dl_runtime_resolve which converts
99 special argument registers t7 ($15) and t8 ($24):
100 t7 address to return to the caller of the function
101 t8 index for this function symbol in .dynsym
102 to usual c arguments.
103
104 Other architectures call fixup from dl-runtime.c in
105 _dl_runtime_resolve. MIPS instead calls __dl_runtime_resolve. We
106 have to use our own version because of the way the got section is
107 treated on MIPS (we've also got ELF_MACHINE_PLT defined). */
108
109/* The flag _dl_mips_gnu_objects is set if all dynamic objects are
110 generated by the gnu linker. */
111int _dl_mips_gnu_objects = 1;
112
113/* This is called from assembly stubs below which the compiler can't see. */
114static ElfW(Addr)
115__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))
116 __attribute_used__;
117
118static ElfW(Addr)
119__dl_runtime_resolve (ElfW(Word) sym_index,
120 ElfW(Word) return_address,
121 ElfW(Addr) old_gpreg,
122 ElfW(Addr) stub_pc)
123{
124 struct link_map *l = elf_machine_runtime_link_map (gpreg: old_gpreg, stub_pc);
125 const ElfW(Sym) *const symtab
126 = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]);
127 const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
128 ElfW(Addr) *got
129 = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
130 const ElfW(Word) local_gotno
131 = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
132 const ElfW(Word) gotsym
133 = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
134 const ElfW(Sym) *sym = &symtab[sym_index];
135 struct link_map *sym_map;
136 ElfW(Addr) value;
137
138 /* FIXME: The symbol versioning stuff is not tested yet. */
139 if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
140 {
141 switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL ? 1 : 0)
142 {
143 default:
144 {
145 const ElfW(Half) *vernum =
146 (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
147 ElfW(Half) ndx = vernum[sym_index] & 0x7fff;
148 const struct r_found_version *version = &l->l_versions[ndx];
149
150 if (version->hash != 0)
151 {
152 /* We need to keep the scope around so do some locking. This is
153 not necessary for objects which cannot be unloaded or when
154 we are not using any threads (yet). */
155 if (!RTLD_SINGLE_THREAD_P)
156 THREAD_GSCOPE_SET_FLAG ();
157
158 sym_map = _dl_lookup_symbol_x (undef: strtab + sym->st_name, undef_map: l,
159 sym: &sym, symbol_scope: l->l_scope, version,
160 ELF_RTYPE_CLASS_PLT, flags: 0, skip_map: 0);
161
162 /* We are done with the global scope. */
163 if (!RTLD_SINGLE_THREAD_P)
164 THREAD_GSCOPE_RESET_FLAG ();
165
166 break;
167 }
168 }
169 /* Fall through. */
170 case 0:
171 {
172 /* We need to keep the scope around so do some locking. This is
173 not necessary for objects which cannot be unloaded or when
174 we are not using any threads (yet). */
175 int flags = DL_LOOKUP_ADD_DEPENDENCY;
176 if (!RTLD_SINGLE_THREAD_P)
177 {
178 THREAD_GSCOPE_SET_FLAG ();
179 flags |= DL_LOOKUP_GSCOPE_LOCK;
180 }
181
182 sym_map = _dl_lookup_symbol_x (undef: strtab + sym->st_name, undef_map: l, sym: &sym,
183 symbol_scope: l->l_scope, version: 0, ELF_RTYPE_CLASS_PLT,
184 flags, skip_map: 0);
185
186 /* We are done with the global scope. */
187 if (!RTLD_SINGLE_THREAD_P)
188 THREAD_GSCOPE_RESET_FLAG ();
189 }
190 }
191
192 /* Currently value contains the base load address of the object
193 that defines sym. Now add in the symbol offset. */
194 value = SYMBOL_ADDRESS (sym_map, sym, true);
195 }
196 else
197 /* We already found the symbol. The module (and therefore its load
198 address) is also known. */
199 value = SYMBOL_ADDRESS (l, sym, true);
200
201 /* Apply the relocation with that value. */
202 *(got + local_gotno + sym_index - gotsym) = value;
203
204 return value;
205}
206
207#if _MIPS_SIM == _ABIO32
208#define ELF_DL_FRAME_SIZE 40
209
210#define ELF_DL_SAVE_ARG_REGS "\
211 sw $15, 36($29)\n \
212 sw $4, 16($29)\n \
213 sw $5, 20($29)\n \
214 sw $6, 24($29)\n \
215 sw $7, 28($29)\n \
216"
217
218#define ELF_DL_RESTORE_ARG_REGS "\
219 lw $31, 36($29)\n \
220 lw $4, 16($29)\n \
221 lw $5, 20($29)\n \
222 lw $6, 24($29)\n \
223 lw $7, 28($29)\n \
224"
225
226/* The PLT resolver should also save and restore $2 and $3, which are used
227 as arguments to MIPS16 stub functions. */
228#define ELF_DL_PLT_FRAME_SIZE 48
229
230#define ELF_DL_PLT_SAVE_ARG_REGS \
231 ELF_DL_SAVE_ARG_REGS "\
232 sw $2, 40($29)\n \
233 sw $3, 44($29)\n \
234"
235
236#define ELF_DL_PLT_RESTORE_ARG_REGS \
237 ELF_DL_RESTORE_ARG_REGS "\
238 lw $2, 40($29)\n \
239 lw $3, 44($29)\n \
240"
241
242#define IFABIO32(X) X
243#define IFNEWABI(X)
244
245#else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
246
247#define ELF_DL_FRAME_SIZE 80
248
249#define ELF_DL_SAVE_ARG_REGS "\
250 sd $15, 72($29)\n \
251 sd $4, 8($29)\n \
252 sd $5, 16($29)\n \
253 sd $6, 24($29)\n \
254 sd $7, 32($29)\n \
255 sd $8, 40($29)\n \
256 sd $9, 48($29)\n \
257 sd $10, 56($29)\n \
258 sd $11, 64($29)\n \
259"
260
261#define ELF_DL_RESTORE_ARG_REGS "\
262 ld $31, 72($29)\n \
263 ld $4, 8($29)\n \
264 ld $5, 16($29)\n \
265 ld $6, 24($29)\n \
266 ld $7, 32($29)\n \
267 ld $8, 40($29)\n \
268 ld $9, 48($29)\n \
269 ld $10, 56($29)\n \
270 ld $11, 64($29)\n \
271"
272
273/* The PLT resolver should also save and restore $2 and $3, which are used
274 as arguments to MIPS16 stub functions. */
275#define ELF_DL_PLT_FRAME_SIZE 96
276
277#define ELF_DL_PLT_SAVE_ARG_REGS \
278 ELF_DL_SAVE_ARG_REGS "\
279 sd $2, 80($29)\n \
280 sd $3, 88($29)\n \
281"
282
283#define ELF_DL_PLT_RESTORE_ARG_REGS \
284 ELF_DL_RESTORE_ARG_REGS "\
285 ld $2, 80($29)\n \
286 ld $3, 88($29)\n \
287"
288
289#define IFABIO32(X)
290#define IFNEWABI(X) X
291
292#endif
293
294#ifndef __mips16
295asm ("\n\
296 .text\n\
297 .align 2\n\
298 .set nomips16\n\
299 .globl _dl_runtime_resolve\n\
300 .type _dl_runtime_resolve,@function\n\
301 .ent _dl_runtime_resolve\n\
302_dl_runtime_resolve:\n\
303 .frame $29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n\
304 .set noreorder\n\
305 # Save GP.\n\
3061: move $3, $28\n\
307 # Save arguments and sp value in stack.\n\
308 " STRINGXP(PTR_SUBIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
309 # Modify t9 ($25) so as to point .cpload instruction.\n\
310 " IFABIO32(STRINGXP(PTR_ADDIU) " $25, (2f-1b)\n") "\
311 # Compute GP.\n\
3122: " STRINGXP(SETUP_GP) "\n\
313 " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\
314 .set reorder\n\
315 # Save slot call pc.\n\
316 move $2, $31\n\
317 " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
318 " ELF_DL_SAVE_ARG_REGS "\
319 move $4, $24\n\
320 move $5, $15\n\
321 move $6, $3\n\
322 move $7, $2\n\
323 jal __dl_runtime_resolve\n\
324 " ELF_DL_RESTORE_ARG_REGS "\
325 " STRINGXP(RESTORE_GP64) "\n\
326 " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
327 move $25, $2\n\
328 jr $25\n\
329 .end _dl_runtime_resolve\n\
330 .previous\n\
331");
332
333/* Assembler veneer called from the PLT header code when using PLTs.
334
335 Code in each PLT entry and the PLT header fills in the arguments to
336 this function:
337
338 - $15 (o32 t7, n32/n64 t3) - caller's return address
339 - $24 (t8) - PLT entry index
340 - $25 (t9) - address of _dl_runtime_pltresolve
341 - o32 $28 (gp), n32/n64 $14 (t2) - address of .got.plt
342
343 Different registers are used for .got.plt because the ABI was
344 originally designed for o32, where gp was available (call
345 clobbered). On n32/n64 gp is call saved.
346
347 _dl_fixup needs:
348
349 - $4 (a0) - link map address
350 - $5 (a1) - .rel.plt offset (== PLT entry index * 8) */
351
352asm ("\n\
353 .text\n\
354 .align 2\n\
355 .set nomips16\n\
356 .globl _dl_runtime_pltresolve\n\
357 .type _dl_runtime_pltresolve,@function\n\
358 .ent _dl_runtime_pltresolve\n\
359_dl_runtime_pltresolve:\n\
360 .frame $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
361 .set noreorder\n\
362 # Save arguments and sp value in stack.\n\
3631: " STRINGXP(PTR_SUBIU) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
364 " IFABIO32(STRINGXP(PTR_L) " $13, " STRINGXP(PTRSIZE) "($28)") "\n\
365 " IFNEWABI(STRINGXP(PTR_L) " $13, " STRINGXP(PTRSIZE) "($14)") "\n\
366 # Modify t9 ($25) so as to point .cpload instruction.\n\
367 " IFABIO32(STRINGXP(PTR_ADDIU) " $25, (2f-1b)\n") "\
368 # Compute GP.\n\
3692: " STRINGXP(SETUP_GP) "\n\
370 " STRINGXV(SETUP_GP64 (0, _dl_runtime_pltresolve)) "\n\
371 .set reorder\n\
372 " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
373 " ELF_DL_PLT_SAVE_ARG_REGS "\
374 move $4, $13\n\
375 sll $5, $24, " STRINGXP(PTRLOG) " + 1\n\
376 jal _dl_fixup\n\
377 move $25, $2\n\
378 " ELF_DL_PLT_RESTORE_ARG_REGS "\
379 " STRINGXP(RESTORE_GP64) "\n\
380 " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
381 jr $25\n\
382 .end _dl_runtime_pltresolve\n\
383 .previous\n\
384");
385
386#elif _MIPS_SIM == _ABIO32 /* __mips16 */
387/* MIPS16 version, O32 only. */
388asm ("\n\
389 .text\n\
390 .align 2\n\
391 .set mips16\n\
392 .globl _dl_runtime_resolve\n\
393 .type _dl_runtime_resolve,@function\n\
394 .ent _dl_runtime_resolve\n\
395_dl_runtime_resolve:\n\
396 .frame $29, " STRINGXP (ELF_DL_FRAME_SIZE) ", $31\n\
397 # Save arguments and sp value in stack.\n\t"
398# if _MIPS_ISA >= _MIPS_ISA_MIPS32
399 "save " STRINGXP (ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
400# else
401 "addiu $sp, -" STRINGXP (ELF_DL_FRAME_SIZE) "\n\
402 sw $7, 32($sp)\n\
403 sw $6, 28($sp)\n\
404 sw $5, 24($sp)\n\
405 sw $4, 20($sp)\n\t"
406# endif
407 "# Preserve caller's $ra, for RESTORE instruction below.\n\
408 move $5, $15\n\
409 sw $5, 36($sp)\n\
410 # Compute GP into $2.\n\
411 li $2, %hi(_gp_disp)\n\
412 addiu $3, $pc, %lo(_gp_disp)\n\
413 sll $2, 16\n\
414 addu $2, $3\n\
415 lw $3, %got(__dl_runtime_resolve)($2)\n\
416 move $4, $24\n\
417 addiu $3, %lo(__dl_runtime_resolve)\n\
418 move $7, $ra\n\
419 move $6, $28\n\
420 move $25, $3\n\
421 jalr $3\n\t"
422# if _MIPS_ISA >= _MIPS_ISA_MIPS32
423 "restore " STRINGXP(ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
424# else
425 "# Restore $ra, move placed further down to hide latency.\n\
426 lw $4, 36($sp)\n\
427 lw $5, 24($sp)\n\
428 lw $6, 28($sp)\n\
429 lw $7, 32($sp)\n\
430 move $ra, $4\n\
431 lw $4, 20($sp)\n\
432 addiu $sp, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\t"
433# endif
434 "move $25, $2\n\
435 jr $2\n\
436 .end _dl_runtime_resolve\n\
437 .previous\n\
438");
439
440asm ("\n\
441 .text\n\
442 .align 2\n\
443 .set mips16\n\
444 .globl _dl_runtime_pltresolve\n\
445 .type _dl_runtime_pltresolve,@function\n\
446 .ent _dl_runtime_pltresolve\n\
447_dl_runtime_pltresolve:\n\
448 .frame $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
449 # Save arguments and sp value in stack.\n\t"
450# if _MIPS_ISA >= _MIPS_ISA_MIPS32
451 "save " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
452# else
453 "addiu $sp, -" STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
454 sw $7, 40($sp)\n\
455 sw $6, 36($sp)\n\
456 sw $5, 32($sp)\n\
457 sw $4, 28($sp)\n\t"
458# endif
459 "# Preserve MIPS16 stub function arguments.\n\
460 sw $3, 20($sp)\n\
461 sw $2, 16($sp)\n\
462 # Preserve caller's $ra, for RESTORE instruction below.\n\
463 move $3, $15\n\
464 sw $3, 44($sp)\n\
465 # Compute GP into $2.\n\
466 li $2, %hi(_gp_disp)\n\
467 addiu $3, $pc, %lo(_gp_disp)\n\
468 sll $2, 16\n\
469 addu $2, $3\n\
470 # Save GP value in slot.\n\
471 sw $2, 24($sp)\n\
472 # Load _dl_fixup address.\n\
473 lw $6, %call16(_dl_fixup)($2)\n\
474 # Load link map address.\n\
475 move $3, $28\n\
476 lw $4, " STRINGXP (PTRSIZE) "($3)\n\
477 move $5, $24\n\
478 sll $5, " STRINGXP (PTRLOG) " + 1\n\
479 # Call _dl_fixup.\n\
480 move $25, $6\n\
481 jalr $6\n\
482 move $25, $2\n\
483 # Reload GP value into $28.\n\
484 lw $3, 24($sp)\n\
485 move $28, $3\n\
486 lw $3, 16($sp)\n\
487 move $15, $3\n\
488 lw $3, 20($sp)\n\t"
489# if _MIPS_ISA >= _MIPS_ISA_MIPS32
490 "restore " STRINGXP (ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
491# else
492 "# Restore $ra, move placed further down to hide latency.\n\
493 lw $4, 44($sp)\n\
494 lw $5, 32($sp)\n\
495 lw $6, 36($sp)\n\
496 lw $7, 40($sp)\n\
497 move $ra, $4\n\
498 lw $4, 28($sp)\n\
499 addiu $sp, " STRINGXP (ELF_DL_PLT_FRAME_SIZE) "\n\t"
500# endif
501 ".set noreorder\n\
502 jr $2\n\
503 move $2, $15\n\
504 .set reorder\n\
505 .end _dl_runtime_pltresolve\n\
506 .previous\n\
507");
508
509#else /* __mips16 && _MIPS_SIM != _ABIO32 */
510# error "MIPS16 support for N32/N64 not implemented"
511
512#endif /* __mips16 */
513

source code of glibc/sysdeps/mips/dl-trampoline.c