1 /* Machine-dependent ELF dynamic relocation inline functions. IA-64 version.
2 Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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. */
21 #define dl_machine_h 1
23 #define ELF_MACHINE_NAME "ia64"
31 /* Translate a processor specific dynamic tag to the index
33 #define DT_IA_64(x) (DT_IA_64_##x - DT_LOPROC + DT_NUM)
36 /* An FPTR is a function descriptor. Properly they consist of just
37 FUNC and GP. But we want to traverse a binary tree too. */
39 #define IA64_BOOT_FPTR_SIZE 256
45 struct ia64_fptr *next;
48 extern struct ia64_fptr __boot_ldso_fptr[];
49 extern struct ia64_fptr *__fptr_next;
50 extern struct ia64_fptr *__fptr_root;
51 extern int __fptr_count;
53 extern Elf64_Addr __ia64_make_fptr (const struct link_map *, Elf64_Addr,
54 struct ia64_fptr **, struct ia64_fptr *);
56 /* Return nonzero iff E_MACHINE is compatible with the running host. */
58 elf_machine_matches_host (Elf64_Word e_machine)
60 return e_machine == EM_IA_64;
63 void * _dl_symbol_address (const struct link_map *map, const ElfW(Sym) *ref);
65 #define DL_SYMBOL_ADDRESS(map, ref) _dl_symbol_address(map, ref)
68 /* Return the link-time address of _DYNAMIC. */
69 static inline Elf64_Addr
70 elf_machine_dynamic (void)
76 " .type __dynamic_ltv#, @object\n"
77 " .size __dynamic_ltv#, 8\n"
79 " data8 @ltv(_DYNAMIC#)\n"
81 " addl %0 = @gprel(__dynamic_ltv#), gp ;;"
88 /* Return the run-time load address of the shared object. */
89 static inline Elf64_Addr
90 elf_machine_load_address (void)
101 " addl %1 = @gprel(2b), gp ;;"
102 : "=r"(ip), "=r"(p));
104 return ip - (Elf64_Addr)*p;
108 /* Set up the loaded object described by L so its unrelocated PLT
109 entries will jump to the on-demand fixup code in dl-runtime.c. */
112 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
114 extern void _dl_runtime_resolve (void);
115 extern void _dl_runtime_profile (void);
119 register Elf64_Addr gp __asm__("gp");
120 Elf64_Addr *reserve, doit;
123 * Careful with the typecast here or it will try to add l-l_addr
126 reserve = (Elf64_Addr *)
127 (l->l_info[DT_IA_64(PLT_RESERVE)]->d_un.d_ptr + l->l_addr);
128 /* Identify this shared object. */
129 reserve[0] = (Elf64_Addr) l;
131 /* This function will be called to perform the relocation. */
133 doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_resolve)->func;
136 if (_dl_name_match_p (_dl_profile, l))
138 /* This is the object we are looking for. Say that we really
139 want profiling and the timers are started. */
142 doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_profile)->func;
154 This code is used in dl-runtime.c to call the `fixup' function
155 and then redirect to the address it returns. `fixup()' takes two
156 arguments, however fixup_profile() takes three.
158 The ABI specifies that we will never see more than 8 input
159 registers to a function call, thus it is safe to simply allocate
160 those, and simpler than playing stack games.
163 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \
164 extern void tramp_name (void); \
166 .global " #tramp_name "#
167 .proc " #tramp_name "#
170 alloc loc0 = ar.pfs, 8, 2, 3, 0
177 mov out2 = b0 /* needed by fixup_profile */
181 stf.spill [r2] = f8, 32
182 stf.spill [r3] = f9, 32
187 stf.spill [r2] = f10, 32
188 stf.spill [r3] = f11, 32
193 stf.spill [r2] = f12, 32
194 stf.spill [r3] = f13, 32
195 shladd out1 = r15, 3, out1
201 br.call.sptk.many b0 = " #fixup_name "#
210 ldf.fill f8 = [r2], 32
211 ldf.fill f9 = [r3], 32
216 ldf.fill f10 = [r2], 32
217 ldf.fill f11 = [r3], 32
222 ldf.fill f12 = [r2], 32
223 ldf.fill f13 = [r3], 32
228 ldf.fill f14 = [r2], 32
229 ldf.fill f15 = [r3], 32
233 /* An alloc is needed for the break system call to work.
234 We don't care about the old value of the pfs register. */
236 alloc r2 = ar.pfs, 0, 0, 8, 0
241 .endp " #tramp_name "#")
244 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
245 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
246 TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
248 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
249 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
250 strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
254 /* Initial entry point code for the dynamic linker.
255 The C function `_dl_start' is the real entry point;
256 its return value is the user program's entry point. */
258 #define RTLD_START asm ("\
264 alloc loc0 = ar.pfs, 0, 3, 4, 0
266 addl r3 = @gprel(0b), r0
270 /* Calculate the GP, and save a copy in loc1. */
272 movl r8 = 0x9804c0270033f
278 /* _dl_start wants a pointer to the pointer to the arg block
279 and the arg block starts with an integer, thus the magic 16. */
283 br.call.sptk.many b0 = _dl_start#
288 .global _dl_start_user#
289 .proc _dl_start_user#
292 /* Save the pointer to the user entry point fptr in loc2. */
294 /* Store the highest stack address. */
295 addl r2 = @ltoff(__libc_stack_end#), gp
296 addl r3 = @gprel(_dl_skip_args), gp
302 adds r11 = 24, sp /* Load the address of argv. */
307 adds r10 = 16, sp /* Load the address of argc. */
310 /* See if we were run as a command with the executable file
311 name as an extra leading argument. If so, adjust the argv
312 pointer to skip _dl_skip_args words.
313 Note that _dl_skip_args is an integer, not a long - Jes
315 The stack pointer has to be 16 byte aligned. We cannot simply
316 addjust the stack pointer. We have to move the whole argv and
320 ld8 out1 = [r10] /* is argc actually stored as a long
323 sub out1 = out1, r3 /* Get the new argc. */
324 shladd r15 = r3, 3, r11 /* The address of the argv we move */
327 /* ??? Could probably merge these two loops into 3 bundles.
328 using predication to control which set of copies we're on. */
331 ld8 r16 = [r15], 8 /* Load the value in the old argv. */
335 st8 [r11] = r16, 8 /* Store it in the new argv. */
336 cmp.ne p6, p7 = 0, r16
337 (p6) br.cond.dptk.few 1b
342 addl out0 = @ltoff(_dl_loaded), gp
346 ld8 r16 = [r15], 8 /* Load the value in the old env. */
350 st8 [r11] = r16, 8 /* Store it in the new env. */
351 cmp.ne p6, p7 = 0, r16
352 (p6) br.cond.dptk.few 1b
356 st8 [r10] = out1 /* Record the new argc. */
360 ld8 out0 = [out0] /* get the linkmap */
361 br.call.sptk.many b0 = _dl_init#
364 /* Pass our finializer function to the user,
365 and jump to the user's entry point. */
371 addl ret0 = @ltoff(@fptr(_dl_fini#)), gp
385 .endp _dl_start_user#
389 #ifndef RTLD_START_SPECIAL_INIT
390 #define RTLD_START_SPECIAL_INIT /* nothing */
393 /* Nonzero iff TYPE describes relocation of a PLT entry, so
394 PLT entries should not be allowed to define the value. */
395 /* ??? Ignore IPLTMSB for now. */
396 #define elf_machine_lookup_noplt_p(type) ((type) == R_IA64_IPLTLSB)
398 /* Nonzero iff TYPE should not be allowed to resolve to one of
399 the main executable's symbols, as for a COPY reloc, which we don't use. */
400 #define elf_machine_lookup_noexec_p(type) (0)
402 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
403 #define ELF_MACHINE_JMP_SLOT R_IA64_IPLTLSB
405 /* According to the IA-64 specific documentation, Rela is always used. */
406 #define ELF_MACHINE_NO_REL 1
408 /* Since ia64's stack has to be 16byte aligned, we cannot arbitrarily
409 move the stack pointer. */
410 #define ELF_MACHINE_FIXED_STACK 1
412 /* Return the address of the entry point. */
413 extern ElfW(Addr) _dl_start_address (const struct link_map *map,
416 #define ELF_MACHINE_START_ADDRESS(map, start) \
417 _dl_start_address ((map), (start))
419 #define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
420 elf_machine_fixup_plt ((l), (reloc), (rel_addr), (value))
422 #define elf_machine_profile_plt(reloc_addr) ((Elf64_Addr) (reloc_addr))
424 /* Fixup a PLT entry to bounce directly to the function at VALUE. */
425 static inline Elf64_Addr
426 elf_machine_fixup_plt (struct link_map *l, lookup_t t,
427 const Elf64_Rela *reloc,
428 Elf64_Addr *reloc_addr, Elf64_Addr value)
430 /* l is the link_map for the caller, t is the link_map for the object
432 /* got has already been relocated in elf_get_dynamic_info() */
433 reloc_addr[1] = t->l_info[DT_PLTGOT]->d_un.d_ptr;
434 reloc_addr[0] = value;
435 return (Elf64_Addr) reloc_addr;
438 /* Return the final value of a plt relocation. */
439 static inline Elf64_Addr
440 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
443 /* No need to handle rel vs rela since IA64 is rela only */
444 return value + reloc->r_addend;
447 #endif /* !dl_machine_h */
451 #define R_IA64_TYPE(R) ((R) & -8)
452 #define R_IA64_FORMAT(R) ((R) & 7)
454 #define R_IA64_FORMAT_32MSB 4
455 #define R_IA64_FORMAT_32LSB 5
456 #define R_IA64_FORMAT_64MSB 6
457 #define R_IA64_FORMAT_64LSB 7
460 /* Perform the relocation specified by RELOC and SYM (which is fully
461 resolved). MAP is the object containing the reloc. */
463 elf_machine_rela (struct link_map *map,
464 const Elf64_Rela *reloc,
465 const Elf64_Sym *sym,
466 const struct r_found_version *version,
467 Elf64_Addr *const reloc_addr)
469 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
472 #ifndef RTLD_BOOTSTRAP
473 /* This is defined in rtld.c, but nowhere in the static libc.a; make the
474 reference weak so static programs can still link. This declaration
475 cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP)
476 because rtld.c contains the common defn for _dl_rtld_map, which is
477 incompatible with a weak decl in the same file. */
478 weak_extern (_dl_rtld_map);
481 /* We cannot use a switch here because we cannot locate the switch
482 jump table until we've self-relocated. */
484 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB))
487 #ifndef RTLD_BOOTSTRAP
488 /* Already done in dynamic linker. */
489 if (map != &_dl_rtld_map)
491 value += map->l_addr;
493 else if (r_type == R_IA64_NONE)
497 struct link_map *sym_map;
500 * RESOLVE_MAP() will return NULL if it fail to locate the symbol
502 if ((sym_map = RESOLVE_MAP (&sym, version, r_type)))
504 value = sym ? sym_map->l_addr + sym->st_value : 0;
505 value += reloc->r_addend;
507 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB))
508 ;/* No adjustment. */
509 else if (r_type == R_IA64_IPLTLSB)
511 elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
514 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_FPTR64LSB))
515 #ifndef RTLD_BOOTSTRAP
516 value = __ia64_make_fptr (sym_map, value, &__fptr_root, NULL);
519 struct ia64_fptr *p_boot_ldso_fptr;
520 struct ia64_fptr **p_fptr_root;
523 /* Special care must be taken to address these variables
524 during bootstrap. Further, since we don't know exactly
525 when __fptr_next will be relocated, we index directly
526 off __boot_ldso_fptr. */
527 asm ("addl %0 = @gprel(__boot_ldso_fptr#), gp\n\t"
528 "addl %1 = @gprel(__fptr_root#), gp\n\t"
529 "addl %2 = @gprel(__fptr_count#), gp"
530 : "=r"(p_boot_ldso_fptr),
535 * Go from the top - __ia64_make_fptr goes from the bottom,
536 * this way we will never clash.
538 value = __ia64_make_fptr (sym_map, value, p_fptr_root,
539 &p_boot_ldso_fptr[--*p_fptr_count]);
542 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB))
543 value -= (Elf64_Addr)reloc_addr & -16;
545 assert (! "unexpected dynamic reloc type");
551 /* ??? Ignore MSB and Instruction format for now. */
552 if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB)
554 else if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_32LSB)
555 *(int *)reloc_addr = value;
556 else if (r_type == R_IA64_IPLTLSB)
562 assert (! "unexpected dynamic reloc format");
566 /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt. */
568 elf_machine_lazy_rel (struct link_map *map,
569 Elf64_Addr l_addr, const Elf64_Rela *reloc)
571 Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
572 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
574 if (r_type == R_IA64_IPLTLSB)
576 reloc_addr[0] += l_addr;
577 reloc_addr[1] += l_addr;
579 else if (r_type == R_IA64_NONE)
582 assert (! "unexpected PLT reloc type");
585 #endif /* RESOLVE_MAP */