1 /* Machine-dependent ELF dynamic relocation inline functions. MIPS version.
2 Copyright (C) 1996-2021 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 #ifndef dl_machine_h
21 #define dl_machine_h
22
23 #define ELF_MACHINE_NAME "MIPS"
24
25 #include <entry.h>
26
27 #ifndef ENTRY_POINT
28 #error ENTRY_POINT needs to be defined for MIPS.
29 #endif
30
31 #include <sgidefs.h>
32 #include <sysdep.h>
33 #include <sys/asm.h>
34 #include <dl-tls.h>
35 #include <dl-static-tls.h>
36 #include <dl-machine-rel.h>
37
38 /* The offset of gp from GOT might be system-dependent. It's set by
39 ld. The same value is also */
40 #define OFFSET_GP_GOT 0x7ff0
41
42 #ifndef _RTLD_PROLOGUE
43 # define _RTLD_PROLOGUE(entry) \
44 ".globl\t" __STRING(entry) "\n\t" \
45 ".ent\t" __STRING(entry) "\n\t" \
46 ".type\t" __STRING(entry) ", @function\n" \
47 __STRING(entry) ":\n\t"
48 #endif
49
50 #ifndef _RTLD_EPILOGUE
51 # define _RTLD_EPILOGUE(entry) \
52 ".end\t" __STRING(entry) "\n\t" \
53 ".size\t" __STRING(entry) ", . - " __STRING(entry) "\n\t"
54 #endif
55
56 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.
57 This only makes sense on MIPS when using PLTs, so choose the
58 PLT relocation (not encountered when not using PLTs). */
59 #define ELF_MACHINE_JMP_SLOT R_MIPS_JUMP_SLOT
60 #define elf_machine_type_class(type) \
61 ((((type) == ELF_MACHINE_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \
62 | (((type) == R_MIPS_COPY) * ELF_RTYPE_CLASS_COPY))
63
64 /* Translate a processor specific dynamic tag to the index
65 in l_info array. */
66 #define DT_MIPS(x) (DT_MIPS_##x - DT_LOPROC + DT_NUM)
67
68 /* If there is a DT_MIPS_RLD_MAP_REL or DT_MIPS_RLD_MAP entry in the dynamic
69 section, fill in the debug map pointer with the run-time address of the
70 r_debug structure. */
71 #define ELF_MACHINE_DEBUG_SETUP(l,r) \
72 do { if ((l)->l_info[DT_MIPS (RLD_MAP_REL)]) \
73 { \
74 char *ptr = (char *)(l)->l_info[DT_MIPS (RLD_MAP_REL)]; \
75 ptr += (l)->l_info[DT_MIPS (RLD_MAP_REL)]->d_un.d_val; \
76 *(ElfW(Addr) *)ptr = (ElfW(Addr)) (r); \
77 } \
78 else if ((l)->l_info[DT_MIPS (RLD_MAP)]) \
79 *(ElfW(Addr) *)((l)->l_info[DT_MIPS (RLD_MAP)]->d_un.d_ptr) = \
80 (ElfW(Addr)) (r); \
81 } while (0)
82
83 #if ((defined __mips_nan2008 && !defined HAVE_MIPS_NAN2008) \
84 || (!defined __mips_nan2008 && defined HAVE_MIPS_NAN2008))
85 # error "Configuration inconsistency: __mips_nan2008 != HAVE_MIPS_NAN2008, overridden CFLAGS?"
86 #endif
87 #ifdef __mips_nan2008
88 # define ELF_MACHINE_NAN2008 EF_MIPS_NAN2008
89 #else
90 # define ELF_MACHINE_NAN2008 0
91 #endif
92
93 /* Return nonzero iff ELF header is compatible with the running host. */
94 static inline int __attribute_used__
elf_machine_matches_host(const ElfW (Ehdr)* ehdr)95 elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
96 {
97 #if _MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIN32
98 /* Don't link o32 and n32 together. */
99 if (((ehdr->e_flags & EF_MIPS_ABI2) != 0) != (_MIPS_SIM == _ABIN32))
100 return 0;
101 #endif
102
103 /* Don't link 2008-NaN and legacy-NaN objects together. */
104 if ((ehdr->e_flags & EF_MIPS_NAN2008) != ELF_MACHINE_NAN2008)
105 return 0;
106
107 /* Ensure that the old O32 FP64 ABI is never loaded, it is not supported
108 on linux. */
109 if (ehdr->e_flags & EF_MIPS_FP64)
110 return 0;
111
112 switch (ehdr->e_machine)
113 {
114 case EM_MIPS:
115 case EM_MIPS_RS3_LE:
116 return 1;
117 default:
118 return 0;
119 }
120 }
121
ElfW(Addr)122 static inline ElfW(Addr) *
123 elf_mips_got_from_gpreg (ElfW(Addr) gpreg)
124 {
125 /* FIXME: the offset of gp from GOT may be system-dependent. */
126 return (ElfW(Addr) *) (gpreg - OFFSET_GP_GOT);
127 }
128
129 /* Return the link-time address of _DYNAMIC. Conveniently, this is the
130 first element of the GOT. This must be inlined in a function which
131 uses global data. We assume its $gp points to the primary GOT. */
132 static inline ElfW(Addr)
elf_machine_dynamic(void)133 elf_machine_dynamic (void)
134 {
135 register ElfW(Addr) gp __asm__ ("$28");
136 return *elf_mips_got_from_gpreg (gp);
137 }
138
139 #define STRINGXP(X) __STRING(X)
140 #define STRINGXV(X) STRINGV_(X)
141 #define STRINGV_(...) # __VA_ARGS__
142
143 /* Return the run-time load address of the shared object. */
144 static inline ElfW(Addr)
elf_machine_load_address(void)145 elf_machine_load_address (void)
146 {
147 ElfW(Addr) addr;
148 #ifndef __mips16
149 asm (" .set noreorder\n"
150 " " STRINGXP (PTR_LA) " %0, 0f\n"
151 # if !defined __mips_isa_rev || __mips_isa_rev < 6
152 " bltzal $0, 0f\n"
153 " nop\n"
154 "0: " STRINGXP (PTR_SUBU) " %0, $31, %0\n"
155 # else
156 "0: addiupc $31, 0\n"
157 " " STRINGXP (PTR_SUBU) " %0, $31, %0\n"
158 # endif
159 " .set reorder\n"
160 : "=r" (addr)
161 : /* No inputs */
162 : "$31");
163 #else
164 ElfW(Addr) tmp;
165 asm (" .set noreorder\n"
166 " move %1,$gp\n"
167 " lw %1,%%got(0f)(%1)\n"
168 "0: .fill 0\n" /* Clear the ISA bit on 0:. */
169 " la %0,0b\n"
170 " addiu %1,%%lo(0b)\n"
171 " subu %0,%1\n"
172 " .set reorder\n"
173 : "=d" (addr), "=d" (tmp)
174 : /* No inputs */);
175 #endif
176 return addr;
177 }
178
179 /* The MSB of got[1] of a gnu object is set to identify gnu objects. */
180 #if _MIPS_SIM == _ABI64
181 # define ELF_MIPS_GNU_GOT1_MASK 0x8000000000000000L
182 #else
183 # define ELF_MIPS_GNU_GOT1_MASK 0x80000000L
184 #endif
185
186 /* We can't rely on elf_machine_got_rel because _dl_object_relocation_scope
187 fiddles with global data. */
188 #define ELF_MACHINE_BEFORE_RTLD_RELOC(bootstrap_map, dynamic_info) \
189 do { \
190 struct link_map *map = bootstrap_map; \
191 ElfW(Sym) *sym; \
192 ElfW(Addr) *got; \
193 int i, n; \
194 \
195 got = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]); \
196 \
197 if (__builtin_expect (map->l_addr == 0, 1)) \
198 break; \
199 \
200 /* got[0] is reserved. got[1] is also reserved for the dynamic object \
201 generated by gnu ld. Skip these reserved entries from \
202 relocation. */ \
203 i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1; \
204 n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; \
205 \
206 /* Add the run-time displacement to all local got entries. */ \
207 while (i < n) \
208 got[i++] += map->l_addr; \
209 \
210 /* Handle global got entries. */ \
211 got += n; \
212 sym = (ElfW(Sym) *) D_PTR(map, l_info[DT_SYMTAB]) \
213 + map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val; \
214 i = (map->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val \
215 - map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val); \
216 \
217 while (i--) \
218 { \
219 if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON) \
220 *got = SYMBOL_ADDRESS (map, sym, true); \
221 else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC \
222 && *got != sym->st_value) \
223 *got += map->l_addr; \
224 else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION) \
225 { \
226 if (sym->st_other == 0) \
227 *got += map->l_addr; \
228 } \
229 else \
230 *got = SYMBOL_ADDRESS (map, sym, true); \
231 \
232 got++; \
233 sym++; \
234 } \
235 } while(0)
236
237
238 /* Mask identifying addresses reserved for the user program,
239 where the dynamic linker should not map anything. */
240 #define ELF_MACHINE_USER_ADDRESS_MASK 0x80000000UL
241
242
243 /* Initial entry point code for the dynamic linker.
244 The C function `_dl_start' is the real entry point;
245 its return value is the user program's entry point.
246 Note how we have to be careful about two things:
247
248 1) That we allocate a minimal stack of 24 bytes for
249 every function call, the MIPS ABI states that even
250 if all arguments are passed in registers the procedure
251 called can use the 16 byte area pointed to by $sp
252 when it is called to store away the arguments passed
253 to it.
254
255 2) That under Unix the entry is named __start
256 and not just plain _start. */
257
258 #ifndef __mips16
259 # if !defined __mips_isa_rev || __mips_isa_rev < 6
260 # define LCOFF STRINGXP(.Lcof2)
261 # define LOAD_31 STRINGXP(bltzal $8) "," STRINGXP(.Lcof2)
262 # else
263 # define LCOFF STRINGXP(.Lcof1)
264 # define LOAD_31 "addiupc $31, 0"
265 # endif
266 # define RTLD_START asm (\
267 ".text\n\
268 " _RTLD_PROLOGUE(ENTRY_POINT) "\
269 " STRINGXV(SETUP_GPX($25)) "\n\
270 " STRINGXV(SETUP_GPX64($18,$25)) "\n\
271 # i386 ABI book says that the first entry of GOT holds\n\
272 # the address of the dynamic structure. Though MIPS ABI\n\
273 # doesn't say nothing about this, I emulate this here.\n\
274 " STRINGXP(PTR_LA) " $4, _DYNAMIC\n\
275 # Subtract OFFSET_GP_GOT\n\
276 " STRINGXP(PTR_S) " $4, -0x7ff0($28)\n\
277 move $4, $29\n\
278 " STRINGXP(PTR_SUBIU) " $29, 16\n\
279 \n\
280 " STRINGXP(PTR_LA) " $8, " LCOFF "\n\
281 .Lcof1: " LOAD_31 "\n\
282 .Lcof2: " STRINGXP(PTR_SUBU) " $8, $31, $8\n\
283 \n\
284 " STRINGXP(PTR_LA) " $25, _dl_start\n\
285 " STRINGXP(PTR_ADDU) " $25, $8\n\
286 jalr $25\n\
287 \n\
288 " STRINGXP(PTR_ADDIU) " $29, 16\n\
289 # Get the value of label '_dl_start_user' in t9 ($25).\n\
290 " STRINGXP(PTR_LA) " $25, _dl_start_user\n\
291 " _RTLD_EPILOGUE(ENTRY_POINT) "\
292 \n\
293 \n\
294 " _RTLD_PROLOGUE(_dl_start_user) "\
295 " STRINGXP(SETUP_GP) "\n\
296 " STRINGXV(SETUP_GP64($18,_dl_start_user)) "\n\
297 move $16, $28\n\
298 # Save the user entry point address in a saved register.\n\
299 move $17, $2\n\
300 # See if we were run as a command with the executable file\n\
301 # name as an extra leading argument.\n\
302 lw $2, _dl_skip_args\n\
303 beq $2, $0, 1f\n\
304 # Load the original argument count.\n\
305 " STRINGXP(PTR_L) " $4, 0($29)\n\
306 # Subtract _dl_skip_args from it.\n\
307 subu $4, $2\n\
308 # Adjust the stack pointer to skip _dl_skip_args words.\n\
309 sll $2, " STRINGXP (PTRLOG) "\n\
310 " STRINGXP(PTR_ADDU) " $29, $2\n\
311 # Save back the modified argument count.\n\
312 " STRINGXP(PTR_S) " $4, 0($29)\n\
313 1: # Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
314 " STRINGXP(PTR_L) " $4, _rtld_local\n\
315 " STRINGXP(PTR_L) /* or lw??? fixme */ " $5, 0($29)\n\
316 " STRINGXP(PTR_LA) " $6, " STRINGXP (PTRSIZE) "($29)\n\
317 sll $7, $5, " STRINGXP (PTRLOG) "\n\
318 " STRINGXP(PTR_ADDU) " $7, $7, $6\n\
319 " STRINGXP(PTR_ADDU) " $7, $7, " STRINGXP (PTRSIZE) " \n\
320 # Make sure the stack pointer is aligned for _dl_init.\n\
321 and $2, $29, -2 * " STRINGXP(SZREG) "\n\
322 move $8, $29\n\
323 " STRINGXP(PTR_SUBIU) " $29, $2, 32\n\
324 " STRINGXP(PTR_S) " $8, (32 - " STRINGXP(SZREG) ")($29)\n\
325 " STRINGXP(SAVE_GP(16)) "\n\
326 # Call the function to run the initializers.\n\
327 jal _dl_init\n\
328 # Restore the stack pointer for _start.\n\
329 " STRINGXP(PTR_L) " $29, (32 - " STRINGXP(SZREG) ")($29)\n\
330 # Pass our finalizer function to the user in $2 as per ELF ABI.\n\
331 " STRINGXP(PTR_LA) " $2, _dl_fini\n\
332 # Jump to the user entry point.\n\
333 move $25, $17\n\
334 jr $25\n\t"\
335 _RTLD_EPILOGUE(_dl_start_user)\
336 ".previous"\
337 );
338
339 #else /* __mips16 */
340 /* MIPS16 version. We currently only support O32 under MIPS16; the proper
341 assembly preprocessor abstractions will need to be added if other ABIs
342 are to be supported. */
343
344 # define RTLD_START asm (\
345 ".text\n\
346 .set mips16\n\
347 " _RTLD_PROLOGUE (ENTRY_POINT) "\
348 # Construct GP value in $3.\n\
349 li $3, %hi(_gp_disp)\n\
350 addiu $4, $pc, %lo(_gp_disp)\n\
351 sll $3, 16\n\
352 addu $3, $4\n\
353 move $28, $3\n\
354 lw $4, %got(_DYNAMIC)($3)\n\
355 sw $4, -0x7ff0($3)\n\
356 move $4, $sp\n\
357 addiu $sp, -16\n\
358 # _dl_start() is sufficiently near to use pc-relative\n\
359 # load address.\n\
360 la $3, _dl_start\n\
361 move $25, $3\n\
362 jalr $3\n\
363 addiu $sp, 16\n\
364 " _RTLD_EPILOGUE (ENTRY_POINT) "\
365 \n\
366 \n\
367 " _RTLD_PROLOGUE (_dl_start_user) "\
368 li $16, %hi(_gp_disp)\n\
369 addiu $4, $pc, %lo(_gp_disp)\n\
370 sll $16, 16\n\
371 addu $16, $4\n\
372 move $17, $2\n\
373 move $28, $16\n\
374 lw $4, %got(_dl_skip_args)($16)\n\
375 lw $4, 0($4)\n\
376 beqz $4, 1f\n\
377 # Load the original argument count.\n\
378 lw $5, 0($sp)\n\
379 # Subtract _dl_skip_args from it.\n\
380 subu $5, $4\n\
381 # Adjust the stack pointer to skip _dl_skip_args words.\n\
382 sll $4, " STRINGXP (PTRLOG) "\n\
383 move $6, $sp\n\
384 addu $6, $4\n\
385 move $sp, $6\n\
386 # Save back the modified argument count.\n\
387 sw $5, 0($sp)\n\
388 1: # Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
389 lw $4, %got(_rtld_local)($16)\n\
390 lw $4, 0($4)\n\
391 lw $5, 0($sp)\n\
392 addiu $6, $sp, " STRINGXP (PTRSIZE) "\n\
393 sll $7, $5, " STRINGXP (PTRLOG) "\n\
394 addu $7, $6\n\
395 addu $7, " STRINGXP (PTRSIZE) "\n\
396 # Make sure the stack pointer is aligned for _dl_init.\n\
397 li $2, 2 * " STRINGXP (SZREG) "\n\
398 neg $2, $2\n\
399 move $3, $sp\n\
400 and $2, $3\n\
401 sw $3, -" STRINGXP (SZREG) "($2)\n\
402 addiu $2, -32\n\
403 move $sp, $2\n\
404 sw $16, 16($sp)\n\
405 # Call the function to run the initializers.\n\
406 lw $2, %call16(_dl_init)($16)\n\
407 move $25, $2\n\
408 jalr $2\n\
409 # Restore the stack pointer for _start.\n\
410 lw $2, 32-" STRINGXP (SZREG) "($sp)\n\
411 move $sp, $2\n\
412 move $28, $16\n\
413 # Pass our finalizer function to the user in $2 as per ELF ABI.\n\
414 lw $2, %call16(_dl_fini)($16)\n\
415 # Jump to the user entry point.\n\
416 move $25, $17\n\
417 jr $17\n\t"\
418 _RTLD_EPILOGUE (_dl_start_user)\
419 ".previous"\
420 );
421
422 #endif /* __mips16 */
423
424 /* Names of the architecture-specific auditing callback functions. */
425 # if _MIPS_SIM == _ABIO32
426 # define ARCH_LA_PLTENTER mips_o32_gnu_pltenter
427 # define ARCH_LA_PLTEXIT mips_o32_gnu_pltexit
428 # elif _MIPS_SIM == _ABIN32
429 # define ARCH_LA_PLTENTER mips_n32_gnu_pltenter
430 # define ARCH_LA_PLTEXIT mips_n32_gnu_pltexit
431 # else
432 # define ARCH_LA_PLTENTER mips_n64_gnu_pltenter
433 # define ARCH_LA_PLTEXIT mips_n64_gnu_pltexit
434 # endif
435
436 /* We define an initialization function. This is called very early in
437 _dl_sysdep_start. */
438 #define DL_PLATFORM_INIT dl_platform_init ()
439
440 static inline void __attribute__ ((unused))
dl_platform_init(void)441 dl_platform_init (void)
442 {
443 if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
444 /* Avoid an empty string which would disturb us. */
445 GLRO(dl_platform) = NULL;
446 }
447
448 /* For a non-writable PLT, rewrite the .got.plt entry at RELOC_ADDR to
449 point at the symbol with address VALUE. For a writable PLT, rewrite
450 the corresponding PLT entry instead. */
451 static inline ElfW(Addr)
elf_machine_fixup_plt(struct link_map * map,lookup_t t,const ElfW (Sym)* refsym,const ElfW (Sym)* sym,const ElfW (Rel)* reloc,ElfW (Addr)* reloc_addr,ElfW (Addr)value)452 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
453 const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
454 const ElfW(Rel) *reloc,
455 ElfW(Addr) *reloc_addr, ElfW(Addr) value)
456 {
457 return *reloc_addr = value;
458 }
459
460 static inline ElfW(Addr)
elf_machine_plt_value(struct link_map * map,const ElfW (Rel)* reloc,ElfW (Addr)value)461 elf_machine_plt_value (struct link_map *map, const ElfW(Rel) *reloc,
462 ElfW(Addr) value)
463 {
464 return value;
465 }
466
467 #endif /* !dl_machine_h */
468
469 #ifdef RESOLVE_MAP
470
471 /* Perform a relocation described by R_INFO at the location pointed to
472 by RELOC_ADDR. SYM is the relocation symbol specified by R_INFO and
473 MAP is the object containing the reloc. */
474
475 static inline void
476 __attribute__ ((always_inline))
elf_machine_reloc(struct link_map * map,struct r_scope_elem * scope[],ElfW (Addr)r_info,const ElfW (Sym)* sym,const struct r_found_version * version,void * reloc_addr,ElfW (Addr)r_addend,int inplace_p)477 elf_machine_reloc (struct link_map *map, struct r_scope_elem *scope[],
478 ElfW(Addr) r_info, const ElfW(Sym) *sym,
479 const struct r_found_version *version, void *reloc_addr,
480 ElfW(Addr) r_addend, int inplace_p)
481 {
482 const unsigned long int r_type = ELFW(R_TYPE) (r_info);
483 ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
484
485 #if !defined RTLD_BOOTSTRAP && !defined SHARED
486 /* This is defined in rtld.c, but nowhere in the static libc.a;
487 make the reference weak so static programs can still link. This
488 declaration cannot be done when compiling rtld.c (i.e. #ifdef
489 RTLD_BOOTSTRAP) because rtld.c contains the common defn for
490 _dl_rtld_map, which is incompatible with a weak decl in the same
491 file. */
492 weak_extern (GL(dl_rtld_map));
493 #endif
494
495 switch (r_type)
496 {
497 #if !defined (RTLD_BOOTSTRAP)
498 # if _MIPS_SIM == _ABI64
499 case R_MIPS_TLS_DTPMOD64:
500 case R_MIPS_TLS_DTPREL64:
501 case R_MIPS_TLS_TPREL64:
502 # else
503 case R_MIPS_TLS_DTPMOD32:
504 case R_MIPS_TLS_DTPREL32:
505 case R_MIPS_TLS_TPREL32:
506 # endif
507 {
508 struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
509 r_type);
510
511 switch (r_type)
512 {
513 case R_MIPS_TLS_DTPMOD64:
514 case R_MIPS_TLS_DTPMOD32:
515 if (sym_map)
516 *addr_field = sym_map->l_tls_modid;
517 break;
518
519 case R_MIPS_TLS_DTPREL64:
520 case R_MIPS_TLS_DTPREL32:
521 if (sym)
522 {
523 if (inplace_p)
524 r_addend = *addr_field;
525 *addr_field = r_addend + TLS_DTPREL_VALUE (sym);
526 }
527 break;
528
529 case R_MIPS_TLS_TPREL32:
530 case R_MIPS_TLS_TPREL64:
531 if (sym)
532 {
533 CHECK_STATIC_TLS (map, sym_map);
534 if (inplace_p)
535 r_addend = *addr_field;
536 *addr_field = r_addend + TLS_TPREL_VALUE (sym_map, sym);
537 }
538 break;
539 }
540
541 break;
542 }
543 #endif
544
545 #if _MIPS_SIM == _ABI64
546 case (R_MIPS_64 << 8) | R_MIPS_REL32:
547 #else
548 case R_MIPS_REL32:
549 #endif
550 {
551 int symidx = ELFW(R_SYM) (r_info);
552 ElfW(Addr) reloc_value;
553
554 if (inplace_p)
555 /* Support relocations on mis-aligned offsets. */
556 __builtin_memcpy (&reloc_value, reloc_addr, sizeof (reloc_value));
557 else
558 reloc_value = r_addend;
559
560 if (symidx)
561 {
562 const ElfW(Word) gotsym
563 = (const ElfW(Word)) map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
564
565 if ((ElfW(Word))symidx < gotsym)
566 {
567 /* This wouldn't work for a symbol imported from other
568 libraries for which there's no GOT entry, but MIPS
569 requires every symbol referenced in a dynamic
570 relocation to have a GOT entry in the primary GOT,
571 so we only get here for locally-defined symbols.
572 For section symbols, we should *NOT* be adding
573 sym->st_value (per the definition of the meaning of
574 S in reloc expressions in the ELF64 MIPS ABI),
575 since it should have already been added to
576 reloc_value by the linker, but older versions of
577 GNU ld didn't add it, and newer versions don't emit
578 useless relocations to section symbols any more, so
579 it is safe to keep on adding sym->st_value, even
580 though it's not ABI compliant. Some day we should
581 bite the bullet and stop doing this. */
582 #ifndef RTLD_BOOTSTRAP
583 if (map != &GL(dl_rtld_map))
584 #endif
585 reloc_value += SYMBOL_ADDRESS (map, sym, true);
586 }
587 else
588 {
589 #ifndef RTLD_BOOTSTRAP
590 const ElfW(Addr) *got
591 = (const ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
592 const ElfW(Word) local_gotno
593 = (const ElfW(Word))
594 map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
595
596 reloc_value += got[symidx + local_gotno - gotsym];
597 #endif
598 }
599 }
600 else
601 #ifndef RTLD_BOOTSTRAP
602 if (map != &GL(dl_rtld_map))
603 #endif
604 reloc_value += map->l_addr;
605
606 __builtin_memcpy (reloc_addr, &reloc_value, sizeof (reloc_value));
607 }
608 break;
609 #ifndef RTLD_BOOTSTRAP
610 #if _MIPS_SIM == _ABI64
611 case (R_MIPS_64 << 8) | R_MIPS_GLOB_DAT:
612 #else
613 case R_MIPS_GLOB_DAT:
614 #endif
615 {
616 int symidx = ELFW(R_SYM) (r_info);
617 const ElfW(Word) gotsym
618 = (const ElfW(Word)) map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
619
620 if (__builtin_expect ((ElfW(Word)) symidx >= gotsym, 1))
621 {
622 const ElfW(Addr) *got
623 = (const ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
624 const ElfW(Word) local_gotno
625 = ((const ElfW(Word))
626 map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val);
627
628 ElfW(Addr) reloc_value = got[symidx + local_gotno - gotsym];
629 __builtin_memcpy (reloc_addr, &reloc_value, sizeof (reloc_value));
630 }
631 }
632 break;
633 #endif
634 case R_MIPS_NONE: /* Alright, Wilbur. */
635 break;
636
637 case R_MIPS_JUMP_SLOT:
638 {
639 struct link_map *sym_map;
640 ElfW(Addr) value;
641
642 /* The addend for a jump slot relocation must always be zero:
643 calls via the PLT always branch to the symbol's address and
644 not to the address plus a non-zero offset. */
645 if (r_addend != 0)
646 _dl_signal_error (0, map->l_name, NULL,
647 "found jump slot relocation with non-zero addend");
648
649 sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
650 value = SYMBOL_ADDRESS (sym_map, sym, true);
651 *addr_field = value;
652
653 break;
654 }
655
656 case R_MIPS_COPY:
657 {
658 const ElfW(Sym) *const refsym = sym;
659 struct link_map *sym_map;
660 ElfW(Addr) value;
661
662 /* Calculate the address of the symbol. */
663 sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
664 value = SYMBOL_ADDRESS (sym_map, sym, true);
665
666 if (__builtin_expect (sym == NULL, 0))
667 /* This can happen in trace mode if an object could not be
668 found. */
669 break;
670 if (__builtin_expect (sym->st_size > refsym->st_size, 0)
671 || (__builtin_expect (sym->st_size < refsym->st_size, 0)
672 && GLRO(dl_verbose)))
673 {
674 const char *strtab;
675
676 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
677 _dl_error_printf ("\
678 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
679 RTLD_PROGNAME, strtab + refsym->st_name);
680 }
681 memcpy (reloc_addr, (void *) value,
682 sym->st_size < refsym->st_size
683 ? sym->st_size : refsym->st_size);
684 break;
685 }
686
687 #if _MIPS_SIM == _ABI64
688 case R_MIPS_64:
689 /* For full compliance with the ELF64 ABI, one must precede the
690 _REL32/_64 pair of relocations with a _64 relocation, such
691 that the in-place addend is read as a 64-bit value. IRIX
692 didn't pick up on this requirement, so we treat the
693 _REL32/_64 relocation as a 64-bit relocation even if it's by
694 itself. For ABI compliance, we ignore such _64 dummy
695 relocations. For RELA, this may be simply removed, since
696 it's totally unnecessary. */
697 if (ELFW(R_SYM) (r_info) == 0)
698 break;
699 #endif
700 /* Fall through. */
701 default:
702 _dl_reloc_bad_type (map, r_type, 0);
703 break;
704 }
705 }
706
707 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
708 MAP is the object containing the reloc. */
709
710 static inline void
711 __attribute__ ((always_inline))
elf_machine_rel(struct link_map * map,struct r_scope_elem * scope[],const ElfW (Rel)* reloc,const ElfW (Sym)* sym,const struct r_found_version * version,void * const reloc_addr,int skip_ifunc)712 elf_machine_rel (struct link_map *map, struct r_scope_elem *scope[],
713 const ElfW(Rel) *reloc, const ElfW(Sym) *sym,
714 const struct r_found_version *version, void *const reloc_addr,
715 int skip_ifunc)
716 {
717 elf_machine_reloc (map, scope, reloc->r_info, sym, version, reloc_addr, 0, 1);
718 }
719
720 static inline void
721 __attribute__((always_inline))
elf_machine_rel_relative(ElfW (Addr)l_addr,const ElfW (Rel)* reloc,void * const reloc_addr)722 elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
723 void *const reloc_addr)
724 {
725 /* XXX Nothing to do. There is no relative relocation, right? */
726 }
727
728 static inline void
729 __attribute__((always_inline))
elf_machine_lazy_rel(struct link_map * map,struct r_scope_elem * scope[],ElfW (Addr)l_addr,const ElfW (Rel)* reloc,int skip_ifunc)730 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
731 ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
732 int skip_ifunc)
733 {
734 ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
735 const unsigned int r_type = ELFW(R_TYPE) (reloc->r_info);
736 /* Check for unexpected PLT reloc type. */
737 if (__builtin_expect (r_type == R_MIPS_JUMP_SLOT, 1))
738 {
739 if (__builtin_expect (map->l_mach.plt, 0) == 0)
740 {
741 /* Nothing is required here since we only support lazy
742 relocation in executables. */
743 }
744 else
745 *reloc_addr = map->l_mach.plt;
746 }
747 else
748 _dl_reloc_bad_type (map, r_type, 1);
749 }
750
751 static inline void
752 __attribute__ ((always_inline))
elf_machine_rela(struct link_map * map,struct r_scope_elem * scope[],const ElfW (Rela)* reloc,const ElfW (Sym)* sym,const struct r_found_version * version,void * const reloc_addr,int skip_ifunc)753 elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[], const ElfW(Rela) *reloc,
754 const ElfW(Sym) *sym, const struct r_found_version *version,
755 void *const reloc_addr, int skip_ifunc)
756 {
757 elf_machine_reloc (map, scope, reloc->r_info, sym, version, reloc_addr,
758 reloc->r_addend, 0);
759 }
760
761 static inline void
762 __attribute__((always_inline))
elf_machine_rela_relative(ElfW (Addr)l_addr,const ElfW (Rela)* reloc,void * const reloc_addr)763 elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
764 void *const reloc_addr)
765 {
766 }
767
768 #ifndef RTLD_BOOTSTRAP
769 /* Relocate GOT. */
770 static inline void
771 __attribute__((always_inline))
elf_machine_got_rel(struct link_map * map,struct r_scope_elem * scope[],int lazy)772 elf_machine_got_rel (struct link_map *map, struct r_scope_elem *scope[], int lazy)
773 {
774 ElfW(Addr) *got;
775 ElfW(Sym) *sym;
776 const ElfW(Half) *vernum;
777 int i, n, symidx;
778
779 #define RESOLVE_GOTSYM(sym,vernum,sym_index,reloc) \
780 ({ \
781 const ElfW(Sym) *ref = sym; \
782 const struct r_found_version *version __attribute__ ((unused)) \
783 = vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL; \
784 struct link_map *sym_map; \
785 sym_map = RESOLVE_MAP (map, scope, &ref, version, reloc); \
786 SYMBOL_ADDRESS (sym_map, ref, true); \
787 })
788
789 if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
790 vernum = (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
791 else
792 vernum = NULL;
793
794 got = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
795
796 n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
797 /* The dynamic linker's local got entries have already been relocated. */
798 if (map != &GL(dl_rtld_map))
799 {
800 /* got[0] is reserved. got[1] is also reserved for the dynamic object
801 generated by gnu ld. Skip these reserved entries from relocation. */
802 i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1;
803
804 /* Add the run-time displacement to all local got entries if
805 needed. */
806 if (__builtin_expect (map->l_addr != 0, 0))
807 {
808 while (i < n)
809 got[i++] += map->l_addr;
810 }
811 }
812
813 /* Handle global got entries. */
814 got += n;
815 /* Keep track of the symbol index. */
816 symidx = map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
817 sym = (ElfW(Sym) *) D_PTR (map, l_info[DT_SYMTAB]) + symidx;
818 i = (map->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val
819 - map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val);
820
821 /* This loop doesn't handle Quickstart. */
822 while (i--)
823 {
824 if (sym->st_shndx == SHN_UNDEF)
825 {
826 if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC && sym->st_value
827 && !(sym->st_other & STO_MIPS_PLT))
828 {
829 if (lazy)
830 *got = SYMBOL_ADDRESS (map, sym, true);
831 else
832 /* This is a lazy-binding stub, so we don't need the
833 canonical address. */
834 *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_JUMP_SLOT);
835 }
836 else
837 *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
838 }
839 else if (sym->st_shndx == SHN_COMMON)
840 *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
841 else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
842 && *got != sym->st_value)
843 {
844 if (lazy)
845 *got += map->l_addr;
846 else
847 /* This is a lazy-binding stub, so we don't need the
848 canonical address. */
849 *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_JUMP_SLOT);
850 }
851 else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)
852 {
853 if (sym->st_other == 0)
854 *got += map->l_addr;
855 }
856 else
857 *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
858
859 ++got;
860 ++sym;
861 ++symidx;
862 }
863
864 #undef RESOLVE_GOTSYM
865 }
866 #endif
867
868 /* Set up the loaded object described by L so its stub function
869 will jump to the on-demand fixup code __dl_runtime_resolve. */
870
871 static inline int
872 __attribute__((always_inline))
elf_machine_runtime_setup(struct link_map * l,struct r_scope_elem * scope[],int lazy,int profile)873 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
874 int lazy, int profile)
875 {
876 # ifndef RTLD_BOOTSTRAP
877 ElfW(Addr) *got;
878 extern void _dl_runtime_resolve (ElfW(Word));
879 extern void _dl_runtime_pltresolve (void);
880 extern int _dl_mips_gnu_objects;
881
882 if (lazy)
883 {
884 /* The GOT entries for functions have not yet been filled in.
885 Their initial contents will arrange when called to put an
886 offset into the .dynsym section in t8, the return address
887 in t7 and then jump to _GLOBAL_OFFSET_TABLE[0]. */
888 got = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
889
890 /* This function will get called to fix up the GOT entry indicated by
891 the register t8, and then jump to the resolved address. */
892 got[0] = (ElfW(Addr)) &_dl_runtime_resolve;
893
894 /* Store l to _GLOBAL_OFFSET_TABLE[1] for gnu object. The MSB
895 of got[1] of a gnu object is set to identify gnu objects.
896 Where we can store l for non gnu objects? XXX */
897 if ((got[1] & ELF_MIPS_GNU_GOT1_MASK) != 0)
898 got[1] = ((ElfW(Addr)) l | ELF_MIPS_GNU_GOT1_MASK);
899 else
900 _dl_mips_gnu_objects = 0;
901 }
902
903 /* Relocate global offset table. */
904 elf_machine_got_rel (l, scope, lazy);
905
906 /* If using PLTs, fill in the first two entries of .got.plt. */
907 if (l->l_info[DT_JMPREL] && lazy)
908 {
909 ElfW(Addr) *gotplt;
910 gotplt = (ElfW(Addr) *) D_PTR (l, l_info[DT_MIPS (PLTGOT)]);
911 /* If a library is prelinked but we have to relocate anyway,
912 we have to be able to undo the prelinking of .got.plt.
913 The prelinker saved the address of .plt for us here. */
914 if (gotplt[1])
915 l->l_mach.plt = gotplt[1] + l->l_addr;
916 gotplt[0] = (ElfW(Addr)) &_dl_runtime_pltresolve;
917 gotplt[1] = (ElfW(Addr)) l;
918 }
919
920 # endif
921 return lazy;
922 }
923
924 #endif /* RESOLVE_MAP */
925