Update copyright dates with scripts/update-copyrights.
[glibc.git] / sysdeps / mips / dl-trampoline.c
blob25b170990ce89b7b9a9fb651f6cd5d0975926323
1 /* PLT trampoline. MIPS version.
2 Copyright (C) 1996-2015 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. */
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>
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);
43 ElfW(Word) g1;
45 g1 = ((ElfW(Word) *) got)[1];
47 if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
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;
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)
66 return l;
72 struct link_map *l;
73 Lmid_t nsid;
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)
89 return l;
94 _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
95 return NULL;
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. */
115 static ElfW(Addr)
116 __dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))
117 __attribute_used__;
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)
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;
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)
144 default:
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 ();
167 break;
169 /* Fall through. */
171 case 0:
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,
185 flags, 0);
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 = (sym ? sym_map->l_addr + sym->st_value : 0);
197 else
198 /* We already found the symbol. The module (and therefore its load
199 address) is also known. */
200 value = l->l_addr + sym->st_value;
202 /* Apply the relocation with that value. */
203 *(got + local_gotno + sym_index - gotsym) = value;
205 return value;
208 #if _MIPS_SIM == _ABIO32
209 #define ELF_DL_FRAME_SIZE 40
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 \
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 \
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 "\
233 sw $2, 40($29)\n \
234 sw $3, 44($29)\n \
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 \
243 #define IFABIO32(X) X
244 #define IFNEWABI(X)
246 #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
248 #define ELF_DL_FRAME_SIZE 80
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 \
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 \
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 "\
280 sd $2, 80($29)\n \
281 sd $3, 88($29)\n \
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 \
290 #define IFABIO32(X)
291 #define IFNEWABI(X) X
293 #endif
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\
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
337 this function:
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.
348 _dl_fixup needs:
350 - $4 (a0) - link map address
351 - $5 (a1) - .rel.plt offset (== PLT entry index * 8) */
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\
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\
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\
510 #else /* __mips16 && _MIPS_SIM != _ABIO32 */
511 # error "MIPS16 support for N32/N64 not implemented"
513 #endif /* __mips16 */