Update copyright notices with scripts/update-copyrights
[jlayton/glibc.git] / ports / sysdeps / mips / dl-trampoline.c
1 /* PLT trampoline.  MIPS version.
2    Copyright (C) 1996-2014 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)
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             /* Fall through.  */
170           }
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 = (sym ? sym_map->l_addr + sym->st_value : 0);
196     }
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;
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 */