1 /* Machine-dependent ELF dynamic relocation inline functions. Alpha version.
2 Copyright (C) 1996-2002, 2003, 2004 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Richard Henderson <rth@tamu.edu>.
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
21 /* This was written in the absence of an ABI -- don't expect
22 it to remain unchanged. */
25 #define dl_machine_h 1
27 #define ELF_MACHINE_NAME "alpha"
32 /* Mask identifying addresses reserved for the user program,
33 where the dynamic linker should not map anything. */
34 #define ELF_MACHINE_USER_ADDRESS_MASK 0x120000000UL
36 /* Return nonzero iff ELF header is compatible with the running host. */
38 elf_machine_matches_host (const Elf64_Ehdr
*ehdr
)
40 return ehdr
->e_machine
== EM_ALPHA
;
43 /* Return the link-time address of _DYNAMIC. The multiple-got-capable
44 linker no longer allocates the first .got entry for this. But not to
45 worry, no special tricks are needed. */
46 static inline Elf64_Addr
47 elf_machine_dynamic (void)
49 #ifndef NO_AXP_MULTI_GOT_LD
50 return (Elf64_Addr
) &_DYNAMIC
;
52 register Elf64_Addr
*gp
__asm__ ("$29");
57 /* Return the run-time load address of the shared object. */
58 static inline Elf64_Addr
59 elf_machine_load_address (void)
61 /* NOTE: While it is generally unfriendly to put data in the text
62 segment, it is only slightly less so when the "data" is an
63 instruction. While we don't have to worry about GLD just yet, an
64 optimizing linker might decide that our "data" is an unreachable
65 instruction and throw it away -- with the right switches, DEC's
66 linker will do this. What ought to happen is we should add
67 something to GAS to allow us access to the new GPREL_HI32/LO32
68 relocation types stolen from OSF/1 3.0. */
69 /* This code relies on the fact that BRADDR relocations do not
70 appear in dynamic relocation tables. Not that that would be very
71 useful anyway -- br/bsr has a 4MB range and the shared libraries
72 are usually many many terabytes away. */
87 zero_disp
= *(int *) dot
;
88 zero_disp
= (zero_disp
<< 43) >> 41;
90 return dot
- *(Elf64_Addr
*) (dot
+ 4 + zero_disp
);
93 /* Set up the loaded object described by L so its unrelocated PLT
94 entries will jump to the on-demand fixup code in dl-runtime.c. */
97 elf_machine_runtime_setup (struct link_map
*l
, int lazy
, int profile
)
100 extern void _dl_runtime_resolve (void);
101 extern void _dl_runtime_profile (void);
103 if (l
->l_info
[DT_JMPREL
] && lazy
)
105 /* The GOT entries for the functions in the PLT have not been
106 filled in yet. Their initial contents are directed to the
107 PLT which arranges for the dynamic linker to be called. */
108 plt
= D_PTR (l
, l_info
[DT_PLTGOT
]);
110 /* This function will be called to perform the relocation. */
112 *(Elf64_Addr
*)(plt
+ 16) = (Elf64_Addr
) &_dl_runtime_resolve
;
115 *(Elf64_Addr
*)(plt
+ 16) = (Elf64_Addr
) &_dl_runtime_profile
;
117 if (_dl_name_match_p (GLRO(dl_profile
), l
))
119 /* This is the object we are looking for. Say that we really
120 want profiling and the timers are started. */
121 GL(dl_profile_map
) = l
;
125 /* Identify this shared object */
126 *(Elf64_Addr
*)(plt
+ 24) = (Elf64_Addr
) l
;
128 /* If the first instruction of the plt entry is not
129 "br $28, plt0", we have to reinitialize .plt for lazy relocation. */
130 if (*(unsigned int *)(plt
+ 32) != 0xc39ffff7)
132 unsigned int val
= 0xc39ffff7;
133 unsigned int *slot
, *end
;
134 const Elf64_Rela
*rela
= (const Elf64_Rela
*)
135 D_PTR (l
, l_info
[DT_JMPREL
]);
136 Elf64_Addr l_addr
= l
->l_addr
;
138 /* br t12,.+4; ldq t12,12(t12); nop; jmp t12,(t12),.+4 */
139 *(unsigned long *)plt
= 0xa77b000cc3600000;
140 *(unsigned long *)(plt
+ 8) = 0x6b7b000047ff041f;
141 slot
= (unsigned int *)(plt
+ 32);
142 end
= (unsigned int *)(plt
+ 32
143 + l
->l_info
[DT_PLTRELSZ
]->d_un
.d_val
/ 2);
148 *(Elf64_Addr
*) rela
->r_offset
= (Elf64_Addr
) slot
- l_addr
;
159 /* This code is used in dl-runtime.c to call the `fixup' function
160 and then redirect to the address it returns. */
161 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name, IMB) \
162 extern void tramp_name (void); \
164 .globl " #tramp_name " \n\
165 .ent " #tramp_name " \n\
167 lda $sp, -44*8($sp) \n\
168 .frame $sp, 44*8, $26 \n\
169 /* Preserve all integer registers that C normally \n\
171 stq $26, 0*8($sp) \n\
181 stq $16, 10*8($sp) \n\
182 stq $17, 11*8($sp) \n\
183 stq $18, 12*8($sp) \n\
184 stq $19, 13*8($sp) \n\
185 stq $20, 14*8($sp) \n\
186 stq $21, 15*8($sp) \n\
187 stq $22, 16*8($sp) \n\
188 stq $23, 17*8($sp) \n\
189 stq $24, 18*8($sp) \n\
190 stq $25, 19*8($sp) \n\
191 stq $29, 20*8($sp) \n\
192 stt $f0, 21*8($sp) \n\
193 stt $f1, 22*8($sp) \n\
194 stt $f10, 23*8($sp) \n\
195 stt $f11, 24*8($sp) \n\
196 stt $f12, 25*8($sp) \n\
197 stt $f13, 26*8($sp) \n\
198 stt $f14, 27*8($sp) \n\
199 stt $f15, 28*8($sp) \n\
200 stt $f16, 29*8($sp) \n\
201 stt $f17, 30*8($sp) \n\
202 stt $f18, 31*8($sp) \n\
203 stt $f19, 32*8($sp) \n\
204 stt $f20, 33*8($sp) \n\
205 stt $f21, 34*8($sp) \n\
206 stt $f22, 35*8($sp) \n\
207 stt $f23, 36*8($sp) \n\
208 stt $f24, 37*8($sp) \n\
209 stt $f25, 38*8($sp) \n\
210 stt $f26, 39*8($sp) \n\
211 stt $f27, 40*8($sp) \n\
212 stt $f28, 41*8($sp) \n\
213 stt $f29, 42*8($sp) \n\
214 stt $f30, 43*8($sp) \n\
215 .mask 0x27ff01ff, -44*8 \n\
216 .fmask 0xfffffc03, -(44-21)*8 \n\
217 /* Set up our $gp */ \n\
221 /* Set up the arguments for fixup: */ \n\
222 /* $16 = link_map out of plt0 */ \n\
223 /* $17 = offset of reloc entry = ($28 - $27 - 20) /12 * 24 */\n\
224 /* $18 = return address */ \n\
225 subq $28, $27, $17 \n\
227 subq $17, 20, $17 \n\
229 addq $17, $17, $17 \n\
230 /* Do the fixup */ \n\
231 bsr $26, " #fixup_name " !samegp \n\
232 /* Move the destination address into position. */ \n\
234 /* Restore program registers. */ \n\
235 ldq $26, 0*8($sp) \n\
245 ldq $16, 10*8($sp) \n\
246 ldq $17, 11*8($sp) \n\
247 ldq $18, 12*8($sp) \n\
248 ldq $19, 13*8($sp) \n\
249 ldq $20, 14*8($sp) \n\
250 ldq $21, 15*8($sp) \n\
251 ldq $22, 16*8($sp) \n\
252 ldq $23, 17*8($sp) \n\
253 ldq $24, 18*8($sp) \n\
254 ldq $25, 19*8($sp) \n\
255 ldq $29, 20*8($sp) \n\
256 ldt $f0, 21*8($sp) \n\
257 ldt $f1, 22*8($sp) \n\
258 ldt $f10, 23*8($sp) \n\
259 ldt $f11, 24*8($sp) \n\
260 ldt $f12, 25*8($sp) \n\
261 ldt $f13, 26*8($sp) \n\
262 ldt $f14, 27*8($sp) \n\
263 ldt $f15, 28*8($sp) \n\
264 ldt $f16, 29*8($sp) \n\
265 ldt $f17, 30*8($sp) \n\
266 ldt $f18, 31*8($sp) \n\
267 ldt $f19, 32*8($sp) \n\
268 ldt $f20, 33*8($sp) \n\
269 ldt $f21, 34*8($sp) \n\
270 ldt $f22, 35*8($sp) \n\
271 ldt $f23, 36*8($sp) \n\
272 ldt $f24, 37*8($sp) \n\
273 ldt $f25, 38*8($sp) \n\
274 ldt $f26, 39*8($sp) \n\
275 ldt $f27, 40*8($sp) \n\
276 ldt $f28, 41*8($sp) \n\
277 ldt $f29, 42*8($sp) \n\
278 ldt $f30, 43*8($sp) \n\
279 /* Flush the Icache after having modified the .plt code. */\n\
281 /* Clean up and turn control to the destination */ \n\
282 lda $sp, 44*8($sp) \n\
287 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
288 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup, imb); \
289 TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup, /* nop */);
291 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
292 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup, imb); \
293 strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
296 /* Initial entry point code for the dynamic linker.
297 The C function `_dl_start' is the real entry point;
298 its return value is the user program's entry point. */
300 #define RTLD_START asm ("\
306 .frame $31,0,$31,0 \n\
308 0: ldgp $gp, 0($gp) \n\
310 /* Pass pointer to argument block to _dl_start. */ \n\
312 bsr $26, _dl_start !samegp \n\
315 .globl _dl_start_user \n\
316 .ent _dl_start_user \n\
318 .frame $31,0,$31,0 \n\
320 /* Save the user entry point address in s0. */ \n\
322 /* See if we were run as a command with the executable \n\
323 file name as an extra leading argument. */ \n\
324 ldah $1, _dl_skip_args($gp) !gprelhigh \n\
325 ldl $1, _dl_skip_args($1) !gprellow \n\
326 bne $1, $fixup_stack \n\
327 $fixup_stack_ret: \n\
328 /* The special initializer gets called with the stack \n\
329 just as the application's entry point will see it; \n\
330 it can switch stacks if it moves these contents \n\
332 " RTLD_START_SPECIAL_INIT " \n\
333 /* Call _dl_init(_dl_loaded, argc, argv, envp) to run \n\
335 ldah $16, _rtld_local($gp) !gprelhigh \n\
336 ldq $16, _rtld_local($16) !gprellow \n\
339 s8addq $17, 8, $19 \n\
340 addq $19, $18, $19 \n\
341 bsr $26, _dl_init_internal !samegp \n\
342 /* Pass our finalizer function to the user in $0. */ \n\
343 ldah $0, _dl_fini($gp) !gprelhigh \n\
344 lda $0, _dl_fini($0) !gprellow \n\
345 /* Jump to the user's entry point. */ \n\
349 /* Adjust the stack pointer to skip _dl_skip_args words.\n\
350 This involves copying everything down, since the \n\
351 stack pointer must always be 16-byte aligned. */ \n\
352 ldah $7, _dl_argv_internal($gp) !gprelhigh \n\
354 ldq $5, _dl_argv_internal($7) !gprellow \n\
357 s8addq $6, $5, $5 \n\
359 s8addq $1, $sp, $3 \n\
361 stq $5, _dl_argv_internal($7) !gprellow \n\
362 /* Copy down argv. */ \n\
368 /* Copy down envp. */ \n\
374 /* Copy down auxiliary table. */ \n\
382 br $fixup_stack_ret \n\
383 .end _dl_start_user \n\
387 #ifndef RTLD_START_SPECIAL_INIT
388 #define RTLD_START_SPECIAL_INIT /* nothing */
391 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry
392 or TLS variables, so undefined references should not be allowed
395 ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve
396 to one of the main executable's symbols, as for a COPY reloc.
397 This is unused on Alpha. */
399 #if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD)
400 #define elf_machine_type_class(type) \
401 (((type) == R_ALPHA_JMP_SLOT \
402 || (type) == R_ALPHA_DTPMOD64 \
403 || (type) == R_ALPHA_DTPREL64 \
404 || (type) == R_ALPHA_TPREL64) * ELF_RTYPE_CLASS_PLT)
406 #define elf_machine_type_class(type) \
407 (((type) == R_ALPHA_JMP_SLOT) * ELF_RTYPE_CLASS_PLT)
410 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
411 #define ELF_MACHINE_JMP_SLOT R_ALPHA_JMP_SLOT
413 /* The alpha never uses Elf64_Rel relocations. */
414 #define ELF_MACHINE_NO_REL 1
416 /* Fix up the instructions of a PLT entry to invoke the function
417 rather than the dynamic linker. */
418 static inline Elf64_Addr
419 elf_machine_fixup_plt (struct link_map
*l
, lookup_t t
,
420 const Elf64_Rela
*reloc
,
421 Elf64_Addr
*got_addr
, Elf64_Addr value
)
423 const Elf64_Rela
*rela_plt
;
427 /* Store the value we are going to load. */
430 /* Recover the PLT entry address by calculating reloc's index into the
431 .rela.plt, and finding that entry in the .plt. */
432 rela_plt
= (void *) D_PTR (l
, l_info
[DT_JMPREL
]);
433 plte
= (void *) (D_PTR (l
, l_info
[DT_PLTGOT
]) + 32);
434 plte
+= 3 * (reloc
- rela_plt
);
436 /* Find the displacement from the plt entry to the function. */
437 edisp
= (long int) (value
- (Elf64_Addr
)&plte
[3]) / 4;
439 if (edisp
>= -0x100000 && edisp
< 0x100000)
441 /* If we are in range, use br to perfect branch prediction and
442 elide the dependency on the address load. This case happens,
443 e.g., when a shared library call is resolved to the same library. */
446 hi
= value
- (Elf64_Addr
)&plte
[0];
448 hi
= (hi
- lo
) >> 16;
450 /* Emit "lda $27,lo($27)" */
451 plte
[1] = 0x237b0000 | (lo
& 0xffff);
453 /* Emit "br $31,function" */
454 plte
[2] = 0xc3e00000 | (edisp
& 0x1fffff);
456 /* Think about thread-safety -- the previous instructions must be
457 committed to memory before the first is overwritten. */
458 __asm__
__volatile__("wmb" : : : "memory");
460 /* Emit "ldah $27,hi($27)" */
461 plte
[0] = 0x277b0000 | (hi
& 0xffff);
465 /* Don't bother with the hint since we already know the hint is
466 wrong. Eliding it prevents the wrong page from getting pulled
470 hi
= (Elf64_Addr
)got_addr
- (Elf64_Addr
)&plte
[0];
472 hi
= (hi
- lo
) >> 16;
474 /* Emit "ldq $27,lo($27)" */
475 plte
[1] = 0xa77b0000 | (lo
& 0xffff);
477 /* Emit "jmp $31,($27)" */
478 plte
[2] = 0x6bfb0000;
480 /* Think about thread-safety -- the previous instructions must be
481 committed to memory before the first is overwritten. */
482 __asm__
__volatile__("wmb" : : : "memory");
484 /* Emit "ldah $27,hi($27)" */
485 plte
[0] = 0x277b0000 | (hi
& 0xffff);
488 /* At this point, if we've been doing runtime resolution, Icache is dirty.
489 This will be taken care of in _dl_runtime_resolve. If instead we are
490 doing this as part of non-lazy startup relocation, that bit of code
491 hasn't made it into Icache yet, so there's nothing to clean up. */
496 /* Return the final value of a plt relocation. */
497 static inline Elf64_Addr
498 elf_machine_plt_value (struct link_map
*map
, const Elf64_Rela
*reloc
,
501 return value
+ reloc
->r_addend
;
504 #endif /* !dl_machine_h */
508 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
509 MAP is the object containing the reloc. */
511 elf_machine_rela (struct link_map
*map
,
512 const Elf64_Rela
*reloc
,
513 const Elf64_Sym
*sym
,
514 const struct r_found_version
*version
,
515 void *const reloc_addr_arg
)
517 Elf64_Addr
*const reloc_addr
= reloc_addr_arg
;
518 unsigned long int const r_type
= ELF64_R_TYPE (reloc
->r_info
);
520 #if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC && !defined SHARED
521 /* This is defined in rtld.c, but nowhere in the static libc.a; make the
522 reference weak so static programs can still link. This declaration
523 cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP)
524 because rtld.c contains the common defn for _dl_rtld_map, which is
525 incompatible with a weak decl in the same file. */
526 weak_extern (_dl_rtld_map
);
529 /* We cannot use a switch here because we cannot locate the switch
530 jump table until we've self-relocated. */
532 #if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
533 if (__builtin_expect (r_type
== R_ALPHA_RELATIVE
, 0))
535 # if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
536 /* Already done in dynamic linker. */
537 if (map
!= &GL(dl_rtld_map
))
540 /* XXX Make some timings. Maybe it's preferable to test for
541 unaligned access and only do it the complex way if necessary. */
542 Elf64_Addr reloc_addr_val
;
544 /* Load value without causing unaligned trap. */
545 memcpy (&reloc_addr_val
, reloc_addr_arg
, 8);
546 reloc_addr_val
+= map
->l_addr
;
548 /* Store value without causing unaligned trap. */
549 memcpy (reloc_addr_arg
, &reloc_addr_val
, 8);
554 if (__builtin_expect (r_type
== R_ALPHA_NONE
, 0))
558 Elf64_Addr sym_value
;
559 Elf64_Addr sym_raw_value
;
561 #if defined USE_TLS && !defined RTLD_BOOTSTRAP
562 struct link_map
*sym_map
= RESOLVE_MAP (&sym
, version
, r_type
);
563 sym_raw_value
= sym_value
= reloc
->r_addend
;
566 sym_raw_value
+= sym
->st_value
;
567 sym_value
= sym_raw_value
+ sym_map
->l_addr
;
570 Elf64_Addr loadbase
= RESOLVE (&sym
, version
, r_type
);
571 sym_raw_value
= sym_value
= reloc
->r_addend
;
574 sym_raw_value
+= sym
->st_value
;
575 sym_value
= sym_raw_value
+ loadbase
;
579 if (r_type
== R_ALPHA_GLOB_DAT
)
580 *reloc_addr
= sym_value
;
581 #ifdef RESOLVE_CONFLICT_FIND_MAP
582 /* In .gnu.conflict section, R_ALPHA_JMP_SLOT relocations have
583 R_ALPHA_JMP_SLOT in lower 8 bits and the remaining 24 bits
584 are .rela.plt index. */
585 else if ((r_type
& 0xff) == R_ALPHA_JMP_SLOT
)
587 /* elf_machine_fixup_plt needs the map reloc_addr points into,
588 while in _dl_resolve_conflicts map is _dl_loaded. */
589 RESOLVE_CONFLICT_FIND_MAP (map
, reloc_addr
);
590 reloc
= ((const Elf64_Rela
*) D_PTR (map
, l_info
[DT_JMPREL
]))
592 elf_machine_fixup_plt (map
, 0, reloc
, reloc_addr
, sym_value
);
595 else if (r_type
== R_ALPHA_JMP_SLOT
)
596 elf_machine_fixup_plt (map
, 0, reloc
, reloc_addr
, sym_value
);
598 #ifndef RTLD_BOOTSTRAP
599 else if (r_type
== R_ALPHA_REFQUAD
)
601 /* Store value without causing unaligned trap. */
602 memcpy (reloc_addr_arg
, &sym_value
, 8);
605 #if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD)
606 else if (r_type
== R_ALPHA_DTPMOD64
)
608 #ifdef RTLD_BOOTSTRAP
609 /* During startup the dynamic linker is always index 1. */
612 /* Get the information from the link map returned by the
615 *reloc_addr
= sym_map
->l_tls_modid
;
618 else if (r_type
== R_ALPHA_DTPREL64
)
620 #ifndef RTLD_BOOTSTRAP
621 /* During relocation all TLS symbols are defined and used.
622 Therefore the offset is already correct. */
623 *reloc_addr
= sym_raw_value
;
626 else if (r_type
== R_ALPHA_TPREL64
)
628 #ifdef RTLD_BOOTSTRAP
629 *reloc_addr
= sym_raw_value
+ map
->l_tls_offset
;
633 CHECK_STATIC_TLS (map
, sym_map
);
634 *reloc_addr
= sym_raw_value
+ sym_map
->l_tls_offset
;
640 _dl_reloc_bad_type (map
, r_type
, 0);
644 /* Let do-rel.h know that on Alpha if l_addr is 0, all RELATIVE relocs
646 #define ELF_MACHINE_REL_RELATIVE 1
649 elf_machine_rela_relative (Elf64_Addr l_addr
, const Elf64_Rela
*reloc
,
650 void *const reloc_addr_arg
)
652 /* XXX Make some timings. Maybe it's preferable to test for
653 unaligned access and only do it the complex way if necessary. */
654 Elf64_Addr reloc_addr_val
;
656 /* Load value without causing unaligned trap. */
657 memcpy (&reloc_addr_val
, reloc_addr_arg
, 8);
658 reloc_addr_val
+= l_addr
;
660 /* Store value without causing unaligned trap. */
661 memcpy (reloc_addr_arg
, &reloc_addr_val
, 8);
665 elf_machine_lazy_rel (struct link_map
*map
,
666 Elf64_Addr l_addr
, const Elf64_Rela
*reloc
)
668 Elf64_Addr
* const reloc_addr
= (void *)(l_addr
+ reloc
->r_offset
);
669 unsigned long int const r_type
= ELF64_R_TYPE (reloc
->r_info
);
671 if (r_type
== R_ALPHA_JMP_SLOT
)
673 /* Perform a RELATIVE reloc on the .got entry that transfers
675 *reloc_addr
+= l_addr
;
677 else if (r_type
== R_ALPHA_NONE
)
680 _dl_reloc_bad_type (map
, r_type
, 1);