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