chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.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, write to the Free
19    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20    02111-1307 USA.  */
21
22 /*  FIXME: Profiling of shared libraries is not implemented yet.  */
23
24 #include <sysdep.h>
25 #include <link.h>
26 #include <elf.h>
27 #include <ldsodefs.h>
28 #include <dl-machine.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                 sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l,
157                                                &sym, l->l_scope, version,
158                                                ELF_RTYPE_CLASS_PLT, 0, 0);
159                 break;
160               }
161             /* Fall through.  */
162           }
163         case 0:
164           sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
165                                          l->l_scope, 0, ELF_RTYPE_CLASS_PLT,
166                                          DL_LOOKUP_ADD_DEPENDENCY, 0);
167         }
168
169       /* Currently value contains the base load address of the object
170          that defines sym.  Now add in the symbol offset.  */
171       value = (sym ? sym_map->l_addr + sym->st_value : 0);
172     }
173   else
174     /* We already found the symbol.  The module (and therefore its load
175        address) is also known.  */
176     value = l->l_addr + sym->st_value;
177
178   /* Apply the relocation with that value.  */
179   *(got + local_gotno + sym_index - gotsym) = value;
180
181   return value;
182 }
183
184 #if _MIPS_SIM == _ABIO32
185 #define ELF_DL_FRAME_SIZE 40
186
187 #define ELF_DL_SAVE_ARG_REGS "\
188         sw      $15, 36($29)\n                                                \
189         sw      $4, 16($29)\n                                                 \
190         sw      $5, 20($29)\n                                                 \
191         sw      $6, 24($29)\n                                                 \
192         sw      $7, 28($29)\n                                                 \
193 "
194
195 #define ELF_DL_RESTORE_ARG_REGS "\
196         lw      $31, 36($29)\n                                                \
197         lw      $4, 16($29)\n                                                 \
198         lw      $5, 20($29)\n                                                 \
199         lw      $6, 24($29)\n                                                 \
200         lw      $7, 28($29)\n                                                 \
201 "
202
203 /* The PLT resolver should also save and restore $2 and $3, which are used
204    as arguments to MIPS16 stub functions.  */
205 #define ELF_DL_PLT_FRAME_SIZE 48
206
207 #define ELF_DL_PLT_SAVE_ARG_REGS \
208         ELF_DL_SAVE_ARG_REGS "\
209         sw      $2, 40($29)\n                                                 \
210         sw      $3, 44($29)\n                                                 \
211 "
212
213 #define ELF_DL_PLT_RESTORE_ARG_REGS \
214         ELF_DL_RESTORE_ARG_REGS "\
215         lw      $2, 40($29)\n                                                 \
216         lw      $3, 44($29)\n                                                 \
217 "
218
219 #define IFABIO32(X) X
220 #define IFNEWABI(X)
221
222 #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
223
224 #define ELF_DL_FRAME_SIZE 80
225
226 #define ELF_DL_SAVE_ARG_REGS "\
227         sd      $15, 72($29)\n                                                \
228         sd      $4, 8($29)\n                                                  \
229         sd      $5, 16($29)\n                                                 \
230         sd      $6, 24($29)\n                                                 \
231         sd      $7, 32($29)\n                                                 \
232         sd      $8, 40($29)\n                                                 \
233         sd      $9, 48($29)\n                                                 \
234         sd      $10, 56($29)\n                                                \
235         sd      $11, 64($29)\n                                                \
236 "
237
238 #define ELF_DL_RESTORE_ARG_REGS "\
239         ld      $31, 72($29)\n                                                \
240         ld      $4, 8($29)\n                                                  \
241         ld      $5, 16($29)\n                                                 \
242         ld      $6, 24($29)\n                                                 \
243         ld      $7, 32($29)\n                                                 \
244         ld      $8, 40($29)\n                                                 \
245         ld      $9, 48($29)\n                                                 \
246         ld      $10, 56($29)\n                                                \
247         ld      $11, 64($29)\n                                                \
248 "
249
250 /* The PLT resolver should also save and restore $2 and $3, which are used
251    as arguments to MIPS16 stub functions.  */
252 #define ELF_DL_PLT_FRAME_SIZE 96
253
254 #define ELF_DL_PLT_SAVE_ARG_REGS \
255         ELF_DL_SAVE_ARG_REGS "\
256         sd      $2, 80($29)\n                                                 \
257         sd      $3, 88($29)\n                                                 \
258 "
259
260 #define ELF_DL_PLT_RESTORE_ARG_REGS \
261         ELF_DL_RESTORE_ARG_REGS "\
262         ld      $2, 80($29)\n                                                 \
263         ld      $3, 88($29)\n                                                 \
264 "
265
266 #define IFABIO32(X)
267 #define IFNEWABI(X) X
268
269 #endif
270
271 asm ("\n\
272         .text\n\
273         .align  2\n\
274         .globl  _dl_runtime_resolve\n\
275         .type   _dl_runtime_resolve,@function\n\
276         .ent    _dl_runtime_resolve\n\
277 _dl_runtime_resolve:\n\
278         .frame  $29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n\
279         .set noreorder\n\
280         # Save GP.\n\
281         move    $3, $28\n\
282         # Save arguments and sp value in stack.\n\
283         " STRINGXP(PTR_SUBIU) "  $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
284         # Modify t9 ($25) so as to point .cpload instruction.\n\
285         " IFABIO32(STRINGXP(PTR_ADDIU) "        $25, 12\n") "\
286         # Compute GP.\n\
287         " STRINGXP(SETUP_GP) "\n\
288         " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\
289         .set reorder\n\
290         # Save slot call pc.\n\
291         move    $2, $31\n\
292         " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
293         " ELF_DL_SAVE_ARG_REGS "\
294         move    $4, $24\n\
295         move    $5, $15\n\
296         move    $6, $3\n\
297         move    $7, $2\n\
298         jal     __dl_runtime_resolve\n\
299         " ELF_DL_RESTORE_ARG_REGS "\
300         " STRINGXP(RESTORE_GP64) "\n\
301         " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
302         move    $25, $2\n\
303         jr      $25\n\
304         .end    _dl_runtime_resolve\n\
305         .previous\n\
306 ");
307
308 /* Assembler veneer called from the PLT header code when using PLTs.
309
310    Code in each PLT entry and the PLT header fills in the arguments to
311    this function:
312
313    - $15 (o32 t7, n32/n64 t3) - caller's return address
314    - $24 (t8) - PLT entry index
315    - $25 (t9) - address of _dl_runtime_pltresolve
316    - o32 $28 (gp), n32/n64 $14 (t2) - address of .got.plt
317
318    Different registers are used for .got.plt because the ABI was
319    originally designed for o32, where gp was available (call
320    clobbered).  On n32/n64 gp is call saved.
321
322    _dl_fixup needs:
323
324    - $4 (a0) - link map address
325    - $5 (a1) - .rel.plt offset (== PLT entry index * 8)  */
326
327 asm ("\n\
328         .text\n\
329         .align  2\n\
330         .globl  _dl_runtime_pltresolve\n\
331         .type   _dl_runtime_pltresolve,@function\n\
332         .ent    _dl_runtime_pltresolve\n\
333 _dl_runtime_pltresolve:\n\
334         .frame  $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
335         .set noreorder\n\
336         # Save arguments and sp value in stack.\n\
337         " STRINGXP(PTR_SUBIU) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
338         " IFABIO32(STRINGXP(PTR_L) "    $13, " STRINGXP(PTRSIZE) "($28)") "\n\
339         " IFNEWABI(STRINGXP(PTR_L) "    $13, " STRINGXP(PTRSIZE) "($14)") "\n\
340         # Modify t9 ($25) so as to point .cpload instruction.\n\
341         " IFABIO32(STRINGXP(PTR_ADDIU) "        $25, 12\n") "\
342         # Compute GP.\n\
343         " STRINGXP(SETUP_GP) "\n\
344         " STRINGXV(SETUP_GP64 (0, _dl_runtime_pltresolve)) "\n\
345         .set reorder\n\
346         " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
347         " ELF_DL_PLT_SAVE_ARG_REGS "\
348         move    $4, $13\n\
349         sll     $5, $24, " STRINGXP(PTRLOG) " + 1\n\
350         jal     _dl_fixup\n\
351         move    $25, $2\n\
352         " ELF_DL_PLT_RESTORE_ARG_REGS "\
353         " STRINGXP(RESTORE_GP64) "\n\
354         " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
355         jr      $25\n\
356         .end    _dl_runtime_pltresolve\n\
357         .previous\n\
358 ");
359