Move all files into ports/ subdirectory in preparation for merge with glibc
[jlayton/glibc.git] / ports / sysdeps / mips / dl-trampoline.c
1 /* PLT trampoline.  MIPS version.
2    Copyright (C) 1996-2001, 2002, 2003, 2004, 2005
3    Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5    Contributed by Kazumoto Kojima <kkojima@info.kanagawa-u.ac.jp>.
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library.  If not, see
19    <http://www.gnu.org/licenses/>.  */
20
21 /*  FIXME: Profiling of shared libraries is not implemented yet.  */
22
23 #include <sysdep.h>
24 #include <link.h>
25 #include <elf.h>
26 #include <ldsodefs.h>
27 #include <dl-machine.h>
28 #include <sysdep-cancel.h>
29
30 /* Get link map for callers object containing STUB_PC.  */
31 static inline struct link_map *
32 elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
33 {
34   extern int _dl_mips_gnu_objects;
35
36   /* got[1] is reserved to keep its link map address for the shared
37      object generated by the gnu linker.  If all are such objects, we
38      can find the link map from current GPREG simply.  If not so, get
39      the link map for caller's object containing STUB_PC.  */
40
41   if (_dl_mips_gnu_objects)
42     {
43       ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
44       ElfW(Word) g1;
45
46       g1 = ((ElfW(Word) *) got)[1];
47
48       if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
49         {
50           struct link_map *l =
51             (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK);
52           ElfW(Addr) base, limit;
53           const ElfW(Phdr) *p = l->l_phdr;
54           ElfW(Half) this, nent = l->l_phnum;
55
56           /* For the common case of a stub being called from the containing
57              object, STUB_PC will point to somewhere within the object that
58              is described by the link map fetched via got[1].  Otherwise we
59              have to scan all maps.  */
60           for (this = 0; this < nent; this++)
61             {
62               if (p[this].p_type == PT_LOAD)
63                 {
64                   base = p[this].p_vaddr + l->l_addr;
65                   limit = base + p[this].p_memsz;
66                   if (stub_pc >= base && stub_pc < limit)
67                     return l;
68                 }
69             }
70         }
71     }
72
73     struct link_map *l;
74     Lmid_t nsid;
75
76     for (nsid = 0; nsid < DL_NNS; ++nsid)
77       for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
78         {
79           ElfW(Addr) base, limit;
80           const ElfW(Phdr) *p = l->l_phdr;
81           ElfW(Half) this, nent = l->l_phnum;
82
83           for (this = 0; this < nent; ++this)
84             {
85               if (p[this].p_type == PT_LOAD)
86                 {
87                   base = p[this].p_vaddr + l->l_addr;
88                   limit = base + p[this].p_memsz;
89                   if (stub_pc >= base && stub_pc < limit)
90                     return l;
91                 }
92             }
93         }
94
95   _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
96   return NULL;
97 }
98
99 /* Define mips specific runtime resolver. The function __dl_runtime_resolve
100    is called from assembler function _dl_runtime_resolve which converts
101    special argument registers t7 ($15) and t8 ($24):
102      t7  address to return to the caller of the function
103      t8  index for this function symbol in .dynsym
104    to usual c arguments.
105
106    Other architectures call fixup from dl-runtime.c in
107    _dl_runtime_resolve.  MIPS instead calls __dl_runtime_resolve.  We
108    have to use our own version because of the way the got section is
109    treated on MIPS (we've also got ELF_MACHINE_PLT defined).  */
110
111 /* The flag _dl_mips_gnu_objects is set if all dynamic objects are
112    generated by the gnu linker. */
113 int _dl_mips_gnu_objects = 1;
114
115 #define VERSYMIDX(sym)  (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
116
117 /* This is called from assembly stubs below which the compiler can't see.  */
118 static ElfW(Addr)
119 __dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))
120                   __attribute_used__;
121
122 static ElfW(Addr)
123 __dl_runtime_resolve (ElfW(Word) sym_index,
124                       ElfW(Word) return_address,
125                       ElfW(Addr) old_gpreg,
126                       ElfW(Addr) stub_pc)
127 {
128   struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc);
129   const ElfW(Sym) *const symtab
130     = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]);
131   const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
132   ElfW(Addr) *got
133     = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
134   const ElfW(Word) local_gotno
135     = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
136   const ElfW(Word) gotsym
137     = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
138   const ElfW(Sym) *sym = &symtab[sym_index];
139   struct link_map *sym_map;
140   ElfW(Addr) value;
141
142   /* FIXME: The symbol versioning stuff is not tested yet.  */
143   if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
144     {
145       switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
146         {
147         default:
148           {
149             const ElfW(Half) *vernum =
150               (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
151             ElfW(Half) ndx = vernum[sym_index] & 0x7fff;
152             const struct r_found_version *version = &l->l_versions[ndx];
153
154             if (version->hash != 0)
155               {
156                 /* We need to keep the scope around so do some locking.  This is
157                    not necessary for objects which cannot be unloaded or when
158                    we are not using any threads (yet).  */
159                 if (!RTLD_SINGLE_THREAD_P)
160                   THREAD_GSCOPE_SET_FLAG ();
161
162                 sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l,
163                                                &sym, l->l_scope, version,
164                                                ELF_RTYPE_CLASS_PLT, 0, 0);
165
166                 /* We are done with the global scope.  */
167                 if (!RTLD_SINGLE_THREAD_P)
168                   THREAD_GSCOPE_RESET_FLAG ();
169
170                 break;
171               }
172             /* Fall through.  */
173           }
174         case 0:
175           {
176           /* We need to keep the scope around so do some locking.  This is
177              not necessary for objects which cannot be unloaded or when
178              we are not using any threads (yet).  */
179           int flags = DL_LOOKUP_ADD_DEPENDENCY;
180           if (!RTLD_SINGLE_THREAD_P)
181             {
182               THREAD_GSCOPE_SET_FLAG ();
183               flags |= DL_LOOKUP_GSCOPE_LOCK;
184             }
185
186           sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
187                                          l->l_scope, 0, ELF_RTYPE_CLASS_PLT,
188                                          flags, 0);
189
190           /* We are done with the global scope.  */
191           if (!RTLD_SINGLE_THREAD_P)
192             THREAD_GSCOPE_RESET_FLAG ();
193           }
194         }
195
196       /* Currently value contains the base load address of the object
197          that defines sym.  Now add in the symbol offset.  */
198       value = (sym ? sym_map->l_addr + sym->st_value : 0);
199     }
200   else
201     /* We already found the symbol.  The module (and therefore its load
202        address) is also known.  */
203     value = l->l_addr + sym->st_value;
204
205   /* Apply the relocation with that value.  */
206   *(got + local_gotno + sym_index - gotsym) = value;
207
208   return value;
209 }
210
211 #if _MIPS_SIM == _ABIO32
212 #define ELF_DL_FRAME_SIZE 40
213
214 #define ELF_DL_SAVE_ARG_REGS "\
215         sw      $15, 36($29)\n                                                \
216         sw      $4, 16($29)\n                                                 \
217         sw      $5, 20($29)\n                                                 \
218         sw      $6, 24($29)\n                                                 \
219         sw      $7, 28($29)\n                                                 \
220 "
221
222 #define ELF_DL_RESTORE_ARG_REGS "\
223         lw      $31, 36($29)\n                                                \
224         lw      $4, 16($29)\n                                                 \
225         lw      $5, 20($29)\n                                                 \
226         lw      $6, 24($29)\n                                                 \
227         lw      $7, 28($29)\n                                                 \
228 "
229
230 /* The PLT resolver should also save and restore $2 and $3, which are used
231    as arguments to MIPS16 stub functions.  */
232 #define ELF_DL_PLT_FRAME_SIZE 48
233
234 #define ELF_DL_PLT_SAVE_ARG_REGS \
235         ELF_DL_SAVE_ARG_REGS "\
236         sw      $2, 40($29)\n                                                 \
237         sw      $3, 44($29)\n                                                 \
238 "
239
240 #define ELF_DL_PLT_RESTORE_ARG_REGS \
241         ELF_DL_RESTORE_ARG_REGS "\
242         lw      $2, 40($29)\n                                                 \
243         lw      $3, 44($29)\n                                                 \
244 "
245
246 #define IFABIO32(X) X
247 #define IFNEWABI(X)
248
249 #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
250
251 #define ELF_DL_FRAME_SIZE 80
252
253 #define ELF_DL_SAVE_ARG_REGS "\
254         sd      $15, 72($29)\n                                                \
255         sd      $4, 8($29)\n                                                  \
256         sd      $5, 16($29)\n                                                 \
257         sd      $6, 24($29)\n                                                 \
258         sd      $7, 32($29)\n                                                 \
259         sd      $8, 40($29)\n                                                 \
260         sd      $9, 48($29)\n                                                 \
261         sd      $10, 56($29)\n                                                \
262         sd      $11, 64($29)\n                                                \
263 "
264
265 #define ELF_DL_RESTORE_ARG_REGS "\
266         ld      $31, 72($29)\n                                                \
267         ld      $4, 8($29)\n                                                  \
268         ld      $5, 16($29)\n                                                 \
269         ld      $6, 24($29)\n                                                 \
270         ld      $7, 32($29)\n                                                 \
271         ld      $8, 40($29)\n                                                 \
272         ld      $9, 48($29)\n                                                 \
273         ld      $10, 56($29)\n                                                \
274         ld      $11, 64($29)\n                                                \
275 "
276
277 /* The PLT resolver should also save and restore $2 and $3, which are used
278    as arguments to MIPS16 stub functions.  */
279 #define ELF_DL_PLT_FRAME_SIZE 96
280
281 #define ELF_DL_PLT_SAVE_ARG_REGS \
282         ELF_DL_SAVE_ARG_REGS "\
283         sd      $2, 80($29)\n                                                 \
284         sd      $3, 88($29)\n                                                 \
285 "
286
287 #define ELF_DL_PLT_RESTORE_ARG_REGS \
288         ELF_DL_RESTORE_ARG_REGS "\
289         ld      $2, 80($29)\n                                                 \
290         ld      $3, 88($29)\n                                                 \
291 "
292
293 #define IFABIO32(X)
294 #define IFNEWABI(X) X
295
296 #endif
297
298 asm ("\n\
299         .text\n\
300         .align  2\n\
301         .globl  _dl_runtime_resolve\n\
302         .type   _dl_runtime_resolve,@function\n\
303         .ent    _dl_runtime_resolve\n\
304 _dl_runtime_resolve:\n\
305         .frame  $29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n\
306         .set noreorder\n\
307         # Save GP.\n\
308 1:      move    $3, $28\n\
309         # Save arguments and sp value in stack.\n\
310         " STRINGXP(PTR_SUBIU) "  $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
311         # Modify t9 ($25) so as to point .cpload instruction.\n\
312         " IFABIO32(STRINGXP(PTR_ADDIU) "        $25, (2f-1b)\n") "\
313         # Compute GP.\n\
314 2:      " STRINGXP(SETUP_GP) "\n\
315         " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\
316         .set reorder\n\
317         # Save slot call pc.\n\
318         move    $2, $31\n\
319         " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
320         " ELF_DL_SAVE_ARG_REGS "\
321         move    $4, $24\n\
322         move    $5, $15\n\
323         move    $6, $3\n\
324         move    $7, $2\n\
325         jal     __dl_runtime_resolve\n\
326         " ELF_DL_RESTORE_ARG_REGS "\
327         " STRINGXP(RESTORE_GP64) "\n\
328         " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
329         move    $25, $2\n\
330         jr      $25\n\
331         .end    _dl_runtime_resolve\n\
332         .previous\n\
333 ");
334
335 /* Assembler veneer called from the PLT header code when using PLTs.
336
337    Code in each PLT entry and the PLT header fills in the arguments to
338    this function:
339
340    - $15 (o32 t7, n32/n64 t3) - caller's return address
341    - $24 (t8) - PLT entry index
342    - $25 (t9) - address of _dl_runtime_pltresolve
343    - o32 $28 (gp), n32/n64 $14 (t2) - address of .got.plt
344
345    Different registers are used for .got.plt because the ABI was
346    originally designed for o32, where gp was available (call
347    clobbered).  On n32/n64 gp is call saved.
348
349    _dl_fixup needs:
350
351    - $4 (a0) - link map address
352    - $5 (a1) - .rel.plt offset (== PLT entry index * 8)  */
353
354 asm ("\n\
355         .text\n\
356         .align  2\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