]>
Commit | Line | Data |
---|---|---|
4f54cdb1 | 1 | /* Machine-dependent ELF dynamic relocation inline functions. SPARC version. |
f420344c | 2 | Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. |
47707456 | 3 | This file is part of the GNU C Library. |
4f54cdb1 | 4 | |
47707456 UD |
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. | |
4f54cdb1 | 9 | |
47707456 UD |
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. | |
4f54cdb1 | 14 | |
47707456 UD |
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. */ | |
4f54cdb1 RM |
19 | |
20 | #define ELF_MACHINE_NAME "sparc" | |
21 | ||
4f54cdb1 | 22 | #include <string.h> |
1f07e617 | 23 | #include <sys/param.h> |
4194bc66 | 24 | #include <elf/ldsodefs.h> |
4f54cdb1 RM |
25 | |
26 | ||
27 | /* Some SPARC opcodes we need to use for self-modifying code. */ | |
28 | #define OPCODE_NOP 0x01000000 /* nop */ | |
f752bfe3 | 29 | #define OPCODE_CALL 0x40000000 /* call ?; add PC-rel word address */ |
4f54cdb1 RM |
30 | #define OPCODE_SETHI_G1 0x03000000 /* sethi ?, %g1; add value>>10 */ |
31 | #define OPCODE_JMP_G1 0x81c06000 /* jmp %g1+?; add lo 10 bits of value */ | |
f41c8091 | 32 | #define OPCODE_SAVE_SP 0x9de3bfa8 /* save %sp, -(16+6)*4, %sp */ |
4f54cdb1 | 33 | |
4194bc66 RH |
34 | /* Protect some broken versions of gcc from misinterpreting weak addresses. */ |
35 | #define WEAKADDR(x) ({ __typeof(x) *_px = &x; \ | |
36 | __asm ("" : "=r" (_px) : "0" (_px)); \ | |
b062f051 | 37 | _px; }) |
4194bc66 RH |
38 | |
39 | ||
40 | /* Use a different preload file when running in 32-bit emulation mode | |
41 | on a 64-bit host. */ | |
42 | #define LD_SO_PRELOAD ((_dl_hwcap & HWCAP_SPARC_V9) ? "/etc/ld.so.preload32" \ | |
43 | : "/etc/ld.so.preload") | |
44 | ||
45 | ||
4f54cdb1 RM |
46 | /* Return nonzero iff E_MACHINE is compatible with the running host. */ |
47 | static inline int | |
48 | elf_machine_matches_host (Elf32_Half e_machine) | |
49 | { | |
4194bc66 RH |
50 | if (e_machine == EM_SPARC) |
51 | return 1; | |
52 | else if (e_machine == EM_SPARC32PLUS) | |
53 | { | |
b7e6f7bf RH |
54 | unsigned long *hwcap; |
55 | weak_extern (_dl_hwcap); | |
56 | weak_extern (_dl_hwcap_mask); | |
57 | ||
58 | hwcap = WEAKADDR(_dl_hwcap); | |
4194bc66 RH |
59 | return hwcap && (*hwcap & _dl_hwcap_mask & HWCAP_SPARC_V9); |
60 | } | |
61 | else | |
62 | return 0; | |
4f54cdb1 RM |
63 | } |
64 | ||
65 | ||
47707456 UD |
66 | /* Return the link-time address of _DYNAMIC. Conveniently, this is the |
67 | first element of the GOT. This must be inlined in a function which | |
68 | uses global data. */ | |
69 | static inline Elf32_Addr | |
70 | elf_machine_dynamic (void) | |
4f54cdb1 RM |
71 | { |
72 | register Elf32_Addr *got asm ("%l7"); | |
47707456 | 73 | return *got; |
4f54cdb1 RM |
74 | } |
75 | ||
4f54cdb1 RM |
76 | /* Return the run-time load address of the shared object. */ |
77 | static inline Elf32_Addr | |
78 | elf_machine_load_address (void) | |
79 | { | |
f41c8091 | 80 | register Elf32_Addr pc __asm("%o7"), pic __asm("%l7"), got; |
ca34d7a7 UD |
81 | |
82 | /* Utilize the fact that a local .got entry will be partially | |
83 | initialized at startup awaiting its RELATIVE fixup. */ | |
84 | ||
85 | __asm("sethi %%hi(.Load_address),%1\n" | |
86 | ".Load_address:\n\t" | |
87 | "call 1f\n\t" | |
88 | "or %1,%%lo(.Load_address),%1\n" | |
f41c8091 UD |
89 | "1:\tld [%2+%1],%1" |
90 | : "=r"(pc), "=r"(got) : "r"(pic)); | |
ca34d7a7 UD |
91 | |
92 | return pc - got; | |
93 | } | |
94 | ||
f41c8091 UD |
95 | /* Set up the loaded object described by L so its unrelocated PLT |
96 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
97 | ||
98 | static inline int | |
99 | elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) | |
100 | { | |
101 | Elf32_Addr *plt; | |
102 | extern void _dl_runtime_resolve (Elf32_Word); | |
9c4c0024 | 103 | extern void _dl_runtime_profile (Elf32_Word); |
f41c8091 UD |
104 | |
105 | if (l->l_info[DT_JMPREL] && lazy) | |
106 | { | |
9c4c0024 UD |
107 | Elf32_Addr rfunc; |
108 | ||
f41c8091 UD |
109 | /* The entries for functions in the PLT have not yet been filled in. |
110 | Their initial contents will arrange when called to set the high 22 | |
111 | bits of %g1 with an offset into the .rela.plt section and jump to | |
112 | the beginning of the PLT. */ | |
7796221a | 113 | plt = (Elf32_Addr *) l->l_info[DT_PLTGOT]->d_un.d_ptr; |
9c4c0024 UD |
114 | if (! profile) |
115 | rfunc = (Elf32_Addr) &_dl_runtime_resolve; | |
116 | else | |
117 | { | |
118 | rfunc = (Elf32_Addr) &_dl_runtime_profile; | |
119 | ||
120 | if (_dl_name_match_p (_dl_profile, l)) | |
121 | _dl_profile_map = l; | |
122 | } | |
f41c8091 UD |
123 | |
124 | /* The beginning of the PLT does: | |
125 | ||
126 | save %sp, -64, %sp | |
127 | pltpc: call _dl_runtime_resolve | |
128 | nop | |
129 | .word MAP | |
130 | ||
131 | This saves the register window containing the arguments, and the | |
132 | PC value (pltpc) implicitly saved in %o7 by the call points near the | |
133 | location where we store the link_map pointer for this object. */ | |
134 | ||
135 | plt[0] = OPCODE_SAVE_SP; | |
136 | /* Construct PC-relative word address. */ | |
9c4c0024 | 137 | plt[1] = OPCODE_CALL | ((rfunc - (Elf32_Addr) &plt[1]) >> 2); |
f41c8091 UD |
138 | plt[2] = OPCODE_NOP; /* Fill call delay slot. */ |
139 | plt[3] = (Elf32_Addr) l; | |
140 | } | |
141 | ||
142 | return lazy; | |
4f54cdb1 RM |
143 | } |
144 | ||
f41c8091 UD |
145 | /* This code is used in dl-runtime.c to call the `fixup' function |
146 | and then redirect to the address it returns. */ | |
9c4c0024 UD |
147 | #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \ |
148 | asm ( "\ | |
149 | .text | |
150 | .globl " #tramp_name " | |
151 | .type " #tramp_name ", @function | |
152 | .align 32 | |
153 | " #tramp_name ": | |
f41c8091 UD |
154 | /* Set up the arguments to fixup -- |
155 | %o0 = link_map out of plt0 | |
9c4c0024 UD |
156 | %o1 = offset of reloc entry |
157 | %o2 = return address */ | |
f41c8091 UD |
158 | ld [%o7 + 8], %o0 |
159 | srl %g1, 10, %o1 | |
9c4c0024 UD |
160 | mov %i7, %o2 |
161 | call " #fixup_name " | |
f41c8091 UD |
162 | sub %o1, 4*12, %o1 |
163 | jmp %o0 | |
164 | restore | |
9c4c0024 UD |
165 | .size " #tramp_name ", . - " #tramp_name " |
166 | .previous") | |
167 | ||
168 | #ifndef PROF | |
169 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE \ | |
170 | TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \ | |
171 | TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup); | |
172 | #else | |
173 | #define ELF_MACHINE_RUNTIME_TRAMPOLINE \ | |
174 | TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \ | |
175 | TRAMPOLINE_TEMPLATE (_dl_runtime_profile, fixup); | |
176 | #endif | |
f41c8091 | 177 | |
f41c8091 UD |
178 | /* Nonzero iff TYPE should not be allowed to resolve to one of |
179 | the main executable's symbols, as for a COPY reloc. */ | |
180 | #define elf_machine_lookup_noexec_p(type) ((type) == R_SPARC_COPY) | |
181 | ||
182 | /* Nonzero iff TYPE describes relocation of a PLT entry, so | |
183 | PLT entries should not be allowed to define the value. */ | |
184 | #define elf_machine_lookup_noplt_p(type) ((type) == R_SPARC_JMP_SLOT) | |
185 | ||
186 | /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ | |
a2b08ee5 | 187 | #define ELF_MACHINE_JMP_SLOT R_SPARC_JMP_SLOT |
f41c8091 UD |
188 | |
189 | /* The SPARC never uses Elf32_Rel relocations. */ | |
190 | #define ELF_MACHINE_NO_REL 1 | |
191 | ||
192 | /* The SPARC overlaps DT_RELA and DT_PLTREL. */ | |
193 | #define ELF_MACHINE_PLTREL_OVERLAP 1 | |
194 | ||
f41c8091 UD |
195 | /* Initial entry point code for the dynamic linker. |
196 | The C function `_dl_start' is the real entry point; | |
197 | its return value is the user program's entry point. */ | |
198 | ||
199 | #define RTLD_START __asm__ ("\ | |
9c4c0024 UD |
200 | .text |
201 | .globl _start | |
202 | .type _start, @function | |
203 | .align 32 | |
f41c8091 UD |
204 | _start: |
205 | /* Allocate space for functions to drop their arguments. */ | |
206 | sub %sp, 6*4, %sp | |
207 | /* Pass pointer to argument block to _dl_start. */ | |
208 | call _dl_start | |
209 | add %sp, 22*4, %o0 | |
210 | /* FALTHRU */ | |
9c4c0024 UD |
211 | .globl _dl_start_user |
212 | .type _dl_start_user, @function | |
f41c8091 UD |
213 | _dl_start_user: |
214 | /* Load the PIC register. */ | |
215 | 1: call 2f | |
216 | sethi %hi(_GLOBAL_OFFSET_TABLE_-(1b-.)), %l7 | |
217 | 2: or %l7, %lo(_GLOBAL_OFFSET_TABLE_-(1b-.)), %l7 | |
218 | add %l7, %o7, %l7 | |
219 | /* Save the user entry point address in %l0 */ | |
220 | mov %o0, %l0 | |
9c4c0024 UD |
221 | /* Store the highest stack address. */ |
222 | sethi %hi(__libc_stack_end), %g2 | |
223 | or %g2, %lo(__libc_stack_end), %g2 | |
224 | ld [%l7 + %g2], %l1 | |
225 | add %sp, 6*4, %l2 | |
226 | st %l2, [%l1] | |
f41c8091 UD |
227 | /* See if we were run as a command with the executable file name as an |
228 | extra leading argument. If so, adjust the contents of the stack. */ | |
229 | sethi %hi(_dl_skip_args), %g2 | |
230 | or %g2, %lo(_dl_skip_args), %g2 | |
231 | ld [%l7+%g2], %i0 | |
232 | ld [%i0], %i0 | |
233 | tst %i0 | |
234 | beq 3f | |
235 | nop | |
236 | /* Find out how far to shift. */ | |
237 | ld [%sp+22*4], %i1 /* load argc */ | |
238 | sub %i1, %i0, %i1 | |
239 | sll %i0, 2, %i2 | |
240 | st %i1, [%sp+22*4] | |
241 | add %sp, 23*4, %i1 | |
242 | add %i1, %i2, %i2 | |
243 | /* Copy down argv */ | |
244 | 21: ld [%i2], %i3 | |
245 | add %i2, 4, %i2 | |
246 | tst %i3 | |
247 | st %i3, [%i1] | |
248 | bne 21b | |
249 | add %i1, 4, %i1 | |
250 | /* Copy down env */ | |
251 | 22: ld [%i2], %i3 | |
252 | add %i2, 4, %i2 | |
253 | tst %i3 | |
254 | st %i3, [%i1] | |
255 | bne 22b | |
256 | add %i1, 4, %i1 | |
257 | /* Copy down auxiliary table. */ | |
258 | 23: ld [%i2], %i3 | |
259 | ld [%i2+4], %i4 | |
260 | add %i2, 8, %i2 | |
261 | tst %i3 | |
262 | st %i3, [%i1] | |
263 | st %i4, [%i1+4] | |
264 | bne 23b | |
265 | add %i1, 8, %i1 | |
9c4c0024 | 266 | /* Load searchlist of the main object to pass to _dl_init_next. */ |
25c849b1 RH |
267 | 3: sethi %hi(_dl_main_searchlist), %g1 |
268 | or %g1, %lo(_dl_main_searchlist), %g1 | |
f41c8091 | 269 | ld [%l7+%g1], %l1 |
25c849b1 | 270 | ld [%l1], %l1 |
f41c8091 UD |
271 | /* Call _dl_init_next to return the address of an initializer to run. */ |
272 | 4: call _dl_init_next | |
273 | mov %l1, %o0 | |
274 | tst %o0 | |
275 | beq 5f | |
276 | nop | |
277 | jmpl %o0, %o7 | |
9c4c0024 | 278 | sub %o7, 28, %o7 |
f41c8091 UD |
279 | /* Clear the startup flag. */ |
280 | 5: sethi %hi(_dl_starting_up), %g1 | |
281 | or %g1, %lo(_dl_starting_up), %g1 | |
282 | ld [%l7+%g1], %g1 | |
283 | st %g0, [%g1] | |
284 | /* Pass our finalizer function to the user in %g1. */ | |
285 | sethi %hi(_dl_fini), %g1 | |
286 | or %g1, %lo(_dl_fini), %g1 | |
287 | ld [%l7+%g1], %g1 | |
288 | /* Jump to the user's entry point and deallocate the extra stack we got. */ | |
289 | jmp %l0 | |
290 | add %sp, 6*4, %sp | |
9c4c0024 UD |
291 | .size _dl_start_user, . - _dl_start_user |
292 | .previous"); | |
f41c8091 | 293 | |
a2b08ee5 UD |
294 | static inline void |
295 | elf_machine_fixup_plt (struct link_map *map, const Elf32_Rela *reloc, | |
296 | Elf32_Addr *reloc_addr, Elf32_Addr value) | |
297 | { | |
4194bc66 RH |
298 | #ifndef RTLD_BOOTSTRAP |
299 | /* Note that we don't mask the hwcap here, as the flush is essential to | |
300 | functionality on those cpu's that implement it. */ | |
b7e6f7bf RH |
301 | unsigned long *hwcap; |
302 | int do_flush; | |
303 | weak_extern (_dl_hwcap); | |
304 | hwcap = WEAKADDR(_dl_hwcap); | |
305 | do_flush = (!hwcap || (*hwcap & HWCAP_SPARC_FLUSH)); | |
4194bc66 | 306 | #else |
bf47fa23 UD |
307 | /* Unfortunately, this is necessary, so that we can ensure |
308 | ld.so will not execute corrupt PLT entry instructions. */ | |
309 | const int do_flush = 1; | |
4194bc66 | 310 | #endif |
dfd2257a | 311 | |
a2b08ee5 UD |
312 | /* For thread safety, write the instructions from the bottom and |
313 | flush before we overwrite the critical "b,a". This of course | |
314 | need not be done during bootstrapping, since there are no threads. | |
315 | But we also can't tell if we _can_ use flush, so don't. */ | |
316 | ||
317 | reloc_addr[2] = OPCODE_JMP_G1 | (value & 0x3ff); | |
4194bc66 | 318 | if (do_flush) |
a2b08ee5 | 319 | __asm __volatile ("flush %0+8" : : "r"(reloc_addr)); |
a2b08ee5 UD |
320 | |
321 | reloc_addr[1] = OPCODE_SETHI_G1 | (value >> 10); | |
4194bc66 | 322 | if (do_flush) |
a2b08ee5 | 323 | __asm __volatile ("flush %0+4" : : "r"(reloc_addr)); |
a2b08ee5 UD |
324 | } |
325 | ||
dfd2257a UD |
326 | /* Return the final value of a plt relocation. */ |
327 | static inline Elf32_Addr | |
328 | elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc, | |
329 | Elf32_Addr value) | |
330 | { | |
331 | return value + reloc->r_addend; | |
332 | } | |
333 | ||
ceb2d9aa | 334 | #ifdef RESOLVE |
a2b08ee5 | 335 | |
4f54cdb1 RM |
336 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). |
337 | MAP is the object containing the reloc. */ | |
338 | ||
339 | static inline void | |
0d8733c4 | 340 | elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, |
8f2ece69 UD |
341 | const Elf32_Sym *sym, const struct r_found_version *version, |
342 | Elf32_Addr *const reloc_addr) | |
4f54cdb1 | 343 | { |
4194bc66 RH |
344 | #ifndef RTLD_BOOTSTRAP |
345 | /* This is defined in rtld.c, but nowhere in the static libc.a; make the | |
346 | reference weak so static programs can still link. This declaration | |
347 | cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP) | |
348 | because rtld.c contains the common defn for _dl_rtld_map, which is | |
349 | incompatible with a weak decl in the same file. */ | |
350 | weak_extern (_dl_rtld_map); | |
351 | #endif | |
352 | ||
bc9f6000 | 353 | if (ELF32_R_TYPE (reloc->r_info) == R_SPARC_RELATIVE) |
993b3242 UD |
354 | { |
355 | #ifndef RTLD_BOOTSTRAP | |
356 | if (map != &_dl_rtld_map) /* Already done in rtld itself. */ | |
357 | #endif | |
358 | *reloc_addr += map->l_addr + reloc->r_addend; | |
359 | } | |
bc9f6000 | 360 | else |
4f54cdb1 | 361 | { |
1f07e617 | 362 | const Elf32_Sym *const refsym = sym; |
bc9f6000 UD |
363 | Elf32_Addr value; |
364 | if (sym->st_shndx != SHN_UNDEF && | |
365 | ELF32_ST_BIND (sym->st_info) == STB_LOCAL) | |
366 | value = map->l_addr; | |
367 | else | |
ceb2d9aa | 368 | { |
bc9f6000 UD |
369 | value = RESOLVE (&sym, version, ELF32_R_TYPE (reloc->r_info)); |
370 | if (sym) | |
371 | value += sym->st_value; | |
ceb2d9aa | 372 | } |
bc9f6000 | 373 | value += reloc->r_addend; /* Assume copy relocs have zero addend. */ |
ceb2d9aa | 374 | |
bc9f6000 UD |
375 | switch (ELF32_R_TYPE (reloc->r_info)) |
376 | { | |
377 | case R_SPARC_COPY: | |
9c4c0024 UD |
378 | if (sym == NULL) |
379 | /* This can happen in trace mode if an object could not be | |
380 | found. */ | |
381 | break; | |
cf29ffbe UD |
382 | if (sym->st_size > refsym->st_size |
383 | || (_dl_verbose && sym->st_size < refsym->st_size)) | |
1f07e617 | 384 | { |
60c96635 | 385 | extern char **_dl_argv; |
1f07e617 UD |
386 | const char *strtab; |
387 | ||
f420344c | 388 | strtab = (const void *) map->l_info[DT_STRTAB]->d_un.d_ptr; |
60c96635 UD |
389 | _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>", |
390 | ": Symbol `", strtab + refsym->st_name, | |
1f07e617 UD |
391 | "' has different size in shared object, " |
392 | "consider re-linking\n", NULL); | |
393 | } | |
394 | memcpy (reloc_addr, (void *) value, MIN (sym->st_size, | |
395 | refsym->st_size)); | |
bc9f6000 UD |
396 | break; |
397 | case R_SPARC_GLOB_DAT: | |
398 | case R_SPARC_32: | |
399 | *reloc_addr = value; | |
400 | break; | |
401 | case R_SPARC_JMP_SLOT: | |
a2b08ee5 | 402 | elf_machine_fixup_plt(map, reloc, reloc_addr, value); |
bc9f6000 UD |
403 | break; |
404 | case R_SPARC_8: | |
405 | *(char *) reloc_addr = value; | |
406 | break; | |
407 | case R_SPARC_16: | |
408 | *(short *) reloc_addr = value; | |
409 | break; | |
410 | case R_SPARC_DISP8: | |
411 | *(char *) reloc_addr = (value - (Elf32_Addr) reloc_addr); | |
412 | break; | |
413 | case R_SPARC_DISP16: | |
414 | *(short *) reloc_addr = (value - (Elf32_Addr) reloc_addr); | |
415 | break; | |
416 | case R_SPARC_DISP32: | |
417 | *reloc_addr = (value - (Elf32_Addr) reloc_addr); | |
418 | break; | |
419 | case R_SPARC_LO10: | |
420 | *reloc_addr = (*reloc_addr & ~0x3ff) | (value & 0x3ff); | |
421 | break; | |
422 | case R_SPARC_WDISP30: | |
423 | *reloc_addr = ((*reloc_addr & 0xc0000000) | |
424 | | ((value - (unsigned int) reloc_addr) >> 2)); | |
425 | break; | |
426 | case R_SPARC_HI22: | |
427 | *reloc_addr = (*reloc_addr & 0xffc00000) | (value >> 10); | |
428 | break; | |
429 | case R_SPARC_NONE: /* Alright, Wilbur. */ | |
430 | break; | |
431 | default: | |
421c80d2 | 432 | _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 0); |
bc9f6000 UD |
433 | break; |
434 | } | |
4f54cdb1 RM |
435 | } |
436 | } | |
437 | ||
438 | static inline void | |
421c80d2 RM |
439 | elf_machine_lazy_rel (struct link_map *map, |
440 | Elf32_Addr l_addr, const Elf32_Rela *reloc) | |
4f54cdb1 RM |
441 | { |
442 | switch (ELF32_R_TYPE (reloc->r_info)) | |
443 | { | |
444 | case R_SPARC_NONE: | |
445 | break; | |
446 | case R_SPARC_JMP_SLOT: | |
447 | break; | |
448 | default: | |
421c80d2 | 449 | _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 1); |
4f54cdb1 RM |
450 | break; |
451 | } | |
452 | } | |
453 | ||
0d8733c4 | 454 | #endif /* RESOLVE */ |