(ELF_MACHINE_NO_RELA): Define unconditionally to defined RTLD_BOOTSTRAP.
[glibc-ports.git] / sysdeps / mips / dl-machine.h
blob0d87b65691e0a1a533d715bf5405cddb7c2875be
1 /* Machine-dependent ELF dynamic relocation inline functions. MIPS version.
2 Copyright (C) 1996-2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Kazumoto Kojima <kkojima@info.kanagawa-u.ac.jp>.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
21 /* FIXME: Profiling of shared libraries is not implemented yet. */
22 #ifndef dl_machine_h
23 #define dl_machine_h
25 #define ELF_MACHINE_NAME "MIPS"
27 #define ELF_MACHINE_NO_PLT
29 #include <entry.h>
31 #ifndef ENTRY_POINT
32 #error ENTRY_POINT needs to be defined for MIPS.
33 #endif
35 #include <sgidefs.h>
36 #include <sys/asm.h>
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
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
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
56 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.
57 This makes no sense on MIPS but we have to define this to R_MIPS_REL32
58 to avoid the asserts in dl-lookup.c from blowing. */
59 #define ELF_MACHINE_JMP_SLOT R_MIPS_REL32
60 #define elf_machine_type_class(type) ELF_RTYPE_CLASS_PLT
62 /* Translate a processor specific dynamic tag to the index
63 in l_info array. */
64 #define DT_MIPS(x) (DT_MIPS_##x - DT_LOPROC + DT_NUM)
66 /* If there is a DT_MIPS_RLD_MAP entry in the dynamic section, fill it in
67 with the run-time address of the r_debug structure */
68 #define ELF_MACHINE_DEBUG_SETUP(l,r) \
69 do { if ((l)->l_info[DT_MIPS (RLD_MAP)]) \
70 *(ElfW(Addr) *)((l)->l_info[DT_MIPS (RLD_MAP)]->d_un.d_ptr) = \
71 (ElfW(Addr)) (r); \
72 } while (0)
74 /* Return nonzero iff ELF header is compatible with the running host. */
75 static inline int __attribute_used__
76 elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
78 #if _MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIN32
79 /* Don't link o32 and n32 together. */
80 if (((ehdr->e_flags & EF_MIPS_ABI2) != 0) != (_MIPS_SIM == _ABIN32))
81 return 0;
82 #endif
84 switch (ehdr->e_machine)
86 case EM_MIPS:
87 case EM_MIPS_RS3_LE:
88 return 1;
89 default:
90 return 0;
94 static inline ElfW(Addr) *
95 elf_mips_got_from_gpreg (ElfW(Addr) gpreg)
97 /* FIXME: the offset of gp from GOT may be system-dependent. */
98 return (ElfW(Addr) *) (gpreg - OFFSET_GP_GOT);
101 /* Return the link-time address of _DYNAMIC. Conveniently, this is the
102 first element of the GOT. This must be inlined in a function which
103 uses global data. We assume its $gp points to the primary GOT. */
104 static inline ElfW(Addr)
105 elf_machine_dynamic (void)
107 register ElfW(Addr) gp __asm__ ("$28");
108 return *elf_mips_got_from_gpreg (gp);
111 #define STRINGXP(X) __STRING(X)
112 #define STRINGXV(X) STRINGV_(X)
113 #define STRINGV_(...) # __VA_ARGS__
115 /* Return the run-time load address of the shared object. */
116 static inline ElfW(Addr)
117 elf_machine_load_address (void)
119 ElfW(Addr) addr;
120 asm (" .set noreorder\n"
121 " " STRINGXP (PTR_LA) " %0, 0f\n"
122 " bltzal $0, 0f\n"
123 " nop\n"
124 "0: " STRINGXP (PTR_SUBU) " %0, $31, %0\n"
125 " .set reorder\n"
126 : "=r" (addr)
127 : /* No inputs */
128 : "$31");
129 return addr;
132 /* The MSB of got[1] of a gnu object is set to identify gnu objects. */
133 #if _MIPS_SIM == _ABI64
134 # define ELF_MIPS_GNU_GOT1_MASK 0x8000000000000000L
135 #else
136 # define ELF_MIPS_GNU_GOT1_MASK 0x80000000L
137 #endif
139 /* We can't rely on elf_machine_got_rel because _dl_object_relocation_scope
140 fiddles with global data. */
141 #define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) \
142 do { \
143 struct link_map *map = &bootstrap_map; \
144 ElfW(Sym) *sym; \
145 ElfW(Addr) *got; \
146 int i, n; \
148 got = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]); \
150 if (__builtin_expect (map->l_addr == 0, 1)) \
151 break; \
153 /* got[0] is reserved. got[1] is also reserved for the dynamic object \
154 generated by gnu ld. Skip these reserved entries from \
155 relocation. */ \
156 i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1; \
157 n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; \
159 /* Add the run-time displacement to all local got entries. */ \
160 while (i < n) \
161 got[i++] += map->l_addr; \
163 /* Handle global got entries. */ \
164 got += n; \
165 sym = (ElfW(Sym) *) D_PTR(map, l_info[DT_SYMTAB]) \
166 + map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val; \
167 i = (map->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val \
168 - map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val); \
170 while (i--) \
172 if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON) \
173 *got = map->l_addr + sym->st_value; \
174 else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC \
175 && *got != sym->st_value) \
176 *got += map->l_addr; \
177 else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION) \
179 if (sym->st_other == 0) \
180 *got += map->l_addr; \
182 else \
183 *got = map->l_addr + sym->st_value; \
185 got++; \
186 sym++; \
188 } while(0)
191 /* Get link map for callers object containing STUB_PC. */
192 static inline struct link_map *
193 elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
195 extern int _dl_mips_gnu_objects;
197 /* got[1] is reserved to keep its link map address for the shared
198 object generated by the gnu linker. If all are such objects, we
199 can find the link map from current GPREG simply. If not so, get
200 the link map for caller's object containing STUB_PC. */
202 if (_dl_mips_gnu_objects)
204 ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
205 ElfW(Word) g1;
207 g1 = ((ElfW(Word) *) got)[1];
209 if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
211 struct link_map *l =
212 (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK);
213 ElfW(Addr) base, limit;
214 const ElfW(Phdr) *p = l->l_phdr;
215 ElfW(Half) this, nent = l->l_phnum;
217 /* For the common case of a stub being called from the containing
218 object, STUB_PC will point to somewhere within the object that
219 is described by the link map fetched via got[1]. Otherwise we
220 have to scan all maps. */
221 for (this = 0; this < nent; this++)
223 if (p[this].p_type == PT_LOAD)
225 base = p[this].p_vaddr + l->l_addr;
226 limit = base + p[this].p_memsz;
227 if (stub_pc >= base && stub_pc < limit)
228 return l;
234 struct link_map *l;
235 Lmid_t nsid;
237 for (nsid = 0; nsid < DL_NNS; ++nsid)
238 for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
240 ElfW(Addr) base, limit;
241 const ElfW(Phdr) *p = l->l_phdr;
242 ElfW(Half) this, nent = l->l_phnum;
244 for (this = 0; this < nent; ++this)
246 if (p[this].p_type == PT_LOAD)
248 base = p[this].p_vaddr + l->l_addr;
249 limit = base + p[this].p_memsz;
250 if (stub_pc >= base && stub_pc < limit)
251 return l;
256 _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
257 return NULL;
260 #if _MIPS_SIM == _ABIO32
261 #define ELF_DL_FRAME_SIZE 40
263 #define ELF_DL_SAVE_ARG_REGS "\
264 sw $15, 36($29)\n \
265 sw $4, 16($29)\n \
266 sw $5, 20($29)\n \
267 sw $6, 24($29)\n \
268 sw $7, 28($29)\n \
271 #define ELF_DL_RESTORE_ARG_REGS "\
272 lw $31, 36($29)\n \
273 lw $4, 16($29)\n \
274 lw $5, 20($29)\n \
275 lw $6, 24($29)\n \
276 lw $7, 28($29)\n \
279 #define IFABIO32(X) X
281 #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
283 #define ELF_DL_FRAME_SIZE 80
285 #define ELF_DL_SAVE_ARG_REGS "\
286 sd $15, 72($29)\n \
287 sd $4, 8($29)\n \
288 sd $5, 16($29)\n \
289 sd $6, 24($29)\n \
290 sd $7, 32($29)\n \
291 sd $8, 40($29)\n \
292 sd $9, 48($29)\n \
293 sd $10, 56($29)\n \
294 sd $11, 64($29)\n \
297 #define ELF_DL_RESTORE_ARG_REGS "\
298 ld $31, 72($29)\n \
299 ld $4, 8($29)\n \
300 ld $5, 16($29)\n \
301 ld $6, 24($29)\n \
302 ld $7, 32($29)\n \
303 ld $8, 40($29)\n \
304 ld $9, 48($29)\n \
305 ld $10, 56($29)\n \
306 ld $11, 64($29)\n \
309 #define IFABIO32(X)
311 #endif
313 /* Define mips specific runtime resolver. The function __dl_runtime_resolve
314 is called from assembler function _dl_runtime_resolve which converts
315 special argument registers t7 ($15) and t8 ($24):
316 t7 address to return to the caller of the function
317 t8 index for this function symbol in .dynsym
318 to usual c arguments.
320 Other architectures call fixup from dl-runtime.c in
321 _dl_runtime_resolve. MIPS instead calls __dl_runtime_resolve. We
322 have to use our own version because of the way the got section is
323 treated on MIPS (we've also got ELF_MACHINE_PLT defined). */
325 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
326 /* The flag _dl_mips_gnu_objects is set if all dynamic objects are \
327 generated by the gnu linker. */ \
328 int _dl_mips_gnu_objects = 1; \
330 /* This is called from assembly stubs below which the compiler can't see. */ \
331 static ElfW(Addr) \
332 __dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr)) \
333 __attribute_used__; \
335 static ElfW(Addr) \
336 __dl_runtime_resolve (ElfW(Word) sym_index, \
337 ElfW(Word) return_address, \
338 ElfW(Addr) old_gpreg, \
339 ElfW(Addr) stub_pc) \
341 struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc); \
342 const ElfW(Sym) *const symtab \
343 = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]); \
344 const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); \
345 ElfW(Addr) *got \
346 = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]); \
347 const ElfW(Word) local_gotno \
348 = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; \
349 const ElfW(Word) gotsym \
350 = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val; \
351 const ElfW(Sym) *sym = &symtab[sym_index]; \
352 ElfW(Addr) value; \
354 /* FIXME: The symbol versioning stuff is not tested yet. */ \
355 if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) \
357 switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) \
359 default: \
361 const ElfW(Half) *vernum = \
362 (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); \
363 ElfW(Half) ndx = vernum[sym_index] & 0x7fff; \
364 const struct r_found_version *version = &l->l_versions[ndx]; \
366 if (version->hash != 0) \
368 value = _dl_lookup_symbol_x (strtab + sym->st_name, l, \
369 &sym, l->l_scope, version, \
370 ELF_RTYPE_CLASS_PLT, 0, 0); \
371 break; \
373 /* Fall through. */ \
375 case 0: \
376 value = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, \
377 l->l_scope, 0, ELF_RTYPE_CLASS_PLT, \
378 DL_LOOKUP_ADD_DEPENDENCY, 0); \
381 /* Currently value contains the base load address of the object \
382 that defines sym. Now add in the symbol offset. */ \
383 value = (sym ? value + sym->st_value : 0); \
385 else \
386 /* We already found the symbol. The module (and therefore its load \
387 address) is also known. */ \
388 value = l->l_addr + sym->st_value; \
390 /* Apply the relocation with that value. */ \
391 *(got + local_gotno + sym_index - gotsym) = value; \
393 return value; \
396 asm ("\n \
397 .text\n \
398 .align 2\n \
399 .globl _dl_runtime_resolve\n \
400 .type _dl_runtime_resolve,@function\n \
401 .ent _dl_runtime_resolve\n \
402 _dl_runtime_resolve:\n \
403 .frame $29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n \
404 .set noreorder\n \
405 # Save GP.\n \
406 move $3, $28\n \
407 # Save arguments and sp value in stack.\n \
408 " STRINGXP(PTR_SUBIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n \
409 # Modify t9 ($25) so as to point .cpload instruction.\n \
410 " IFABIO32(STRINGXP(PTR_ADDIU) " $25, 12\n") " \
411 # Compute GP.\n \
412 " STRINGXP(SETUP_GP) "\n \
413 " STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n \
414 .set reorder\n \
415 # Save slot call pc.\n \
416 move $2, $31\n \
417 " IFABIO32(STRINGXP(CPRESTORE(32))) "\n \
418 " ELF_DL_SAVE_ARG_REGS " \
419 move $4, $24\n \
420 move $5, $15\n \
421 move $6, $3\n \
422 move $7, $2\n \
423 jal __dl_runtime_resolve\n \
424 " ELF_DL_RESTORE_ARG_REGS " \
425 " STRINGXP(RESTORE_GP64) "\n \
426 " STRINGXP(PTR_ADDIU) " $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n \
427 move $25, $2\n \
428 jr $25\n \
429 .end _dl_runtime_resolve\n \
430 .previous\n \
433 /* Mask identifying addresses reserved for the user program,
434 where the dynamic linker should not map anything. */
435 #define ELF_MACHINE_USER_ADDRESS_MASK 0x80000000UL
438 /* Initial entry point code for the dynamic linker.
439 The C function `_dl_start' is the real entry point;
440 its return value is the user program's entry point.
441 Note how we have to be careful about two things:
443 1) That we allocate a minimal stack of 24 bytes for
444 every function call, the MIPS ABI states that even
445 if all arguments are passed in registers the procedure
446 called can use the 16 byte area pointed to by $sp
447 when it is called to store away the arguments passed
448 to it.
450 2) That under Linux the entry is named __start
451 and not just plain _start. */
453 #define RTLD_START asm (\
454 ".text\n"\
455 _RTLD_PROLOGUE(ENTRY_POINT) "\
456 " STRINGXV(SETUP_GPX($25)) "\n\
457 " STRINGXV(SETUP_GPX64($18,$25)) "\n\
458 # i386 ABI book says that the first entry of GOT holds\n\
459 # the address of the dynamic structure. Though MIPS ABI\n\
460 # doesn't say nothing about this, I emulate this here.\n\
461 " STRINGXP(PTR_LA) " $4, _DYNAMIC\n\
462 # Subtract OFFSET_GP_GOT\n\
463 " STRINGXP(PTR_S) " $4, -0x7ff0($28)\n\
464 move $4, $29\n\
465 " STRINGXP(PTR_SUBIU) " $29, 16\n\
467 " STRINGXP(PTR_LA) " $8, .Lcoff\n\
468 bltzal $8, .Lcoff\n\
469 .Lcoff: " STRINGXP(PTR_SUBU) " $8, $31, $8\n\
471 " STRINGXP(PTR_LA) " $25, _dl_start\n\
472 " STRINGXP(PTR_ADDU) " $25, $8\n\
473 jalr $25\n\
475 " STRINGXP(PTR_ADDIU) " $29, 16\n\
476 # Get the value of label '_dl_start_user' in t9 ($25).\n\
477 " STRINGXP(PTR_LA) " $25, _dl_start_user\n\
478 .globl _dl_start_user\n\
479 .type _dl_start_user,@function\n\
480 .aent _dl_start_user\n\
481 _dl_start_user:\n\
482 " STRINGXP(SETUP_GP) "\n\
483 " STRINGXV(SETUP_GP64($18,_dl_start_user)) "\n\
484 move $16, $28\n\
485 # Save the user entry point address in a saved register.\n\
486 move $17, $2\n\
487 # See if we were run as a command with the executable file\n\
488 # name as an extra leading argument.\n\
489 lw $2, _dl_skip_args\n\
490 beq $2, $0, 1f\n\
491 # Load the original argument count.\n\
492 " STRINGXP(PTR_L) " $4, 0($29)\n\
493 # Subtract _dl_skip_args from it.\n\
494 subu $4, $2\n\
495 # Adjust the stack pointer to skip _dl_skip_args words.\n\
496 sll $2, " STRINGXP (PTRLOG) "\n\
497 " STRINGXP(PTR_ADDU) " $29, $2\n\
498 # Save back the modified argument count.\n\
499 " STRINGXP(PTR_S) " $4, 0($29)\n\
500 1: # Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
501 " STRINGXP(PTR_L) " $4, _rtld_local\n\
502 " STRINGXP(PTR_L) /* or lw??? fixme */ " $5, 0($29)\n\
503 " STRINGXP(PTR_LA) " $6, " STRINGXP (PTRSIZE) "($29)\n\
504 sll $7, $5, " STRINGXP (PTRLOG) "\n\
505 " STRINGXP(PTR_ADDU) " $7, $7, $6\n\
506 " STRINGXP(PTR_ADDU) " $7, $7, " STRINGXP (PTRSIZE) " \n\
507 " STRINGXP(PTR_SUBIU) " $29, 32\n\
508 " STRINGXP(SAVE_GP(16)) "\n\
509 # Call the function to run the initializers.\n\
510 jal _dl_init_internal\n\
511 " STRINGXP(PTR_ADDIU) " $29, 32\n\
512 # Pass our finalizer function to the user in $2 as per ELF ABI.\n\
513 " STRINGXP(PTR_LA) " $2, _dl_fini\n\
514 # Jump to the user entry point.\n\
515 move $25, $17\n\
516 jr $25\n\t"\
517 _RTLD_EPILOGUE(ENTRY_POINT)\
518 ".previous"\
521 /* The MIPS never uses Elfxx_Rela relocations. */
522 #define ELF_MACHINE_NO_RELA 1
524 #endif /* !dl_machine_h */
526 #ifdef RESOLVE
528 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
529 MAP is the object containing the reloc. */
531 static inline void
532 #ifdef RTLD_BOOTSTRAP
533 __attribute__ ((always_inline))
534 #endif
535 elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc,
536 const ElfW(Sym) *sym, const struct r_found_version *version,
537 void *const reloc_addr)
539 const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info);
541 #if !defined RTLD_BOOTSTRAP && !defined SHARED
542 /* This is defined in rtld.c, but nowhere in the static libc.a;
543 make the reference weak so static programs can still link. This
544 declaration cannot be done when compiling rtld.c (i.e. #ifdef
545 RTLD_BOOTSTRAP) because rtld.c contains the common defn for
546 _dl_rtld_map, which is incompatible with a weak decl in the same
547 file. */
548 weak_extern (GL(dl_rtld_map));
549 #endif
551 switch (r_type)
553 #if _MIPS_SIM == _ABI64
554 case (R_MIPS_64 << 8) | R_MIPS_REL32:
555 #else
556 case R_MIPS_REL32:
557 #endif
559 int symidx = ELFW(R_SYM) (reloc->r_info);
560 ElfW(Addr) reloc_value;
562 /* Support relocations on mis-aligned offsets. Should we ever
563 implement RELA, this should be replaced with an assignment
564 from reloc->r_addend. */
565 __builtin_memcpy (&reloc_value, reloc_addr, sizeof (reloc_value));
567 if (symidx)
569 const ElfW(Word) gotsym
570 = (const ElfW(Word)) map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
572 if ((ElfW(Word))symidx < gotsym)
574 /* This wouldn't work for a symbol imported from other
575 libraries for which there's no GOT entry, but MIPS
576 requires every symbol referenced in a dynamic
577 relocation to have a GOT entry in the primary GOT,
578 so we only get here for locally-defined symbols.
579 For section symbols, we should *NOT* be adding
580 sym->st_value (per the definition of the meaning of
581 S in reloc expressions in the ELF64 MIPS ABI),
582 since it should have already been added to
583 reloc_value by the linker, but older versions of
584 GNU ld didn't add it, and newer versions don't emit
585 useless relocations to section symbols any more, so
586 it is safe to keep on adding sym->st_value, even
587 though it's not ABI compliant. Some day we should
588 bite the bullet and stop doing this. */
589 #ifndef RTLD_BOOTSTRAP
590 if (map != &GL(dl_rtld_map))
591 #endif
592 reloc_value += sym->st_value + map->l_addr;
594 else
596 #ifndef RTLD_BOOTSTRAP
597 const ElfW(Addr) *got
598 = (const ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
599 const ElfW(Word) local_gotno
600 = (const ElfW(Word))
601 map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
603 reloc_value += got[symidx + local_gotno - gotsym];
604 #endif
607 else
608 #ifndef RTLD_BOOTSTRAP
609 if (map != &GL(dl_rtld_map))
610 #endif
611 reloc_value += map->l_addr;
613 __builtin_memcpy (reloc_addr, &reloc_value, sizeof (reloc_value));
615 break;
616 case R_MIPS_NONE: /* Alright, Wilbur. */
617 break;
618 #if _MIPS_SIM == _ABI64
619 case R_MIPS_64:
620 /* For full compliance with the ELF64 ABI, one must precede the
621 _REL32/_64 pair of relocations with a _64 relocation, such
622 that the in-place addend is read as a 64-bit value. IRIX
623 didn't pick up on this requirement, so we treat the
624 _REL32/_64 relocation as a 64-bit relocation even if it's by
625 itself. For ABI compliance, we ignore such _64 dummy
626 relocations. For RELA, this may be simply removed, since
627 it's totally unnecessary. */
628 if (ELFW(R_SYM) (reloc->r_info) == 0)
629 break;
630 /* Fall through. */
631 #endif
632 default:
633 _dl_reloc_bad_type (map, r_type, 0);
634 break;
638 static inline void
639 elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
640 void *const reloc_addr)
642 /* XXX Nothing to do. There is no relative relocation, right? */
645 static inline void
646 elf_machine_lazy_rel (struct link_map *map,
647 ElfW(Addr) l_addr, const ElfW(Rel) *reloc)
649 /* Do nothing. */
652 #ifndef RTLD_BOOTSTRAP
653 /* Relocate GOT. */
654 static inline void
655 elf_machine_got_rel (struct link_map *map, int lazy)
657 ElfW(Addr) *got;
658 ElfW(Sym) *sym;
659 const ElfW(Half) *vernum;
660 int i, n, symidx;
662 #define RESOLVE_GOTSYM(sym,vernum,sym_index) \
663 ({ \
664 const ElfW(Sym) *ref = sym; \
665 const struct r_found_version *version \
666 = vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL; \
667 ElfW(Addr) value; \
668 value = RESOLVE (&ref, version, R_MIPS_REL32); \
669 (ref)? value + ref->st_value: 0; \
672 if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
673 vernum = (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
674 else
675 vernum = NULL;
677 got = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
679 n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
680 /* The dynamic linker's local got entries have already been relocated. */
681 if (map != &GL(dl_rtld_map))
683 /* got[0] is reserved. got[1] is also reserved for the dynamic object
684 generated by gnu ld. Skip these reserved entries from relocation. */
685 i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1;
687 /* Add the run-time displacement to all local got entries if
688 needed. */
689 if (__builtin_expect (map->l_addr != 0, 0))
691 while (i < n)
692 got[i++] += map->l_addr;
696 /* Handle global got entries. */
697 got += n;
698 /* Keep track of the symbol index. */
699 symidx = map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
700 sym = (ElfW(Sym) *) D_PTR (map, l_info[DT_SYMTAB]) + symidx;
701 i = (map->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val
702 - map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val);
704 /* This loop doesn't handle Quickstart. */
705 while (i--)
707 if (sym->st_shndx == SHN_UNDEF)
709 if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
710 && sym->st_value && lazy)
711 *got = sym->st_value + map->l_addr;
712 else
713 *got = RESOLVE_GOTSYM (sym, vernum, symidx);
715 else if (sym->st_shndx == SHN_COMMON)
716 *got = RESOLVE_GOTSYM (sym, vernum, symidx);
717 else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
718 && *got != sym->st_value
719 && lazy)
720 *got += map->l_addr;
721 else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)
723 if (sym->st_other == 0)
724 *got += map->l_addr;
726 else
727 *got = RESOLVE_GOTSYM (sym, vernum, symidx);
729 ++got;
730 ++sym;
731 ++symidx;
734 #undef RESOLVE_GOTSYM
736 #endif
738 /* Set up the loaded object described by L so its stub function
739 will jump to the on-demand fixup code __dl_runtime_resolve. */
741 static inline int
742 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
744 # ifndef RTLD_BOOTSTRAP
745 ElfW(Addr) *got;
746 extern void _dl_runtime_resolve (ElfW(Word));
747 extern int _dl_mips_gnu_objects;
749 if (lazy)
751 /* The GOT entries for functions have not yet been filled in.
752 Their initial contents will arrange when called to put an
753 offset into the .dynsym section in t8, the return address
754 in t7 and then jump to _GLOBAL_OFFSET_TABLE[0]. */
755 got = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
757 /* This function will get called to fix up the GOT entry indicated by
758 the register t8, and then jump to the resolved address. */
759 got[0] = (ElfW(Addr)) &_dl_runtime_resolve;
761 /* Store l to _GLOBAL_OFFSET_TABLE[1] for gnu object. The MSB
762 of got[1] of a gnu object is set to identify gnu objects.
763 Where we can store l for non gnu objects? XXX */
764 if ((got[1] & ELF_MIPS_GNU_GOT1_MASK) != 0)
765 got[1] = ((ElfW(Addr)) l | ELF_MIPS_GNU_GOT1_MASK);
766 else
767 _dl_mips_gnu_objects = 0;
770 /* Relocate global offset table. */
771 elf_machine_got_rel (l, lazy);
773 # endif
774 return lazy;
777 #endif /* RESOLVE */