1 /* PLT trampoline.  MIPS version.
2    Copyright (C) 1996-2022 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 *
elf_machine_runtime_link_map(ElfW (Addr)gpreg,ElfW (Addr)stub_pc)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)
__dl_runtime_resolve(ElfW (Word)sym_index,ElfW (Word)return_address,ElfW (Addr)old_gpreg,ElfW (Addr)stub_pc)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 */
513