]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/mips/dl-trampoline.c
5a8cc7dc56eadcb8e4e752150110477afbaf4342
[thirdparty/glibc.git] / sysdeps / mips / dl-trampoline.c
1 /* PLT trampoline. MIPS version.
2 Copyright (C) 1996-2019 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>.
5
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.
10
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.
15
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/>. */
19
20 /* FIXME: Profiling of shared libraries is not implemented yet. */
21
22 #include <sysdep.h>
23 #include <link.h>
24 #include <elf.h>
25 #include <ldsodefs.h>
26 #include <dl-machine.h>
27 #include <sysdep-cancel.h>
28
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)
32 {
33 extern int _dl_mips_gnu_objects;
34
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. */
39
40 if (_dl_mips_gnu_objects)
41 {
42 ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
43 ElfW(Word) g1;
44
45 g1 = ((ElfW(Word) *) got)[1];
46
47 if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
48 {
49 struct link_map *l =
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;
54
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++)
60 {
61 if (p[this].p_type == PT_LOAD)
62 {
63 base = p[this].p_vaddr + l->l_addr;
64 limit = base + p[this].p_memsz;
65 if (stub_pc >= base && stub_pc < limit)
66 return l;
67 }
68 }
69 }
70 }
71
72 struct link_map *l;
73 Lmid_t nsid;
74
75 for (nsid = 0; nsid < DL_NNS; ++nsid)
76 for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
77 {
78 ElfW(Addr) base, limit;
79 const ElfW(Phdr) *p = l->l_phdr;
80 ElfW(Half) this, nent = l->l_phnum;
81
82 for (this = 0; this < nent; ++this)
83 {
84 if (p[this].p_type == PT_LOAD)
85 {
86 base = p[this].p_vaddr + l->l_addr;
87 limit = base + p[this].p_memsz;
88 if (stub_pc >= base && stub_pc < limit)
89 return l;
90 }
91 }
92 }
93
94 _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
95 return NULL;
96 }
97
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.
104
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). */
109
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;
113
114 /* This is called from assembly stubs below which the compiler can't see. */
115 static ElfW(Addr)
116 __dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))
117 __attribute_used__;
118
119 static ElfW(Addr)
120 __dl_runtime_resolve (ElfW(Word) sym_index,
121 ElfW(Word) return_address,
122 ElfW(Addr) old_gpreg,
123 ElfW(Addr) stub_pc)
124 {
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]);
129 ElfW(Addr) *got
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;
137 ElfW(Addr) value;
138
139 /* FIXME: The symbol versioning stuff is not tested yet. */
140 if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
141 {
142 switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL ? 1 : 0)
143 {
144 default:
145 {
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];
150
151 if (version->hash != 0)
152 {
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 ();
158
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);
162
163 /* We are done with the global scope. */
164 if (!RTLD_SINGLE_THREAD_P)
165 THREAD_GSCOPE_RESET_FLAG ();
166
167 break;
168 }
169 }
170 /* Fall through. */
171 case 0:
172 {
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)
178 {
179 THREAD_GSCOPE_SET_FLAG ();
180 flags |= DL_LOOKUP_GSCOPE_LOCK;
181 }
182
183 sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
184 l->l_scope, 0, ELF_RTYPE_CLASS_PLT,
185 flags, 0);
186
187 /* We are done with the global scope. */
188 if (!RTLD_SINGLE_THREAD_P)
189 THREAD_GSCOPE_RESET_FLAG ();
190 }
191 }
192
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);
196 }
197 else
198 /* We already found the symbol. The module (and therefore its load
199 address) is also known. */
200 value = SYMBOL_ADDRESS (l, sym, true);
201
202 /* Apply the relocation with that value. */
203 *(got + local_gotno + sym_index - gotsym) = value;
204
205 return value;
206 }
207
208 #if _MIPS_SIM == _ABIO32
209 #define ELF_DL_FRAME_SIZE 40
210
211 #define ELF_DL_SAVE_ARG_REGS "\
212 sw $15, 36($29)\n \
213 sw $4, 16($29)\n \
214 sw $5, 20($29)\n \
215 sw $6, 24($29)\n \
216 sw $7, 28($29)\n \
217 "
218
219 #define ELF_DL_RESTORE_ARG_REGS "\
220 lw $31, 36($29)\n \
221 lw $4, 16($29)\n \
222 lw $5, 20($29)\n \
223 lw $6, 24($29)\n \
224 lw $7, 28($29)\n \
225 "
226
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
230
231 #define ELF_DL_PLT_SAVE_ARG_REGS \
232 ELF_DL_SAVE_ARG_REGS "\
233 sw $2, 40($29)\n \
234 sw $3, 44($29)\n \
235 "
236
237 #define ELF_DL_PLT_RESTORE_ARG_REGS \
238 ELF_DL_RESTORE_ARG_REGS "\
239 lw $2, 40($29)\n \
240 lw $3, 44($29)\n \
241 "
242
243 #define IFABIO32(X) X
244 #define IFNEWABI(X)
245
246 #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
247
248 #define ELF_DL_FRAME_SIZE 80
249
250 #define ELF_DL_SAVE_ARG_REGS "\
251 sd $15, 72($29)\n \
252 sd $4, 8($29)\n \
253 sd $5, 16($29)\n \
254 sd $6, 24($29)\n \
255 sd $7, 32($29)\n \
256 sd $8, 40($29)\n \
257 sd $9, 48($29)\n \
258 sd $10, 56($29)\n \
259 sd $11, 64($29)\n \
260 "
261
262 #define ELF_DL_RESTORE_ARG_REGS "\
263 ld $31, 72($29)\n \
264 ld $4, 8($29)\n \
265 ld $5, 16($29)\n \
266 ld $6, 24($29)\n \
267 ld $7, 32($29)\n \
268 ld $8, 40($29)\n \
269 ld $9, 48($29)\n \
270 ld $10, 56($29)\n \
271 ld $11, 64($29)\n \
272 "
273
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
277
278 #define ELF_DL_PLT_SAVE_ARG_REGS \
279 ELF_DL_SAVE_ARG_REGS "\
280 sd $2, 80($29)\n \
281 sd $3, 88($29)\n \
282 "
283
284 #define ELF_DL_PLT_RESTORE_ARG_REGS \
285 ELF_DL_RESTORE_ARG_REGS "\
286 ld $2, 80($29)\n \
287 ld $3, 88($29)\n \
288 "
289
290 #define IFABIO32(X)
291 #define IFNEWABI(X) X
292
293 #endif
294
295 #ifndef __mips16
296 asm ("\n\
297 .text\n\
298 .align 2\n\
299 .set nomips16\n\
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\
305 .set noreorder\n\
306 # Save GP.\n\
307 1: move $3, $28\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") "\
312 # Compute GP.\n\
313 2: " STRINGXP(SETUP_GP) "\n\
314 " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\
315 .set reorder\n\
316 # Save slot call pc.\n\
317 move $2, $31\n\
318 " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
319 " ELF_DL_SAVE_ARG_REGS "\
320 move $4, $24\n\
321 move $5, $15\n\
322 move $6, $3\n\
323 move $7, $2\n\
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\
328 move $25, $2\n\
329 jr $25\n\
330 .end _dl_runtime_resolve\n\
331 .previous\n\
332 ");
333
334 /* Assembler veneer called from the PLT header code when using PLTs.
335
336 Code in each PLT entry and the PLT header fills in the arguments to
337 this function:
338
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
343
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.
347
348 _dl_fixup needs:
349
350 - $4 (a0) - link map address
351 - $5 (a1) - .rel.plt offset (== PLT entry index * 8) */
352
353 asm ("\n\
354 .text\n\
355 .align 2\n\
356 .set nomips16\n\
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\
362 .set noreorder\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") "\
369 # Compute GP.\n\
370 2: " STRINGXP(SETUP_GP) "\n\
371 " STRINGXV(SETUP_GP64 (0, _dl_runtime_pltresolve)) "\n\
372 .set reorder\n\
373 " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
374 " ELF_DL_PLT_SAVE_ARG_REGS "\
375 move $4, $13\n\
376 sll $5, $24, " STRINGXP(PTRLOG) " + 1\n\
377 jal _dl_fixup\n\
378 move $25, $2\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\
382 jr $25\n\
383 .end _dl_runtime_pltresolve\n\
384 .previous\n\
385 ");
386
387 #elif _MIPS_SIM == _ABIO32 /* __mips16 */
388 /* MIPS16 version, O32 only. */
389 asm ("\n\
390 .text\n\
391 .align 2\n\
392 .set mips16\n\
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"
401 # else
402 "addiu $sp, -" STRINGXP (ELF_DL_FRAME_SIZE) "\n\
403 sw $7, 32($sp)\n\
404 sw $6, 28($sp)\n\
405 sw $5, 24($sp)\n\
406 sw $4, 20($sp)\n\t"
407 # endif
408 "# Preserve caller's $ra, for RESTORE instruction below.\n\
409 move $5, $15\n\
410 sw $5, 36($sp)\n\
411 # Compute GP into $2.\n\
412 li $2, %hi(_gp_disp)\n\
413 addiu $3, $pc, %lo(_gp_disp)\n\
414 sll $2, 16\n\
415 addu $2, $3\n\
416 lw $3, %got(__dl_runtime_resolve)($2)\n\
417 move $4, $24\n\
418 addiu $3, %lo(__dl_runtime_resolve)\n\
419 move $7, $ra\n\
420 move $6, $28\n\
421 move $25, $3\n\
422 jalr $3\n\t"
423 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
424 "restore " STRINGXP(ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
425 # else
426 "# Restore $ra, move placed further down to hide latency.\n\
427 lw $4, 36($sp)\n\
428 lw $5, 24($sp)\n\
429 lw $6, 28($sp)\n\
430 lw $7, 32($sp)\n\
431 move $ra, $4\n\
432 lw $4, 20($sp)\n\
433 addiu $sp, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\t"
434 # endif
435 "move $25, $2\n\
436 jr $2\n\
437 .end _dl_runtime_resolve\n\
438 .previous\n\
439 ");
440
441 asm ("\n\
442 .text\n\
443 .align 2\n\
444 .set mips16\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"
453 # else
454 "addiu $sp, -" STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
455 sw $7, 40($sp)\n\
456 sw $6, 36($sp)\n\
457 sw $5, 32($sp)\n\
458 sw $4, 28($sp)\n\t"
459 # endif
460 "# Preserve MIPS16 stub function arguments.\n\
461 sw $3, 20($sp)\n\
462 sw $2, 16($sp)\n\
463 # Preserve caller's $ra, for RESTORE instruction below.\n\
464 move $3, $15\n\
465 sw $3, 44($sp)\n\
466 # Compute GP into $2.\n\
467 li $2, %hi(_gp_disp)\n\
468 addiu $3, $pc, %lo(_gp_disp)\n\
469 sll $2, 16\n\
470 addu $2, $3\n\
471 # Save GP value in slot.\n\
472 sw $2, 24($sp)\n\
473 # Load _dl_fixup address.\n\
474 lw $6, %call16(_dl_fixup)($2)\n\
475 # Load link map address.\n\
476 move $3, $28\n\
477 lw $4, " STRINGXP (PTRSIZE) "($3)\n\
478 move $5, $24\n\
479 sll $5, " STRINGXP (PTRLOG) " + 1\n\
480 # Call _dl_fixup.\n\
481 move $25, $6\n\
482 jalr $6\n\
483 move $25, $2\n\
484 # Reload GP value into $28.\n\
485 lw $3, 24($sp)\n\
486 move $28, $3\n\
487 lw $3, 16($sp)\n\
488 move $15, $3\n\
489 lw $3, 20($sp)\n\t"
490 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
491 "restore " STRINGXP (ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
492 # else
493 "# Restore $ra, move placed further down to hide latency.\n\
494 lw $4, 44($sp)\n\
495 lw $5, 32($sp)\n\
496 lw $6, 36($sp)\n\
497 lw $7, 40($sp)\n\
498 move $ra, $4\n\
499 lw $4, 28($sp)\n\
500 addiu $sp, " STRINGXP (ELF_DL_PLT_FRAME_SIZE) "\n\t"
501 # endif
502 ".set noreorder\n\
503 jr $2\n\
504 move $2, $15\n\
505 .set reorder\n\
506 .end _dl_runtime_pltresolve\n\
507 .previous\n\
508 ");
509
510 #else /* __mips16 && _MIPS_SIM != _ABIO32 */
511 # error "MIPS16 support for N32/N64 not implemented"
512
513 #endif /* __mips16 */