]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/sparc/sparc64/dl-machine.h
2000-05-08 Jakub Jelinek <jakub@redhat.com>
[thirdparty/glibc.git] / sysdeps / sparc / sparc64 / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions. Sparc64 version.
2 Copyright (C) 1997, 1998, 1999, 2000 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 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.
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 Library General Public License for more details.
14
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. */
19
20 #define ELF_MACHINE_NAME "sparc64"
21
22 #include <string.h>
23 #include <sys/param.h>
24 #include <ldsodefs.h>
25 #include <sysdep.h>
26
27 #define ELF64_R_TYPE_ID(info) ((info) & 0xff)
28 #define ELF64_R_TYPE_DATA(info) ((info) >> 8)
29
30 /* Return nonzero iff E_MACHINE is compatible with the running host. */
31 static inline int
32 elf_machine_matches_host (Elf64_Half e_machine)
33 {
34 return e_machine == EM_SPARCV9;
35 }
36
37 /* Return the link-time address of _DYNAMIC. Conveniently, this is the
38 first element of the GOT. This must be inlined in a function which
39 uses global data. */
40 static inline Elf64_Addr
41 elf_machine_dynamic (void)
42 {
43 register Elf64_Addr *elf_pic_register __asm__("%l7");
44
45 return *elf_pic_register;
46 }
47
48 /* Return the run-time load address of the shared object. */
49 static inline Elf64_Addr
50 elf_machine_load_address (void)
51 {
52 register Elf64_Addr elf_pic_register __asm__("%l7");
53 Elf64_Addr pc, la;
54
55 /* Utilize the fact that a local .got entry will be partially
56 initialized at startup awaiting its RELATIVE fixup. */
57
58 __asm("sethi %%hi(.Load_address), %1\n"
59 ".Load_address:\n\t"
60 "rd %%pc, %0\n\t"
61 "or %1, %%lo(.Load_address), %1\n\t"
62 : "=r"(pc), "=r"(la));
63
64 return pc - *(Elf64_Addr *)(elf_pic_register + la);
65 }
66
67 /* We have 4 cases to handle. And we code different code sequences
68 for each one. I love V9 code models... */
69 static inline Elf64_Addr
70 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
71 const Elf64_Rela *reloc,
72 Elf64_Addr *reloc_addr, Elf64_Addr value)
73 {
74 unsigned int *insns = (unsigned int *) reloc_addr;
75 Elf64_Addr plt_vaddr = (Elf64_Addr) reloc_addr;
76
77 /* Now move plt_vaddr up to the call instruction. */
78 plt_vaddr += (2 * 4);
79
80 /* PLT entries .PLT32768 and above look always the same. */
81 if (__builtin_expect (reloc->r_addend, 0) != 0)
82 {
83 *reloc_addr = value - map->l_addr;
84 }
85 /* 32-bit Sparc style, the target is in the lower 32-bits of
86 address space. */
87 else if ((value >> 32) == 0)
88 {
89 /* sethi %hi(target), %g1
90 jmpl %g1 + %lo(target), %g0 */
91
92 insns[2] = 0x81c06000 | (value & 0x3ff);
93 __asm __volatile ("flush %0 + 8" : : "r" (insns));
94
95 insns[1] = 0x03000000 | ((unsigned int)(value >> 10));
96 __asm __volatile ("flush %0 + 4" : : "r" (insns));
97 }
98 /* We can also get somewhat simple sequences if the distance between
99 the target and the PLT entry is within +/- 2GB. */
100 else if ((plt_vaddr > value
101 && ((plt_vaddr - value) >> 32) == 0)
102 || (value > plt_vaddr
103 && ((value - plt_vaddr) >> 32) == 0))
104 {
105 unsigned int displacement;
106
107 if (plt_vaddr > value)
108 displacement = (0 - (plt_vaddr - value));
109 else
110 displacement = value - plt_vaddr;
111
112 /* mov %o7, %g1
113 call displacement
114 mov %g1, %o7 */
115
116 insns[3] = 0x9e100001;
117 __asm __volatile ("flush %0 + 12" : : "r" (insns));
118
119 insns[2] = 0x40000000 | (displacement >> 2);
120 __asm __volatile ("flush %0 + 8" : : "r" (insns));
121
122 insns[1] = 0x8210000f;
123 __asm __volatile ("flush %0 + 4" : : "r" (insns));
124 }
125 /* Worst case, ho hum... */
126 else
127 {
128 unsigned int high32 = (value >> 32);
129 unsigned int low32 = (unsigned int) value;
130
131 /* ??? Some tricks can be stolen from the sparc64 egcs backend
132 constant formation code I wrote. -DaveM */
133
134 /* sethi %hh(value), %g1
135 sethi %lm(value), %g5
136 or %g1, %hm(value), %g1
137 or %g5, %lo(value), %g5
138 sllx %g1, 32, %g1
139 jmpl %g1 + %g5, %g0
140 nop */
141
142 insns[6] = 0x81c04005;
143 __asm __volatile ("flush %0 + 24" : : "r" (insns));
144
145 insns[5] = 0x83287020;
146 __asm __volatile ("flush %0 + 20" : : "r" (insns));
147
148 insns[4] = 0x8a116000 | (low32 & 0x3ff);
149 __asm __volatile ("flush %0 + 16" : : "r" (insns));
150
151 insns[3] = 0x82106000 | (high32 & 0x3ff);
152 __asm __volatile ("flush %0 + 12" : : "r" (insns));
153
154 insns[2] = 0x0b000000 | (low32 >> 10);
155 __asm __volatile ("flush %0 + 8" : : "r" (insns));
156
157 insns[1] = 0x03000000 | (high32 >> 10);
158 __asm __volatile ("flush %0 + 4" : : "r" (insns));
159 }
160
161 return value;
162 }
163
164 /* Return the final value of a plt relocation. */
165 static inline Elf64_Addr
166 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
167 Elf64_Addr value)
168 {
169 return value + reloc->r_addend;
170 }
171
172 #ifdef RESOLVE
173
174 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
175 MAP is the object containing the reloc. */
176
177 static inline void
178 elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
179 const Elf64_Sym *sym, const struct r_found_version *version,
180 Elf64_Addr *const reloc_addr)
181 {
182 #ifndef RTLD_BOOTSTRAP
183 /* This is defined in rtld.c, but nowhere in the static libc.a; make the
184 reference weak so static programs can still link. This declaration
185 cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP)
186 because rtld.c contains the common defn for _dl_rtld_map, which is
187 incompatible with a weak decl in the same file. */
188 weak_extern (_dl_rtld_map);
189 #endif
190
191 if (ELF64_R_TYPE_ID (reloc->r_info) == R_SPARC_RELATIVE)
192 {
193 #ifndef RTLD_BOOTSTRAP
194 if (map != &_dl_rtld_map) /* Already done in rtld itself. */
195 #endif
196 *reloc_addr = map->l_addr + reloc->r_addend;
197 }
198 else if (ELF64_R_TYPE_ID (reloc->r_info) != R_SPARC_NONE) /* Who is Wilbur? */
199 {
200 const Elf64_Sym *const refsym = sym;
201 Elf64_Addr value;
202 if (sym->st_shndx != SHN_UNDEF &&
203 ELF64_ST_BIND (sym->st_info) == STB_LOCAL)
204 value = map->l_addr;
205 else
206 {
207 value = RESOLVE (&sym, version, ELF64_R_TYPE_ID (reloc->r_info));
208 if (sym)
209 value += sym->st_value;
210 }
211 value += reloc->r_addend; /* Assume copy relocs have zero addend. */
212
213 switch (ELF64_R_TYPE_ID (reloc->r_info))
214 {
215 case R_SPARC_COPY:
216 if (sym == NULL)
217 /* This can happen in trace mode if an object could not be
218 found. */
219 break;
220 if (sym->st_size > refsym->st_size
221 || (_dl_verbose && sym->st_size < refsym->st_size))
222 {
223 extern char **_dl_argv;
224 const char *strtab;
225
226 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
227 _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>",
228 ": Symbol `", strtab + refsym->st_name,
229 "' has different size in shared object, "
230 "consider re-linking\n", NULL);
231 }
232 memcpy (reloc_addr, (void *) value, MIN (sym->st_size,
233 refsym->st_size));
234 break;
235
236 case R_SPARC_64:
237 case R_SPARC_GLOB_DAT:
238 *reloc_addr = value;
239 break;
240 case R_SPARC_8:
241 *(char *) reloc_addr = value;
242 break;
243 case R_SPARC_16:
244 *(short *) reloc_addr = value;
245 break;
246 case R_SPARC_32:
247 *(unsigned int *) reloc_addr = value;
248 break;
249 case R_SPARC_DISP8:
250 *(char *) reloc_addr = (value - (Elf64_Addr) reloc_addr);
251 break;
252 case R_SPARC_DISP16:
253 *(short *) reloc_addr = (value - (Elf64_Addr) reloc_addr);
254 break;
255 case R_SPARC_DISP32:
256 *(unsigned int *) reloc_addr = (value - (Elf64_Addr) reloc_addr);
257 break;
258 case R_SPARC_WDISP30:
259 *(unsigned int *) reloc_addr =
260 ((*(unsigned int *)reloc_addr & 0xc0000000) |
261 ((value - (Elf64_Addr) reloc_addr) >> 2));
262 break;
263
264 /* MEDLOW code model relocs */
265 case R_SPARC_LO10:
266 *(unsigned int *) reloc_addr =
267 ((*(unsigned int *)reloc_addr & ~0x3ff) |
268 (value & 0x3ff));
269 break;
270 case R_SPARC_HI22:
271 *(unsigned int *) reloc_addr =
272 ((*(unsigned int *)reloc_addr & 0xffc00000) |
273 (value >> 10));
274 break;
275 case R_SPARC_OLO10:
276 *(unsigned int *) reloc_addr =
277 ((*(unsigned int *)reloc_addr & ~0x1fff) |
278 (((value & 0x3ff) + ELF64_R_TYPE_DATA (reloc->r_info)) & 0x1fff));
279 break;
280
281 /* MEDMID code model relocs */
282 case R_SPARC_H44:
283 *(unsigned int *) reloc_addr =
284 ((*(unsigned int *)reloc_addr & 0xffc00000) |
285 (value >> 22));
286 break;
287 case R_SPARC_M44:
288 *(unsigned int *) reloc_addr =
289 ((*(unsigned int *)reloc_addr & ~0x3ff) |
290 ((value >> 12) & 0x3ff));
291 break;
292 case R_SPARC_L44:
293 *(unsigned int *) reloc_addr =
294 ((*(unsigned int *)reloc_addr & ~0xfff) |
295 (value & 0xfff));
296 break;
297
298 /* MEDANY code model relocs */
299 case R_SPARC_HH22:
300 *(unsigned int *) reloc_addr =
301 ((*(unsigned int *)reloc_addr & 0xffc00000) |
302 (value >> 42));
303 break;
304 case R_SPARC_HM10:
305 *(unsigned int *) reloc_addr =
306 ((*(unsigned int *)reloc_addr & ~0x3ff) |
307 ((value >> 32) & 0x3ff));
308 break;
309 case R_SPARC_LM22:
310 *(unsigned int *) reloc_addr =
311 ((*(unsigned int *)reloc_addr & 0xffc00000) |
312 ((value >> 10) & 0x003fffff));
313 break;
314
315 case R_SPARC_JMP_SLOT:
316 elf_machine_fixup_plt(map, NULL, reloc, reloc_addr, value);
317 break;
318
319 default:
320 _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 0);
321 break;
322 }
323 }
324 }
325
326 static inline void
327 elf_machine_lazy_rel (struct link_map *map,
328 Elf64_Addr l_addr, const Elf64_Rela *reloc)
329 {
330 switch (ELF64_R_TYPE (reloc->r_info))
331 {
332 case R_SPARC_NONE:
333 break;
334 case R_SPARC_JMP_SLOT:
335 break;
336 default:
337 _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 1);
338 break;
339 }
340 }
341
342 #endif /* RESOLVE */
343
344 /* Nonzero iff TYPE should not be allowed to resolve to one of
345 the main executable's symbols, as for a COPY reloc. */
346 #define elf_machine_lookup_noexec_p(type) ((type) == R_SPARC_COPY)
347
348 /* Nonzero iff TYPE describes relocation of a PLT entry, so
349 PLT entries should not be allowed to define the value. */
350 #define elf_machine_lookup_noplt_p(type) ((type) == R_SPARC_JMP_SLOT)
351
352 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
353 #define ELF_MACHINE_JMP_SLOT R_SPARC_JMP_SLOT
354
355 /* The SPARC never uses Elf64_Rel relocations. */
356 #define ELF_MACHINE_NO_REL 1
357
358 /* The SPARC overlaps DT_RELA and DT_PLTREL. */
359 #define ELF_MACHINE_PLTREL_OVERLAP 1
360
361 /* Set up the loaded object described by L so its unrelocated PLT
362 entries will jump to the on-demand fixup code in dl-runtime.c. */
363
364 static inline int
365 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
366 {
367 if (l->l_info[DT_JMPREL] && lazy)
368 {
369 extern void _dl_runtime_resolve_0 (void);
370 extern void _dl_runtime_resolve_1 (void);
371 extern void _dl_runtime_profile_0 (void);
372 extern void _dl_runtime_profile_1 (void);
373 Elf64_Addr res0_addr, res1_addr;
374 unsigned int *plt = (void *) D_PTR (l, l_info[DT_PLTGOT]);
375
376 if (! profile)
377 {
378 res0_addr = (Elf64_Addr) &_dl_runtime_resolve_0;
379 res1_addr = (Elf64_Addr) &_dl_runtime_resolve_1;
380 }
381 else
382 {
383 res0_addr = (Elf64_Addr) &_dl_runtime_profile_0;
384 res1_addr = (Elf64_Addr) &_dl_runtime_profile_1;
385 if (_dl_name_match_p (_dl_profile, l))
386 _dl_profile_map = l;
387 }
388
389 /* PLT0 looks like:
390
391 save %sp, -192, %sp
392 sethi %hh(_dl_runtime_{resolve,profile}_0), %l0
393 sethi %lm(_dl_runtime_{resolve,profile}_0), %l1
394 or %l0, %hm(_dl_runtime_{resolve,profile}_0), %l0
395 or %l1, %lo(_dl_runtime_{resolve,profile}_0), %l1
396 sllx %l0, 32, %l0
397 jmpl %l0 + %l1, %l6
398 sethi %hi(0xffc00), %l2
399 */
400
401 plt[0] = 0x9de3bf40;
402 plt[1] = 0x21000000 | (res0_addr >> (64 - 22));
403 plt[2] = 0x23000000 | ((res0_addr >> 10) & 0x003fffff);
404 plt[3] = 0xa0142000 | ((res0_addr >> 32) & 0x3ff);
405 plt[4] = 0xa2146000 | (res0_addr & 0x3ff);
406 plt[5] = 0xa12c3020;
407 plt[6] = 0xadc40011;
408 plt[7] = 0x250003ff;
409
410 /* PLT1 looks like:
411
412 save %sp, -192, %sp
413 sethi %hh(_dl_runtime_{resolve,profile}_1), %l0
414 sethi %lm(_dl_runtime_{resolve,profile}_1), %l1
415 or %l0, %hm(_dl_runtime_{resolve,profile}_1), %l0
416 or %l1, %lo(_dl_runtime_{resolve,profile}_1), %l1
417 sllx %l0, 32, %l0
418 jmpl %l0 + %l1, %l6
419 srlx %g1, 12, %o1
420 */
421
422 plt[8 + 0] = 0x9de3bf40;
423 plt[8 + 1] = 0x21000000 | (res1_addr >> (64 - 22));
424 plt[8 + 2] = 0x23000000 | ((res1_addr >> 10) & 0x003fffff);
425 plt[8 + 3] = 0xa0142000 | ((res1_addr >> 32) & 0x3ff);
426 plt[8 + 4] = 0xa2146000 | (res1_addr & 0x3ff);
427 plt[8 + 5] = 0xa12c3020;
428 plt[8 + 6] = 0xadc40011;
429 plt[8 + 7] = 0x9330700c;
430
431 /* Now put the magic cookie at the beginning of .PLT3
432 Entry .PLT4 is unused by this implementation. */
433 *((struct link_map **)(&plt[16 + 0])) = l;
434 }
435
436 return lazy;
437 }
438
439 /* This code is used in dl-runtime.c to call the `fixup' function
440 and then redirect to the address it returns. */
441 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \
442 asm ("\
443 .text
444 .globl " #tramp_name "_0
445 .type " #tramp_name "_0, @function
446 .align 32
447 " #tramp_name "_0:
448 ! sethi %hi(1047552), %l2 - Done in .PLT0
449 ldx [%l6 + 32 + 8], %o0
450 sub %g1, %l6, %l0
451 xor %l2, -1016, %l2
452 sethi %hi(5120), %l3
453 add %l0, %l2, %l0
454 sethi %hi(32768), %l4
455 udivx %l0, %l3, %l3
456 sllx %l3, 2, %l1
457 add %l1, %l3, %l1
458 sllx %l1, 10, %l2
459 sllx %l1, 5, %l1
460 sub %l0, %l2, %l0
461 udivx %l0, 24, %l0
462 add %l0, %l4, %l0
463 add %l1, %l0, %l1
464 add %l1, %l1, %l0
465 add %l0, %l1, %l0
466 mov %i7, %o2
467 call " #fixup_name "
468 sllx %l0, 3, %o1
469 jmp %o0
470 restore
471 .size " #tramp_name "_0, . - " #tramp_name "_0
472
473 .globl " #tramp_name "_1
474 .type " #tramp_name "_1, @function
475 .align 32
476 " #tramp_name "_1:
477 ! srlx %g1, 12, %o1 - Done in .PLT1
478 ldx [%l6 + 8], %o0
479 add %o1, %o1, %o3
480 mov %i7, %o2
481 call " #fixup_name "
482 add %o1, %o3, %o1
483 jmp %o0
484 restore
485 .size " #tramp_name "_1, . - " #tramp_name "_1
486 .previous");
487
488 #ifndef PROF
489 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
490 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
491 TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
492 #else
493 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
494 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
495 TRAMPOLINE_TEMPLATE (_dl_runtime_profile, fixup);
496 #endif
497
498 /* The PLT uses Elf64_Rela relocs. */
499 #define elf_machine_relplt elf_machine_rela
500
501 /* Initial entry point code for the dynamic linker.
502 The C function `_dl_start' is the real entry point;
503 its return value is the user program's entry point. */
504
505 #define __S1(x) #x
506 #define __S(x) __S1(x)
507
508 #define RTLD_START __asm__ ( "\
509 .text
510 .global _start
511 .type _start, @function
512 .align 32
513 _start:
514 /* Make room for functions to drop their arguments on the stack. */
515 sub %sp, 6*8, %sp
516 /* Pass pointer to argument block to _dl_start. */
517 call _dl_start
518 add %sp," __S(STACK_BIAS) "+22*8,%o0
519 /* FALLTHRU */
520 .size _start, .-_start
521
522 .global _dl_start_user
523 .type _dl_start_user, @function
524 _dl_start_user:
525 /* Load the GOT register. */
526 1: call 11f
527 sethi %hi(_GLOBAL_OFFSET_TABLE_-(1b-.)),%l7
528 11: or %l7,%lo(_GLOBAL_OFFSET_TABLE_-(1b-.)),%l7
529 add %l7,%o7,%l7
530 /* Save the user entry point address in %l0. */
531 mov %o0,%l0
532 /* Store the highest stack address. */
533 sethi %hi(__libc_stack_end), %g5
534 or %g5, %lo(__libc_stack_end), %g5
535 ldx [%l7 + %g5], %l1
536 add %sp, 6*8, %l2
537 stx %l2, [%l1]
538 /* See if we were run as a command with the executable file name as an
539 extra leading argument. If so, we must shift things around since we
540 must keep the stack doubleword aligned. */
541 sethi %hi(_dl_skip_args), %g5
542 or %g5, %lo(_dl_skip_args), %g5
543 ldx [%l7+%g5], %i0
544 ld [%i0], %i0
545 brz,pt %i0, 2f
546 ldx [%sp+" __S(STACK_BIAS) "+22*8], %i5
547 /* Find out how far to shift. */
548 sub %i5, %i0, %i5
549 sllx %i0, 3, %i2
550 stx %i5, [%sp+" __S(STACK_BIAS) "+22*8]
551 add %sp, " __S(STACK_BIAS) "+23*8, %i1
552 add %i1, %i2, %i2
553 /* Copy down argv. */
554 12: ldx [%i2], %i3
555 add %i2, 8, %i2
556 stx %i3, [%i1]
557 brnz,pt %i3, 12b
558 add %i1, 8, %i1
559 /* Copy down envp. */
560 13: ldx [%i2], %i3
561 add %i2, 8, %i2
562 stx %i3, [%i1]
563 brnz,pt %i3, 13b
564 add %i1, 8, %i1
565 /* Copy down auxiliary table. */
566 14: ldx [%i2], %i3
567 ldx [%i2+8], %i4
568 add %i2, 16, %i2
569 stx %i3, [%i1]
570 stx %i4, [%i1+8]
571 brnz,pt %i3, 13b
572 add %i1, 16, %i1
573 /* %o0 = _dl_loaded, %o1 = argc, %o2 = argv, %o3 = envp. */
574 2: sethi %hi(_dl_loaded), %o0
575 add %sp, " __S(STACK_BIAS) "+23*8, %o2
576 orcc %o0, %lo(_dl_loaded), %o0
577 sllx %i5, 3, %o3
578 ldx [%l7+%o0], %o0
579 add %o3, 8, %o3
580 mov %i5, %o1
581 add %o2, %o3, %o3
582 call _dl_init
583 ldx [%o0], %o0
584 /* Pass our finalizer function to the user in %g1. */
585 sethi %hi(_dl_fini), %g1
586 or %g1, %lo(_dl_fini), %g1
587 ldx [%l7+%g1], %g1
588 /* Jump to the user's entry point and deallocate the extra stack we got. */
589 jmp %l0
590 add %sp, 6*8, %sp
591 .size _dl_start_user, . - _dl_start_user
592 .previous");