packaing: switch off build_html
[platform/upstream/glibc.git] / sysdeps / mips / dl-trampoline.c
1 /* PLT trampoline.  MIPS version.
2    Copyright (C) 1996-2024 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library.  If not, see
17    <https://www.gnu.org/licenses/>.  */
18
19 /*  FIXME: Profiling of shared libraries is not implemented yet.  */
20
21 #include <sysdep.h>
22 #include <link.h>
23 #include <elf.h>
24 #include <ldsodefs.h>
25 #include <dl-machine.h>
26 #include <sysdep-cancel.h>
27
28 /* Get link map for callers object containing STUB_PC.  */
29 static inline struct link_map *
30 elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
31 {
32   extern int _dl_mips_gnu_objects;
33
34   /* got[1] is reserved to keep its link map address for the shared
35      object generated by the gnu linker.  If all are such objects, we
36      can find the link map from current GPREG simply.  If not so, get
37      the link map for caller's object containing STUB_PC.  */
38
39   if (_dl_mips_gnu_objects)
40     {
41       ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
42       ElfW(Word) g1;
43
44       g1 = ((ElfW(Word) *) got)[1];
45
46       if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
47         {
48           struct link_map *l =
49             (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK);
50           ElfW(Addr) base, limit;
51           const ElfW(Phdr) *p = l->l_phdr;
52           ElfW(Half) this, nent = l->l_phnum;
53
54           /* For the common case of a stub being called from the containing
55              object, STUB_PC will point to somewhere within the object that
56              is described by the link map fetched via got[1].  Otherwise we
57              have to scan all maps.  */
58           for (this = 0; this < nent; this++)
59             {
60               if (p[this].p_type == PT_LOAD)
61                 {
62                   base = p[this].p_vaddr + l->l_addr;
63                   limit = base + p[this].p_memsz;
64                   if (stub_pc >= base && stub_pc < limit)
65                     return l;
66                 }
67             }
68         }
69     }
70
71     struct link_map *l;
72     Lmid_t nsid;
73
74     for (nsid = 0; nsid < DL_NNS; ++nsid)
75       for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
76         {
77           ElfW(Addr) base, limit;
78           const ElfW(Phdr) *p = l->l_phdr;
79           ElfW(Half) this, nent = l->l_phnum;
80
81           for (this = 0; this < nent; ++this)
82             {
83               if (p[this].p_type == PT_LOAD)
84                 {
85                   base = p[this].p_vaddr + l->l_addr;
86                   limit = base + p[this].p_memsz;
87                   if (stub_pc >= base && stub_pc < limit)
88                     return l;
89                 }
90             }
91         }
92
93   _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
94   return NULL;
95 }
96
97 /* Define mips specific runtime resolver. The function __dl_runtime_resolve
98    is called from assembler function _dl_runtime_resolve which converts
99    special argument registers t7 ($15) and t8 ($24):
100      t7  address to return to the caller of the function
101      t8  index for this function symbol in .dynsym
102    to usual c arguments.
103
104    Other architectures call fixup from dl-runtime.c in
105    _dl_runtime_resolve.  MIPS instead calls __dl_runtime_resolve.  We
106    have to use our own version because of the way the got section is
107    treated on MIPS (we've also got ELF_MACHINE_PLT defined).  */
108
109 /* The flag _dl_mips_gnu_objects is set if all dynamic objects are
110    generated by the gnu linker. */
111 int _dl_mips_gnu_objects = 1;
112
113 /* This is called from assembly stubs below which the compiler can't see.  */
114 static ElfW(Addr)
115 __dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))
116                   __attribute_used__;
117
118 static ElfW(Addr)
119 __dl_runtime_resolve (ElfW(Word) sym_index,
120                       ElfW(Word) return_address,
121                       ElfW(Addr) old_gpreg,
122                       ElfW(Addr) stub_pc)
123 {
124   struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc);
125   const ElfW(Sym) *const symtab
126     = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]);
127   const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
128   ElfW(Addr) *got
129     = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
130   const ElfW(Word) local_gotno
131     = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
132   const ElfW(Word) gotsym
133     = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
134   const ElfW(Sym) *sym = &symtab[sym_index];
135   struct link_map *sym_map;
136   ElfW(Addr) value;
137
138   /* FIXME: The symbol versioning stuff is not tested yet.  */
139   if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
140     {
141       switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL ? 1 : 0)
142         {
143         default:
144           {
145             const ElfW(Half) *vernum =
146               (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
147             ElfW(Half) ndx = vernum[sym_index] & 0x7fff;
148             const struct r_found_version *version = &l->l_versions[ndx];
149
150             if (version->hash != 0)
151               {
152                 /* We need to keep the scope around so do some locking.  This is
153                    not necessary for objects which cannot be unloaded or when
154                    we are not using any threads (yet).  */
155                 if (!RTLD_SINGLE_THREAD_P)
156                   THREAD_GSCOPE_SET_FLAG ();
157
158                 sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l,
159                                                &sym, l->l_scope, version,
160                                                ELF_RTYPE_CLASS_PLT, 0, 0);
161
162                 /* We are done with the global scope.  */
163                 if (!RTLD_SINGLE_THREAD_P)
164                   THREAD_GSCOPE_RESET_FLAG ();
165
166                 break;
167               }
168           }
169           /* Fall through.  */
170         case 0:
171           {
172           /* We need to keep the scope around so do some locking.  This is
173              not necessary for objects which cannot be unloaded or when
174              we are not using any threads (yet).  */
175           int flags = DL_LOOKUP_ADD_DEPENDENCY;
176           if (!RTLD_SINGLE_THREAD_P)
177             {
178               THREAD_GSCOPE_SET_FLAG ();
179               flags |= DL_LOOKUP_GSCOPE_LOCK;
180             }
181
182           sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
183                                          l->l_scope, 0, ELF_RTYPE_CLASS_PLT,
184                                          flags, 0);
185
186           /* We are done with the global scope.  */
187           if (!RTLD_SINGLE_THREAD_P)
188             THREAD_GSCOPE_RESET_FLAG ();
189           }
190         }
191
192       /* Currently value contains the base load address of the object
193          that defines sym.  Now add in the symbol offset.  */
194       value = SYMBOL_ADDRESS (sym_map, sym, true);
195     }
196   else
197     /* We already found the symbol.  The module (and therefore its load
198        address) is also known.  */
199     value = SYMBOL_ADDRESS (l, sym, true);
200
201   /* Apply the relocation with that value.  */
202   *(got + local_gotno + sym_index - gotsym) = value;
203
204   return value;
205 }
206
207 #if _MIPS_SIM == _ABIO32
208 #define ELF_DL_FRAME_SIZE 40
209
210 #define ELF_DL_SAVE_ARG_REGS "\
211         sw      $15, 36($29)\n                                                \
212         sw      $4, 16($29)\n                                                 \
213         sw      $5, 20($29)\n                                                 \
214         sw      $6, 24($29)\n                                                 \
215         sw      $7, 28($29)\n                                                 \
216 "
217
218 #define ELF_DL_RESTORE_ARG_REGS "\
219         lw      $31, 36($29)\n                                                \
220         lw      $4, 16($29)\n                                                 \
221         lw      $5, 20($29)\n                                                 \
222         lw      $6, 24($29)\n                                                 \
223         lw      $7, 28($29)\n                                                 \
224 "
225
226 /* The PLT resolver should also save and restore $2 and $3, which are used
227    as arguments to MIPS16 stub functions.  */
228 #define ELF_DL_PLT_FRAME_SIZE 48
229
230 #define ELF_DL_PLT_SAVE_ARG_REGS \
231         ELF_DL_SAVE_ARG_REGS "\
232         sw      $2, 40($29)\n                                                 \
233         sw      $3, 44($29)\n                                                 \
234 "
235
236 #define ELF_DL_PLT_RESTORE_ARG_REGS \
237         ELF_DL_RESTORE_ARG_REGS "\
238         lw      $2, 40($29)\n                                                 \
239         lw      $3, 44($29)\n                                                 \
240 "
241
242 #define IFABIO32(X) X
243 #define IFNEWABI(X)
244
245 #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
246
247 #define ELF_DL_FRAME_SIZE 80
248
249 #define ELF_DL_SAVE_ARG_REGS "\
250         sd      $15, 72($29)\n                                                \
251         sd      $4, 8($29)\n                                                  \
252         sd      $5, 16($29)\n                                                 \
253         sd      $6, 24($29)\n                                                 \
254         sd      $7, 32($29)\n                                                 \
255         sd      $8, 40($29)\n                                                 \
256         sd      $9, 48($29)\n                                                 \
257         sd      $10, 56($29)\n                                                \
258         sd      $11, 64($29)\n                                                \
259 "
260
261 #define ELF_DL_RESTORE_ARG_REGS "\
262         ld      $31, 72($29)\n                                                \
263         ld      $4, 8($29)\n                                                  \
264         ld      $5, 16($29)\n                                                 \
265         ld      $6, 24($29)\n                                                 \
266         ld      $7, 32($29)\n                                                 \
267         ld      $8, 40($29)\n                                                 \
268         ld      $9, 48($29)\n                                                 \
269         ld      $10, 56($29)\n                                                \
270         ld      $11, 64($29)\n                                                \
271 "
272
273 /* The PLT resolver should also save and restore $2 and $3, which are used
274    as arguments to MIPS16 stub functions.  */
275 #define ELF_DL_PLT_FRAME_SIZE 96
276
277 #define ELF_DL_PLT_SAVE_ARG_REGS \
278         ELF_DL_SAVE_ARG_REGS "\
279         sd      $2, 80($29)\n                                                 \
280         sd      $3, 88($29)\n                                                 \
281 "
282
283 #define ELF_DL_PLT_RESTORE_ARG_REGS \
284         ELF_DL_RESTORE_ARG_REGS "\
285         ld      $2, 80($29)\n                                                 \
286         ld      $3, 88($29)\n                                                 \
287 "
288
289 #define IFABIO32(X)
290 #define IFNEWABI(X) X
291
292 #endif
293
294 #ifndef __mips16
295 asm ("\n\
296         .text\n\
297         .align  2\n\
298         .set    nomips16\n\
299         .globl  _dl_runtime_resolve\n\
300         .type   _dl_runtime_resolve,@function\n\
301         .ent    _dl_runtime_resolve\n\
302 _dl_runtime_resolve:\n\
303         .frame  $29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n\
304         .set noreorder\n\
305         # Save GP.\n\
306 1:      move    $3, $28\n\
307         # Save arguments and sp value in stack.\n\
308         " STRINGXP(PTR_SUBIU) "  $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
309         # Modify t9 ($25) so as to point .cpload instruction.\n\
310         " IFABIO32(STRINGXP(PTR_ADDIU) "        $25, (2f-1b)\n") "\
311         # Compute GP.\n\
312 2:      " STRINGXP(SETUP_GP) "\n\
313         " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\
314         .set reorder\n\
315         # Save slot call pc.\n\
316         move    $2, $31\n\
317         " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
318         " ELF_DL_SAVE_ARG_REGS "\
319         move    $4, $24\n\
320         move    $5, $15\n\
321         move    $6, $3\n\
322         move    $7, $2\n\
323         jal     __dl_runtime_resolve\n\
324         " ELF_DL_RESTORE_ARG_REGS "\
325         " STRINGXP(RESTORE_GP64) "\n\
326         " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
327         move    $25, $2\n\
328         jr      $25\n\
329         .end    _dl_runtime_resolve\n\
330         .previous\n\
331 ");
332
333 /* Assembler veneer called from the PLT header code when using PLTs.
334
335    Code in each PLT entry and the PLT header fills in the arguments to
336    this function:
337
338    - $15 (o32 t7, n32/n64 t3) - caller's return address
339    - $24 (t8) - PLT entry index
340    - $25 (t9) - address of _dl_runtime_pltresolve
341    - o32 $28 (gp), n32/n64 $14 (t2) - address of .got.plt
342
343    Different registers are used for .got.plt because the ABI was
344    originally designed for o32, where gp was available (call
345    clobbered).  On n32/n64 gp is call saved.
346
347    _dl_fixup needs:
348
349    - $4 (a0) - link map address
350    - $5 (a1) - .rel.plt offset (== PLT entry index * 8)  */
351
352 asm ("\n\
353         .text\n\
354         .align  2\n\
355         .set    nomips16\n\
356         .globl  _dl_runtime_pltresolve\n\
357         .type   _dl_runtime_pltresolve,@function\n\
358         .ent    _dl_runtime_pltresolve\n\
359 _dl_runtime_pltresolve:\n\
360         .frame  $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
361         .set noreorder\n\
362         # Save arguments and sp value in stack.\n\
363 1:      " STRINGXP(PTR_SUBIU) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
364         " IFABIO32(STRINGXP(PTR_L) "    $13, " STRINGXP(PTRSIZE) "($28)") "\n\
365         " IFNEWABI(STRINGXP(PTR_L) "    $13, " STRINGXP(PTRSIZE) "($14)") "\n\
366         # Modify t9 ($25) so as to point .cpload instruction.\n\
367         " IFABIO32(STRINGXP(PTR_ADDIU) "        $25, (2f-1b)\n") "\
368         # Compute GP.\n\
369 2:      " STRINGXP(SETUP_GP) "\n\
370         " STRINGXV(SETUP_GP64 (0, _dl_runtime_pltresolve)) "\n\
371         .set reorder\n\
372         " IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
373         " ELF_DL_PLT_SAVE_ARG_REGS "\
374         move    $4, $13\n\
375         sll     $5, $24, " STRINGXP(PTRLOG) " + 1\n\
376         jal     _dl_fixup\n\
377         move    $25, $2\n\
378         " ELF_DL_PLT_RESTORE_ARG_REGS "\
379         " STRINGXP(RESTORE_GP64) "\n\
380         " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
381         jr      $25\n\
382         .end    _dl_runtime_pltresolve\n\
383         .previous\n\
384 ");
385
386 #elif _MIPS_SIM == _ABIO32 /* __mips16 */
387 /* MIPS16 version, O32 only.  */
388 asm ("\n\
389         .text\n\
390         .align  2\n\
391         .set    mips16\n\
392         .globl  _dl_runtime_resolve\n\
393         .type   _dl_runtime_resolve,@function\n\
394         .ent    _dl_runtime_resolve\n\
395 _dl_runtime_resolve:\n\
396         .frame  $29, " STRINGXP (ELF_DL_FRAME_SIZE) ", $31\n\
397         # Save arguments and sp value in stack.\n\t"
398 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
399         "save   " STRINGXP (ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
400 # else
401         "addiu  $sp, -" STRINGXP (ELF_DL_FRAME_SIZE) "\n\
402         sw      $7, 32($sp)\n\
403         sw      $6, 28($sp)\n\
404         sw      $5, 24($sp)\n\
405         sw      $4, 20($sp)\n\t"
406 # endif
407         "# Preserve caller's $ra, for RESTORE instruction below.\n\
408         move    $5, $15\n\
409         sw      $5, 36($sp)\n\
410         # Compute GP into $2.\n\
411         li      $2, %hi(_gp_disp)\n\
412         addiu   $3, $pc, %lo(_gp_disp)\n\
413         sll     $2, 16\n\
414         addu    $2, $3\n\
415         lw      $3, %got(__dl_runtime_resolve)($2)\n\
416         move    $4, $24\n\
417         addiu   $3, %lo(__dl_runtime_resolve)\n\
418         move    $7, $ra\n\
419         move    $6, $28\n\
420         move    $25, $3\n\
421         jalr    $3\n\t"
422 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
423         "restore " STRINGXP(ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
424 # else
425         "# Restore $ra, move placed further down to hide latency.\n\
426         lw      $4, 36($sp)\n\
427         lw      $5, 24($sp)\n\
428         lw      $6, 28($sp)\n\
429         lw      $7, 32($sp)\n\
430         move    $ra, $4\n\
431         lw      $4, 20($sp)\n\
432         addiu   $sp, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\t"
433 # endif
434         "move   $25, $2\n\
435         jr      $2\n\
436         .end    _dl_runtime_resolve\n\
437         .previous\n\
438 ");
439
440 asm ("\n\
441         .text\n\
442         .align  2\n\
443         .set    mips16\n\
444         .globl  _dl_runtime_pltresolve\n\
445         .type   _dl_runtime_pltresolve,@function\n\
446         .ent    _dl_runtime_pltresolve\n\
447 _dl_runtime_pltresolve:\n\
448         .frame  $29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
449         # Save arguments and sp value in stack.\n\t"
450 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
451         "save   " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
452 # else
453         "addiu  $sp, -" STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
454         sw      $7, 40($sp)\n\
455         sw      $6, 36($sp)\n\
456         sw      $5, 32($sp)\n\
457         sw      $4, 28($sp)\n\t"
458 # endif
459         "# Preserve MIPS16 stub function arguments.\n\
460         sw      $3, 20($sp)\n\
461         sw      $2, 16($sp)\n\
462         # Preserve caller's $ra, for RESTORE instruction below.\n\
463         move    $3, $15\n\
464         sw      $3, 44($sp)\n\
465         # Compute GP into $2.\n\
466         li      $2, %hi(_gp_disp)\n\
467         addiu   $3, $pc, %lo(_gp_disp)\n\
468         sll     $2, 16\n\
469         addu    $2, $3\n\
470         # Save GP value in slot.\n\
471         sw      $2, 24($sp)\n\
472         # Load _dl_fixup address.\n\
473         lw      $6, %call16(_dl_fixup)($2)\n\
474         # Load link map address.\n\
475         move    $3, $28\n\
476         lw      $4, " STRINGXP (PTRSIZE) "($3)\n\
477         move    $5, $24\n\
478         sll     $5, " STRINGXP (PTRLOG) " + 1\n\
479         # Call _dl_fixup.\n\
480         move    $25, $6\n\
481         jalr    $6\n\
482         move    $25, $2\n\
483         # Reload GP value into $28.\n\
484         lw      $3, 24($sp)\n\
485         move    $28, $3\n\
486         lw      $3, 16($sp)\n\
487         move    $15, $3\n\
488         lw      $3, 20($sp)\n\t"
489 # if _MIPS_ISA >= _MIPS_ISA_MIPS32
490         "restore " STRINGXP (ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
491 # else
492         "# Restore $ra, move placed further down to hide latency.\n\
493         lw      $4, 44($sp)\n\
494         lw      $5, 32($sp)\n\
495         lw      $6, 36($sp)\n\
496         lw      $7, 40($sp)\n\
497         move    $ra, $4\n\
498         lw      $4, 28($sp)\n\
499         addiu   $sp, " STRINGXP (ELF_DL_PLT_FRAME_SIZE) "\n\t"
500 # endif
501         ".set   noreorder\n\
502         jr      $2\n\
503          move   $2, $15\n\
504         .set    reorder\n\
505         .end    _dl_runtime_pltresolve\n\
506         .previous\n\
507 ");
508
509 #else /* __mips16 && _MIPS_SIM != _ABIO32 */
510 # error "MIPS16 support for N32/N64 not implemented"
511
512 #endif /* __mips16 */