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;
64 /* Return the link-time address of _DYNAMIC. */
65 static inline Elf64_Addr
66 elf_machine_dynamic (void)
72 " .type __dynamic_ltv#, @object\n"
73 " .size __dynamic_ltv#, 8\n"
75 " data8 @ltv(_DYNAMIC#)\n"
77 " addl %0 = @gprel(__dynamic_ltv#), gp ;;"
84 /* Return the run-time load address of the shared object. */
85 static inline Elf64_Addr
86 elf_machine_load_address (void)
97 " addl %1 = @gprel(2b), gp ;;"
100 return ip - (Elf64_Addr)*p;
104 /* Set up the loaded object described by L so its unrelocated PLT
105 entries will jump to the on-demand fixup code in dl-runtime.c. */
108 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
110 extern void _dl_runtime_resolve (void);
111 extern void _dl_runtime_profile (void);
115 register Elf64_Addr gp __asm__("gp");
116 Elf64_Addr *reserve, doit;
119 * Careful with the typecast here or it will try to add l-l_addr
122 reserve = (Elf64_Addr *)
123 (l->l_info[DT_IA_64(PLT_RESERVE)]->d_un.d_ptr + l->l_addr);
124 /* Identify this shared object. */
125 reserve[0] = (Elf64_Addr) l;
127 /* This function will be called to perform the relocation. */
129 doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_resolve)->func;
132 if (_dl_name_match_p (_dl_profile, l))
134 /* This is the object we are looking for. Say that we really
135 want profiling and the timers are started. */
138 doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_profile)->func;
150 This code is used in dl-runtime.c to call the `fixup' function
151 and then redirect to the address it returns. `fixup()' takes two
152 arguments, however fixup_profile() takes three.
154 The ABI specifies that we will never see more than 8 input
155 registers to a function call, thus it is safe to simply allocate
156 those, and simpler than playing stack games.
159 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \
160 extern void tramp_name (void); \
162 .global " #tramp_name "#
163 .proc " #tramp_name "#
166 alloc loc0 = ar.pfs, 8, 2, 3, 0
173 mov out2 = b0 /* needed by fixup_profile */
177 stf.spill [r2] = f8, 32
178 stf.spill [r3] = f9, 32
183 stf.spill [r2] = f10, 32
184 stf.spill [r3] = f11, 32
189 stf.spill [r2] = f12, 32
190 stf.spill [r3] = f13, 32
191 shladd out1 = r15, 3, out1
197 br.call.sptk.many b0 = " #fixup_name "#
206 ldf.fill f8 = [r2], 32
207 ldf.fill f9 = [r3], 32
212 ldf.fill f10 = [r2], 32
213 ldf.fill f11 = [r3], 32
218 ldf.fill f12 = [r2], 32
219 ldf.fill f13 = [r3], 32
224 ldf.fill f14 = [r2], 32
225 ldf.fill f15 = [r3], 32
229 /* An alloc is needed for the break system call to work.
230 We don't care about the old value of the pfs register. */
232 alloc r2 = ar.pfs, 0, 0, 8, 0
237 .endp " #tramp_name "#")
240 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
241 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
242 TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
244 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
245 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
246 strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
250 /* Initial entry point code for the dynamic linker.
251 The C function `_dl_start' is the real entry point;
252 its return value is the user program's entry point. */
254 #define RTLD_START asm ("\
260 alloc loc0 = ar.pfs, 0, 3, 4, 0
262 addl r3 = @gprel(0b), r0
266 /* Calculate the GP, and save a copy in loc1. */
268 movl r8 = 0x9804c0270033f
274 /* _dl_start wants a pointer to the pointer to the arg block
275 and the arg block starts with an integer, thus the magic 16. */
279 br.call.sptk.many b0 = _dl_start#
284 .global _dl_start_user#
285 .proc _dl_start_user#
288 /* Save the pointer to the user entry point fptr in loc2. */
290 /* Store the highest stack address. */
291 addl r2 = @ltoff(__libc_stack_end#), gp
292 addl r3 = @gprel(_dl_skip_args), gp
298 adds r11 = 24, sp /* Load the address of argv. */
303 adds r10 = 16, sp /* Load the address of argc. */
306 /* See if we were run as a command with the executable file
307 name as an extra leading argument. If so, adjust the argv
308 pointer to skip _dl_skip_args words.
309 Note that _dl_skip_args is an integer, not a long - Jes
311 The stack pointer has to be 16 byte aligned. We cannot simply
312 addjust the stack pointer. We have to move the whole argv and
316 ld8 out1 = [r10] /* is argc actually stored as a long
319 sub out1 = out1, r3 /* Get the new argc. */
320 shladd r15 = r3, 3, r11 /* The address of the argv we move */
323 /* ??? Could probably merge these two loops into 3 bundles.
324 using predication to control which set of copies we're on. */
327 ld8 r16 = [r15], 8 /* Load the value in the old argv. */
331 st8 [r11] = r16, 8 /* Store it in the new argv. */
332 cmp.ne p6, p7 = 0, r16
333 (p6) br.cond.dptk.few 1b
338 addl out0 = @ltoff(_dl_loaded), gp
342 ld8 r16 = [r15], 8 /* Load the value in the old env. */
346 st8 [r11] = r16, 8 /* Store it in the new env. */
347 cmp.ne p6, p7 = 0, r16
348 (p6) br.cond.dptk.few 1b
352 st8 [r10] = out1 /* Record the new argc. */
357 ld8 out0 = [out0] /* get the linkmap */
358 br.call.sptk.many b0 = _dl_init#
361 /* Pass our finializer function to the user,
362 and jump to the user's entry point. */
368 addl ret0 = @ltoff(@fptr(_dl_fini#)), gp
382 .endp _dl_start_user#
386 #ifndef RTLD_START_SPECIAL_INIT
387 #define RTLD_START_SPECIAL_INIT /* nothing */
390 /* Nonzero iff TYPE describes relocation of a PLT entry, so
391 PLT entries should not be allowed to define the value. */
392 /* ??? Ignore IPLTMSB for now. */
393 #define elf_machine_lookup_noplt_p(type) ((type) == R_IA64_IPLTLSB)
395 /* Nonzero iff TYPE should not be allowed to resolve to one of
396 the main executable's symbols, as for a COPY reloc, which we don't use. */
397 #define elf_machine_lookup_noexec_p(type) (0)
399 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
400 #define ELF_MACHINE_JMP_SLOT R_IA64_IPLTLSB
402 /* According to the IA-64 specific documentation, Rela is always used. */
403 #define ELF_MACHINE_NO_REL 1
405 /* Since ia64's stack has to be 16byte aligned, we cannot arbitrarily
406 move the stack pointer. */
407 #define ELF_MACHINE_FIXED_STACK 1
409 /* Return the address of the entry point. */
410 extern ElfW(Addr) _dl_start_address (const struct link_map *map,
413 #define ELF_MACHINE_START_ADDRESS(map, start) \
414 _dl_start_address ((map), (start))
416 #define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
417 elf_machine_fixup_plt ((l), (reloc), (rel_addr), (value))
419 #define elf_machine_profile_plt(reloc_addr) ((Elf64_Addr) (reloc_addr))
421 /* Fixup a PLT entry to bounce directly to the function at VALUE. */
422 static inline Elf64_Addr
423 elf_machine_fixup_plt (struct link_map *l, lookup_t t,
424 const Elf64_Rela *reloc,
425 Elf64_Addr *reloc_addr, Elf64_Addr value)
427 /* l is the link_map for the caller, t is the link_map for the object
429 /* got has already been relocated in elf_get_dynamic_info() */
430 reloc_addr[1] = t->l_info[DT_PLTGOT]->d_un.d_ptr;
431 reloc_addr[0] = value;
432 return (Elf64_Addr) reloc_addr;
435 /* Return the final value of a plt relocation. */
436 static inline Elf64_Addr
437 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
440 /* No need to handle rel vs rela since IA64 is rela only */
441 return value + reloc->r_addend;
444 #endif /* !dl_machine_h */
448 #define R_IA64_TYPE(R) ((R) & -8)
449 #define R_IA64_FORMAT(R) ((R) & 7)
451 #define R_IA64_FORMAT_32MSB 4
452 #define R_IA64_FORMAT_32LSB 5
453 #define R_IA64_FORMAT_64MSB 6
454 #define R_IA64_FORMAT_64LSB 7
457 /* Perform the relocation specified by RELOC and SYM (which is fully
458 resolved). MAP is the object containing the reloc. */
460 elf_machine_rela (struct link_map *map,
461 const Elf64_Rela *reloc,
462 const Elf64_Sym *sym,
463 const struct r_found_version *version,
464 Elf64_Addr *const reloc_addr)
466 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
469 #ifndef RTLD_BOOTSTRAP
470 /* This is defined in rtld.c, but nowhere in the static libc.a; make the
471 reference weak so static programs can still link. This declaration
472 cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP)
473 because rtld.c contains the common defn for _dl_rtld_map, which is
474 incompatible with a weak decl in the same file. */
475 weak_extern (_dl_rtld_map);
478 /* We cannot use a switch here because we cannot locate the switch
479 jump table until we've self-relocated. */
481 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB))
484 #ifndef RTLD_BOOTSTRAP
485 /* Already done in dynamic linker. */
486 if (map != &_dl_rtld_map)
488 value += map->l_addr;
490 else if (r_type == R_IA64_NONE)
494 struct link_map *sym_map;
497 * RESOLVE_MAP() will return NULL if it fail to locate the symbol
499 if ((sym_map = RESOLVE_MAP (&sym, version, r_type)))
501 value = sym ? sym_map->l_addr + sym->st_value : 0;
502 value += reloc->r_addend;
504 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB))
505 ;/* No adjustment. */
506 else if (r_type == R_IA64_IPLTLSB)
508 elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
511 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_FPTR64LSB))
512 #ifndef RTLD_BOOTSTRAP
513 value = __ia64_make_fptr (sym_map, value, &__fptr_root, NULL);
516 struct ia64_fptr *p_boot_ldso_fptr;
517 struct ia64_fptr **p_fptr_root;
520 /* Special care must be taken to address these variables
521 during bootstrap. Further, since we don't know exactly
522 when __fptr_next will be relocated, we index directly
523 off __boot_ldso_fptr. */
524 asm ("addl %0 = @gprel(__boot_ldso_fptr#), gp\n\t"
525 "addl %1 = @gprel(__fptr_root#), gp\n\t"
526 "addl %2 = @gprel(__fptr_count#), gp"
527 : "=r"(p_boot_ldso_fptr),
532 * Go from the top - __ia64_make_fptr goes from the bottom,
533 * this way we will never clash.
535 value = __ia64_make_fptr (sym_map, value, p_fptr_root,
536 &p_boot_ldso_fptr[--*p_fptr_count]);
539 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB))
540 value -= (Elf64_Addr)reloc_addr & -16;
542 assert (! "unexpected dynamic reloc type");
548 /* ??? Ignore MSB and Instruction format for now. */
549 if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB)
551 else if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_32LSB)
552 *(int *)reloc_addr = value;
553 else if (r_type == R_IA64_IPLTLSB)
559 assert (! "unexpected dynamic reloc format");
563 /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt. */
565 elf_machine_lazy_rel (struct link_map *map,
566 Elf64_Addr l_addr, const Elf64_Rela *reloc)
568 Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
569 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
571 if (r_type == R_IA64_IPLTLSB)
573 reloc_addr[0] += l_addr;
574 reloc_addr[1] += l_addr;
576 else if (r_type == R_IA64_NONE)
579 assert (! "unexpected PLT reloc type");
582 #endif /* RESOLVE_MAP */