1 /* Machine-dependent ELF dynamic relocation inline functions. SPARC version.
2 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
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 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If
17 not, write to the Free Software Foundation, Inc.,
18 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 #define ELF_MACHINE_NAME "sparc"
25 #include <sys/param.h>
28 /* Some SPARC opcodes we need to use for self-modifying code. */
29 #define OPCODE_NOP 0x01000000 /* nop */
30 #define OPCODE_CALL 0x40000000 /* call ?; add PC-rel word address */
31 #define OPCODE_SETHI_G1 0x03000000 /* sethi ?, %g1; add value>>10 */
32 #define OPCODE_JMP_G1 0x81c06000 /* jmp %g1+?; add lo 10 bits of value */
33 #define OPCODE_SAVE_SP 0x9de3bfa8 /* save %sp, -(16+6)*4, %sp */
36 /* Return nonzero iff E_MACHINE is compatible with the running host. */
38 elf_machine_matches_host (Elf32_Half e_machine)
40 return e_machine == EM_SPARC;
44 /* Return the link-time address of _DYNAMIC. Conveniently, this is the
45 first element of the GOT. This must be inlined in a function which
47 static inline Elf32_Addr
48 elf_machine_dynamic (void)
50 register Elf32_Addr *got asm ("%l7");
54 /* Return the run-time load address of the shared object. */
55 static inline Elf32_Addr
56 elf_machine_load_address (void)
58 register Elf32_Addr pc __asm("%o7"), pic __asm("%l7"), got;
60 /* Utilize the fact that a local .got entry will be partially
61 initialized at startup awaiting its RELATIVE fixup. */
63 __asm("sethi %%hi(.Load_address),%1\n"
66 "or %1,%%lo(.Load_address),%1\n"
68 : "=r"(pc), "=r"(got) : "r"(pic));
73 /* Set up the loaded object described by L so its unrelocated PLT
74 entries will jump to the on-demand fixup code in dl-runtime.c. */
77 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
80 extern void _dl_runtime_resolve (Elf32_Word);
82 if (l->l_info[DT_JMPREL] && lazy)
84 /* The entries for functions in the PLT have not yet been filled in.
85 Their initial contents will arrange when called to set the high 22
86 bits of %g1 with an offset into the .rela.plt section and jump to
87 the beginning of the PLT. */
88 plt = (Elf32_Addr *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr);
90 /* The beginning of the PLT does:
93 pltpc: call _dl_runtime_resolve
97 This saves the register window containing the arguments, and the
98 PC value (pltpc) implicitly saved in %o7 by the call points near the
99 location where we store the link_map pointer for this object. */
101 plt[0] = OPCODE_SAVE_SP;
102 /* Construct PC-relative word address. */
103 plt[1] = OPCODE_CALL | (((Elf32_Addr) &_dl_runtime_resolve -
104 (Elf32_Addr) &plt[1]) >> 2);
105 plt[2] = OPCODE_NOP; /* Fill call delay slot. */
106 plt[3] = (Elf32_Addr) l;
112 /* This code is used in dl-runtime.c to call the `fixup' function
113 and then redirect to the address it returns. */
114 #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
115 .globl _dl_runtime_resolve
116 .type _dl_runtime_resolve, @function
118 /* Set up the arguments to fixup --
119 %o0 = link_map out of plt0
120 %o1 = offset of reloc entry */
127 .size _dl_runtime_resolve, . - _dl_runtime_resolve");
129 /* The address of the JMP_SLOT reloc is the .plt entry, thus we don't
130 dereference the reloc's addr to get the final destination. Ideally
131 there would be a generic way to return the value of the symbol from
132 elf_machine_relplt, but as it is, the address of the .plt entry is
134 #define ELF_FIXUP_RETURN_VALUE(map, result) ((Elf32_Addr) &(result))
136 /* Nonzero iff TYPE should not be allowed to resolve to one of
137 the main executable's symbols, as for a COPY reloc. */
138 #define elf_machine_lookup_noexec_p(type) ((type) == R_SPARC_COPY)
140 /* Nonzero iff TYPE describes relocation of a PLT entry, so
141 PLT entries should not be allowed to define the value. */
142 #define elf_machine_lookup_noplt_p(type) ((type) == R_SPARC_JMP_SLOT)
144 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
145 #define ELF_MACHINE_RELOC_NOPLT R_SPARC_JMP_SLOT
147 /* The SPARC never uses Elf32_Rel relocations. */
148 #define ELF_MACHINE_NO_REL 1
150 /* The SPARC overlaps DT_RELA and DT_PLTREL. */
151 #define ELF_MACHINE_PLTREL_OVERLAP 1
153 /* The PLT uses Elf32_Rela relocs. */
154 #define elf_machine_relplt elf_machine_rela
156 /* Initial entry point code for the dynamic linker.
157 The C function `_dl_start' is the real entry point;
158 its return value is the user program's entry point. */
160 #define RTLD_START __asm__ ("\
163 .type _start,@function
165 /* Allocate space for functions to drop their arguments. */
167 /* Pass pointer to argument block to _dl_start. */
171 .globl _dl_start_user
172 .type _dl_start_user,@function
174 /* Load the PIC register. */
176 sethi %hi(_GLOBAL_OFFSET_TABLE_-(1b-.)), %l7
177 2: or %l7, %lo(_GLOBAL_OFFSET_TABLE_-(1b-.)), %l7
179 /* Save the user entry point address in %l0 */
181 /* See if we were run as a command with the executable file name as an
182 extra leading argument. If so, adjust the contents of the stack. */
183 sethi %hi(_dl_skip_args), %g2
184 or %g2, %lo(_dl_skip_args), %g2
190 /* Find out how far to shift. */
191 ld [%sp+22*4], %i1 /* load argc */
211 /* Copy down auxiliary table. */
220 /* Load _dl_default_scope[2] to pass to _dl_init_next. */
221 3: sethi %hi(_dl_default_scope), %g1
222 or %g1, %lo(_dl_default_scope), %g1
225 /* Call _dl_init_next to return the address of an initializer to run. */
226 4: call _dl_init_next
234 /* Clear the startup flag. */
235 5: sethi %hi(_dl_starting_up), %g1
236 or %g1, %lo(_dl_starting_up), %g1
239 /* Pass our finalizer function to the user in %g1. */
240 sethi %hi(_dl_fini), %g1
241 or %g1, %lo(_dl_fini), %g1
243 /* Jump to the user's entry point and deallocate the extra stack we got. */
246 .size _dl_start_user,.-_dl_start_user
250 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
251 MAP is the object containing the reloc. */
254 elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
255 const Elf32_Sym *sym, const struct r_found_version *version,
256 Elf32_Addr *const reloc_addr)
258 extern unsigned long _dl_hwcap;
260 if (ELF32_R_TYPE (reloc->r_info) == R_SPARC_RELATIVE)
262 #ifndef RTLD_BOOTSTRAP
263 if (map != &_dl_rtld_map) /* Already done in rtld itself. */
265 *reloc_addr += map->l_addr + reloc->r_addend;
269 const Elf32_Sym *const refsym = sym;
271 if (sym->st_shndx != SHN_UNDEF &&
272 ELF32_ST_BIND (sym->st_info) == STB_LOCAL)
276 value = RESOLVE (&sym, version, ELF32_R_TYPE (reloc->r_info));
278 value += sym->st_value;
280 value += reloc->r_addend; /* Assume copy relocs have zero addend. */
282 switch (ELF32_R_TYPE (reloc->r_info))
285 #ifndef RTLD_BOOTSTRAP
286 if (sym->st_size > refsym->st_size
287 || (_dl_verbose && sym->st_size < refsym->st_size))
289 extern char **_dl_argv;
292 strtab = ((void *) map->l_addr
293 + map->l_info[DT_STRTAB]->d_un.d_ptr);
294 _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>",
295 ": Symbol `", strtab + refsym->st_name,
296 "' has different size in shared object, "
297 "consider re-linking\n", NULL);
299 memcpy (reloc_addr, (void *) value, MIN (sym->st_size,
303 case R_SPARC_GLOB_DAT:
307 case R_SPARC_JMP_SLOT:
308 /* For thread safety, write the instructions from the bottom and
309 flush before we overwrite the critical "b,a". */
310 reloc_addr[2] = OPCODE_JMP_G1 | (value & 0x3ff);
311 if (_dl_hwcap & HWCAP_SPARC_FLUSH)
312 __asm __volatile ("flush %0+8" : : "r"(reloc_addr));
313 reloc_addr[1] = OPCODE_SETHI_G1 | (value >> 10);
314 if (_dl_hwcap & HWCAP_SPARC_FLUSH)
315 __asm __volatile ("flush %0+4" : : "r"(reloc_addr));
318 *(char *) reloc_addr = value;
321 *(short *) reloc_addr = value;
324 *(char *) reloc_addr = (value - (Elf32_Addr) reloc_addr);
327 *(short *) reloc_addr = (value - (Elf32_Addr) reloc_addr);
330 *reloc_addr = (value - (Elf32_Addr) reloc_addr);
333 *reloc_addr = (*reloc_addr & ~0x3ff) | (value & 0x3ff);
335 case R_SPARC_WDISP30:
336 *reloc_addr = ((*reloc_addr & 0xc0000000)
337 | ((value - (unsigned int) reloc_addr) >> 2));
340 *reloc_addr = (*reloc_addr & 0xffc00000) | (value >> 10);
342 case R_SPARC_NONE: /* Alright, Wilbur. */
345 assert (! "unexpected dynamic reloc type");
352 elf_machine_lazy_rel (struct link_map *map, const Elf32_Rela *reloc)
354 switch (ELF32_R_TYPE (reloc->r_info))
358 case R_SPARC_JMP_SLOT:
361 assert (! "unexpected PLT reloc type");