1 /* Machine-dependent ELF dynamic relocation inline functions. Alpha version.
2 Copyright (C) 1996,1997,1998,1999,2000,2001 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 /* Return nonzero iff ELF header is compatible with the running host. */
34 elf_machine_matches_host (const Elf64_Ehdr *ehdr)
36 return ehdr->e_machine == EM_ALPHA;
39 /* Return the link-time address of _DYNAMIC. The multiple-got-capable
40 linker no longer allocates the first .got entry for this. But not to
41 worry, no special tricks are needed. */
42 static inline Elf64_Addr
43 elf_machine_dynamic (void)
45 #ifndef NO_AXP_MULTI_GOT_LD
46 return (Elf64_Addr) &_DYNAMIC;
48 register Elf64_Addr *gp __asm__ ("$29");
53 /* Return the run-time load address of the shared object. */
54 static inline Elf64_Addr
55 elf_machine_load_address (void)
57 /* NOTE: While it is generally unfriendly to put data in the text
58 segment, it is only slightly less so when the "data" is an
59 instruction. While we don't have to worry about GLD just yet, an
60 optimizing linker might decide that our "data" is an unreachable
61 instruction and throw it away -- with the right switches, DEC's
62 linker will do this. What ought to happen is we should add
63 something to GAS to allow us access to the new GPREL_HI32/LO32
64 relocation types stolen from OSF/1 3.0. */
65 /* This code relies on the fact that BRADDR relocations do not
66 appear in dynamic relocation tables. Not that that would be very
67 useful anyway -- br/bsr has a 4MB range and the shared libraries
68 are usually many many terabytes away. */
83 zero_disp = *(int *) dot;
84 zero_disp = (zero_disp << 43) >> 41;
86 return dot - *(Elf64_Addr *) (dot + 4 + zero_disp);
89 /* Set up the loaded object described by L so its unrelocated PLT
90 entries will jump to the on-demand fixup code in dl-runtime.c. */
93 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
96 extern void _dl_runtime_resolve (void);
97 extern void _dl_runtime_profile (void);
99 if (l->l_info[DT_JMPREL] && lazy)
101 /* The GOT entries for the functions in the PLT have not been
102 filled in yet. Their initial contents are directed to the
103 PLT which arranges for the dynamic linker to be called. */
104 plt = D_PTR (l, l_info[DT_PLTGOT]);
106 /* This function will be called to perform the relocation. */
108 *(Elf64_Addr *)(plt + 16) = (Elf64_Addr) &_dl_runtime_resolve;
111 *(Elf64_Addr *)(plt + 16) = (Elf64_Addr) &_dl_runtime_profile;
113 if (_dl_name_match_p (_dl_profile, l))
115 /* This is the object we are looking for. Say that we really
116 want profiling and the timers are started. */
121 /* Identify this shared object */
122 *(Elf64_Addr *)(plt + 24) = (Elf64_Addr) l;
124 /* If the first instruction of the plt entry is not
125 "br $28, plt0", we have to reinitialize .plt for lazy relocation. */
126 if (*(unsigned int *)(plt + 32) != 0xc39ffff7)
128 unsigned int val = 0xc39ffff7;
129 unsigned int *slot, *end;
130 const Elf64_Rela *rela = (const Elf64_Rela *)
131 D_PTR (l, l_info[DT_JMPREL]);
132 Elf64_Addr l_addr = l->l_addr;
134 /* br t12,.+4; ldq t12,12(t12); nop; jmp t12,(t12),.+4 */
135 *(unsigned long *)plt = 0xa77b000cc3600000;
136 *(unsigned long *)(plt + 8) = 0x6b7b000047ff041f;
137 slot = (unsigned int *)(plt + 32);
138 end = (unsigned int *)(plt + 32
139 + l->l_info[DT_PLTRELSZ]->d_un.d_val / 2);
144 *(Elf64_Addr *) rela->r_offset = (Elf64_Addr) slot - l_addr;
155 /* This code is used in dl-runtime.c to call the `fixup' function
156 and then redirect to the address it returns. */
157 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name, IMB) \
158 extern void tramp_name (void); \
160 .globl " #tramp_name " \n\
161 .ent " #tramp_name " \n\
163 lda $sp, -44*8($sp) \n\
164 .frame $sp, 44*8, $26 \n\
165 /* Preserve all integer registers that C normally \n\
167 stq $26, 0*8($sp) \n\
177 stq $16, 10*8($sp) \n\
178 stq $17, 11*8($sp) \n\
179 stq $18, 12*8($sp) \n\
180 stq $19, 13*8($sp) \n\
181 stq $20, 14*8($sp) \n\
182 stq $21, 15*8($sp) \n\
183 stq $22, 16*8($sp) \n\
184 stq $23, 17*8($sp) \n\
185 stq $24, 18*8($sp) \n\
186 stq $25, 19*8($sp) \n\
187 stq $29, 20*8($sp) \n\
188 stt $f0, 21*8($sp) \n\
189 stt $f1, 22*8($sp) \n\
190 stt $f10, 23*8($sp) \n\
191 stt $f11, 24*8($sp) \n\
192 stt $f12, 25*8($sp) \n\
193 stt $f13, 26*8($sp) \n\
194 stt $f14, 27*8($sp) \n\
195 stt $f15, 28*8($sp) \n\
196 stt $f16, 29*8($sp) \n\
197 stt $f17, 30*8($sp) \n\
198 stt $f18, 31*8($sp) \n\
199 stt $f19, 32*8($sp) \n\
200 stt $f20, 33*8($sp) \n\
201 stt $f21, 34*8($sp) \n\
202 stt $f22, 35*8($sp) \n\
203 stt $f23, 36*8($sp) \n\
204 stt $f24, 37*8($sp) \n\
205 stt $f25, 38*8($sp) \n\
206 stt $f26, 39*8($sp) \n\
207 stt $f27, 40*8($sp) \n\
208 stt $f28, 41*8($sp) \n\
209 stt $f29, 42*8($sp) \n\
210 stt $f30, 43*8($sp) \n\
211 .mask 0x27ff01ff, -44*8 \n\
212 .fmask 0xfffffc03, -(44-21)*8 \n\
213 /* Set up our $gp */ \n\
217 /* Set up the arguments for fixup: */ \n\
218 /* $16 = link_map out of plt0 */ \n\
219 /* $17 = offset of reloc entry = ($28 - $27 - 20) /12 * 24 */\n\
220 /* $18 = return address */ \n\
221 subq $28, $27, $17 \n\
223 subq $17, 20, $17 \n\
225 addq $17, $17, $17 \n\
226 /* Do the fixup */ \n\
227 bsr $26, " ASM_ALPHA_NG_SYMBOL_PREFIX #fixup_name "..ng\n\
228 /* Move the destination address into position. */ \n\
230 /* Restore program registers. */ \n\
231 ldq $26, 0*8($sp) \n\
241 ldq $16, 10*8($sp) \n\
242 ldq $17, 11*8($sp) \n\
243 ldq $18, 12*8($sp) \n\
244 ldq $19, 13*8($sp) \n\
245 ldq $20, 14*8($sp) \n\
246 ldq $21, 15*8($sp) \n\
247 ldq $22, 16*8($sp) \n\
248 ldq $23, 17*8($sp) \n\
249 ldq $24, 18*8($sp) \n\
250 ldq $25, 19*8($sp) \n\
251 ldq $29, 20*8($sp) \n\
252 ldt $f0, 21*8($sp) \n\
253 ldt $f1, 22*8($sp) \n\
254 ldt $f10, 23*8($sp) \n\
255 ldt $f11, 24*8($sp) \n\
256 ldt $f12, 25*8($sp) \n\
257 ldt $f13, 26*8($sp) \n\
258 ldt $f14, 27*8($sp) \n\
259 ldt $f15, 28*8($sp) \n\
260 ldt $f16, 29*8($sp) \n\
261 ldt $f17, 30*8($sp) \n\
262 ldt $f18, 31*8($sp) \n\
263 ldt $f19, 32*8($sp) \n\
264 ldt $f20, 33*8($sp) \n\
265 ldt $f21, 34*8($sp) \n\
266 ldt $f22, 35*8($sp) \n\
267 ldt $f23, 36*8($sp) \n\
268 ldt $f24, 37*8($sp) \n\
269 ldt $f25, 38*8($sp) \n\
270 ldt $f26, 39*8($sp) \n\
271 ldt $f27, 40*8($sp) \n\
272 ldt $f28, 41*8($sp) \n\
273 ldt $f29, 42*8($sp) \n\
274 ldt $f30, 43*8($sp) \n\
275 /* Flush the Icache after having modified the .plt code. */\n\
277 /* Clean up and turn control to the destination */ \n\
278 lda $sp, 44*8($sp) \n\
283 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
284 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup, imb); \
285 TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup, /* nop */);
287 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
288 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup, imb); \
289 strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
292 /* Initial entry point code for the dynamic linker.
293 The C function `_dl_start' is the real entry point;
294 its return value is the user program's entry point. */
296 #define RTLD_START asm ("\
303 0: ldgp $gp, 0($gp) \n\
305 /* Pass pointer to argument block to _dl_start. */ \n\
307 bsr $26, "ASM_ALPHA_NG_SYMBOL_PREFIX"_dl_start..ng \n\
310 .globl _dl_start_user \n\
311 .ent _dl_start_user \n\
313 .frame $30,0,$31,0 \n\
315 /* Save the user entry point address in s0. */ \n\
317 /* Store the highest stack address. */ \n\
318 stq $30, __libc_stack_end \n\
319 /* See if we were run as a command with the executable \n\
320 file name as an extra leading argument. */ \n\
321 ldl $1, _dl_skip_args \n\
322 bne $1, $fixup_stack \n\
323 $fixup_stack_ret: \n\
324 /* The special initializer gets called with the stack \n\
325 just as the application's entry point will see it; \n\
326 it can switch stacks if it moves these contents \n\
328 " RTLD_START_SPECIAL_INIT " \n\
329 /* Call _dl_init(_dl_loaded, argc, argv, envp) to run \n\
331 ldq $16, _dl_loaded \n\
334 s8addq $17, 8, $19 \n\
335 addq $19, $18, $19 \n\
336 jsr $26, _dl_init \n\
337 /* Pass our finalizer function to the user in $0. */ \n\
339 /* Jump to the user's entry point. */ \n\
343 /* Adjust the stack pointer to skip _dl_skip_args words.\n\
344 This involves copying everything down, since the \n\
345 stack pointer must always be 16-byte aligned. */ \n\
350 s8addq $6, $5, $5 \n\
352 s8addq $1, $sp, $3 \n\
355 /* Copy down argv. */ \n\
361 /* Copy down envp. */ \n\
367 /* Copy down auxiliary table. */ \n\
375 br $fixup_stack_ret \n\
376 .end _dl_start_user \n\
380 #ifndef RTLD_START_SPECIAL_INIT
381 #define RTLD_START_SPECIAL_INIT /* nothing */
384 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
385 PLT entries should not be allowed to define the value.
386 ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
387 of the main executable's symbols, as for a COPY reloc, which we don't
389 #define elf_machine_type_class(type) \
390 (((type) == R_ALPHA_JMP_SLOT) * ELF_RTYPE_CLASS_PLT)
392 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
393 #define ELF_MACHINE_JMP_SLOT R_ALPHA_JMP_SLOT
395 /* The alpha never uses Elf64_Rel relocations. */
396 #define ELF_MACHINE_NO_REL 1
398 /* Fix up the instructions of a PLT entry to invoke the function
399 rather than the dynamic linker. */
400 static inline Elf64_Addr
401 elf_machine_fixup_plt (struct link_map *l, lookup_t t,
402 const Elf64_Rela *reloc,
403 Elf64_Addr *got_addr, Elf64_Addr value)
405 const Elf64_Rela *rela_plt;
409 /* Store the value we are going to load. */
412 /* Recover the PLT entry address by calculating reloc's index into the
413 .rela.plt, and finding that entry in the .plt. */
414 rela_plt = (void *) D_PTR (l, l_info[DT_JMPREL]);
415 plte = (void *) (D_PTR (l, l_info[DT_PLTGOT]) + 32);
416 plte += 3 * (reloc - rela_plt);
418 /* Find the displacement from the plt entry to the function. */
419 edisp = (long int) (value - (Elf64_Addr)&plte[3]) / 4;
421 if (edisp >= -0x100000 && edisp < 0x100000)
423 /* If we are in range, use br to perfect branch prediction and
424 elide the dependency on the address load. This case happens,
425 e.g., when a shared library call is resolved to the same library. */
428 hi = value - (Elf64_Addr)&plte[0];
430 hi = (hi - lo) >> 16;
432 /* Emit "lda $27,lo($27)" */
433 plte[1] = 0x237b0000 | (lo & 0xffff);
435 /* Emit "br $31,function" */
436 plte[2] = 0xc3e00000 | (edisp & 0x1fffff);
438 /* Think about thread-safety -- the previous instructions must be
439 committed to memory before the first is overwritten. */
440 __asm__ __volatile__("wmb" : : : "memory");
442 /* Emit "ldah $27,hi($27)" */
443 plte[0] = 0x277b0000 | (hi & 0xffff);
447 /* Don't bother with the hint since we already know the hint is
448 wrong. Eliding it prevents the wrong page from getting pulled
452 hi = (Elf64_Addr)got_addr - (Elf64_Addr)&plte[0];
454 hi = (hi - lo) >> 16;
456 /* Emit "ldq $27,lo($27)" */
457 plte[1] = 0xa77b0000 | (lo & 0xffff);
459 /* Emit "jmp $31,($27)" */
460 plte[2] = 0x6bfb0000;
462 /* Think about thread-safety -- the previous instructions must be
463 committed to memory before the first is overwritten. */
464 __asm__ __volatile__("wmb" : : : "memory");
466 /* Emit "ldah $27,hi($27)" */
467 plte[0] = 0x277b0000 | (hi & 0xffff);
470 /* At this point, if we've been doing runtime resolution, Icache is dirty.
471 This will be taken care of in _dl_runtime_resolve. If instead we are
472 doing this as part of non-lazy startup relocation, that bit of code
473 hasn't made it into Icache yet, so there's nothing to clean up. */
478 /* Return the final value of a plt relocation. */
479 static inline Elf64_Addr
480 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
483 return value + reloc->r_addend;
486 #endif /* !dl_machine_h */
490 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
491 MAP is the object containing the reloc. */
493 elf_machine_rela (struct link_map *map,
494 const Elf64_Rela *reloc,
495 const Elf64_Sym *sym,
496 const struct r_found_version *version,
497 Elf64_Addr *const reloc_addr)
499 unsigned long int const r_type = ELF64_R_TYPE (reloc->r_info);
501 #if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
502 /* This is defined in rtld.c, but nowhere in the static libc.a; make the
503 reference weak so static programs can still link. This declaration
504 cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP)
505 because rtld.c contains the common defn for _dl_rtld_map, which is
506 incompatible with a weak decl in the same file. */
507 weak_extern (_dl_rtld_map);
510 /* We cannot use a switch here because we cannot locate the switch
511 jump table until we've self-relocated. */
513 #if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
514 if (__builtin_expect (r_type == R_ALPHA_RELATIVE, 0))
516 # if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
517 /* Already done in dynamic linker. */
518 if (map != &_dl_rtld_map)
521 /* XXX Make some timings. Maybe it's preverable to test for
522 unaligned access and only do it the complex way if necessary. */
523 void *reloc_addr_1 = reloc_addr;
524 Elf64_Addr reloc_addr_val;
526 /* Load value without causing unaligned trap. */
527 memcpy (&reloc_addr_val, reloc_addr_1, 8);
528 reloc_addr_val += map->l_addr;
530 /* Store value without causing unaligned trap. */
531 memcpy (reloc_addr_1, &reloc_addr_val, 8);
534 # ifndef RTLD_BOOTSTRAP
535 else if (__builtin_expect (r_type == R_ALPHA_NONE, 0))
541 Elf64_Addr loadbase, sym_value;
543 loadbase = RESOLVE (&sym, version, r_type);
544 sym_value = sym ? loadbase + sym->st_value : 0;
545 sym_value += reloc->r_addend;
547 if (r_type == R_ALPHA_GLOB_DAT)
548 *reloc_addr = sym_value;
549 #ifdef RESOLVE_CONFLICT_FIND_MAP
550 /* In .gnu.conflict section, R_ALPHA_JMP_SLOT relocations have
551 R_ALPHA_JMP_SLOT in lower 8 bits and the remaining 24 bits
552 are .rela.plt index. */
553 else if ((r_type & 0xff) == R_ALPHA_JMP_SLOT)
555 /* elf_machine_fixup_plt needs the map reloc_addr points into,
556 while in _dl_resolve_conflicts map is _dl_loaded. */
557 RESOLVE_CONFLICT_FIND_MAP (map, reloc_addr);
558 reloc = ((const Elf64_Rela *) D_PTR (map, l_info[DT_JMPREL]))
560 elf_machine_fixup_plt (map, 0, reloc, reloc_addr, sym_value);
563 else if (r_type == R_ALPHA_JMP_SLOT)
564 elf_machine_fixup_plt (map, 0, reloc, reloc_addr, sym_value);
566 #ifndef RTLD_BOOTSTRAP
567 else if (r_type == R_ALPHA_REFQUAD)
569 void *reloc_addr_1 = reloc_addr;
571 /* Store value without causing unaligned trap. */
572 memcpy (reloc_addr_1, &sym_value, 8);
576 _dl_reloc_bad_type (map, r_type, 0);
581 elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc,
582 Elf64_Addr *const reloc_addr)
584 /* XXX Make some timings. Maybe it's preverable to test for
585 unaligned access and only do it the complex way if necessary. */
586 void *reloc_addr_1 = reloc_addr;
587 Elf64_Addr reloc_addr_val;
589 /* Load value without causing unaligned trap. */
590 memcpy (&reloc_addr_val, reloc_addr_1, 8);
591 reloc_addr_val += l_addr;
593 /* Store value without causing unaligned trap. */
594 memcpy (reloc_addr_1, &reloc_addr_val, 8);
598 elf_machine_lazy_rel (struct link_map *map,
599 Elf64_Addr l_addr, const Elf64_Rela *reloc)
601 Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
602 unsigned long int const r_type = ELF64_R_TYPE (reloc->r_info);
604 if (r_type == R_ALPHA_JMP_SLOT)
606 /* Perform a RELATIVE reloc on the .got entry that transfers
608 *reloc_addr += l_addr;
610 else if (r_type == R_ALPHA_NONE)
613 _dl_reloc_bad_type (map, r_type, 1);