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
313 envp and adjust _dl_argv by _dl_skip_args. H.J. */
316 ld8 out1 = [r10] /* is argc actually stored as a long
318 addl r2 = @ltoff(_dl_argv), gp
322 ld8 r2 = [r2] /* Get the address of _dl_argv. */
323 sub out1 = out1, r3 /* Get the new argc. */
324 shladd r3 = r3, 3, r0
329 ld8 r17 = [r2] /* Get _dl_argv. */
330 add r15 = r11, r3 /* The address of the argv we move */
333 /* ??? Could probably merge these two loops into 3 bundles.
334 using predication to control which set of copies we're on. */
337 ld8 r16 = [r15], 8 /* Load the value in the old argv. */
341 st8 [r11] = r16, 8 /* Store it in the new argv. */
342 cmp.ne p6, p7 = 0, r16
343 (p6) br.cond.dptk.few 1b
348 sub r17 = r17, r3 /* Substract _dl_skip_args. */
349 addl out0 = @ltoff(_dl_loaded), gp
353 ld8 r16 = [r15], 8 /* Load the value in the old env. */
357 st8 [r11] = r16, 8 /* Store it in the new env. */
358 cmp.ne p6, p7 = 0, r16
359 (p6) br.cond.dptk.few 1b
363 st8 [r10] = out1 /* Record the new argc. */
368 ld8 out0 = [out0] /* get the linkmap */
369 st8 [r2] = r17 /* Load the new _dl_argv. */
370 br.call.sptk.many b0 = _dl_init#
373 /* Pass our finializer function to the user,
374 and jump to the user's entry point. */
380 addl ret0 = @ltoff(@fptr(_dl_fini#)), gp
394 .endp _dl_start_user#
398 #ifndef RTLD_START_SPECIAL_INIT
399 #define RTLD_START_SPECIAL_INIT /* nothing */
402 /* Nonzero iff TYPE describes relocation of a PLT entry, so
403 PLT entries should not be allowed to define the value. */
404 /* ??? Ignore IPLTMSB for now. */
405 #define elf_machine_lookup_noplt_p(type) ((type) == R_IA64_IPLTLSB)
407 /* Nonzero iff TYPE should not be allowed to resolve to one of
408 the main executable's symbols, as for a COPY reloc, which we don't use. */
409 #define elf_machine_lookup_noexec_p(type) (0)
411 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
412 #define ELF_MACHINE_JMP_SLOT R_IA64_IPLTLSB
414 /* According to the IA-64 specific documentation, Rela is always used. */
415 #define ELF_MACHINE_NO_REL 1
417 /* Return the address of the entry point. */
418 extern ElfW(Addr) _dl_start_address (const struct link_map *map,
421 #define ELF_MACHINE_START_ADDRESS(map, start) \
422 _dl_start_address ((map), (start))
424 #define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
425 elf_machine_fixup_plt ((l), (reloc), (rel_addr), (value))
427 #define elf_machine_profile_plt(reloc_addr) ((Elf64_Addr) (reloc_addr))
429 /* Fixup a PLT entry to bounce directly to the function at VALUE. */
430 static inline Elf64_Addr
431 elf_machine_fixup_plt (struct link_map *l, lookup_t t,
432 const Elf64_Rela *reloc,
433 Elf64_Addr *reloc_addr, Elf64_Addr value)
435 /* l is the link_map for the caller, t is the link_map for the object
437 /* got has already been relocated in elf_get_dynamic_info() */
438 reloc_addr[1] = t->l_info[DT_PLTGOT]->d_un.d_ptr;
439 reloc_addr[0] = value;
440 return (Elf64_Addr) reloc_addr;
443 /* Return the final value of a plt relocation. */
444 static inline Elf64_Addr
445 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
448 /* No need to handle rel vs rela since IA64 is rela only */
449 return value + reloc->r_addend;
452 #endif /* !dl_machine_h */
456 #define R_IA64_TYPE(R) ((R) & -8)
457 #define R_IA64_FORMAT(R) ((R) & 7)
459 #define R_IA64_FORMAT_32MSB 4
460 #define R_IA64_FORMAT_32LSB 5
461 #define R_IA64_FORMAT_64MSB 6
462 #define R_IA64_FORMAT_64LSB 7
465 /* Perform the relocation specified by RELOC and SYM (which is fully
466 resolved). MAP is the object containing the reloc. */
468 elf_machine_rela (struct link_map *map,
469 const Elf64_Rela *reloc,
470 const Elf64_Sym *sym,
471 const struct r_found_version *version,
472 Elf64_Addr *const reloc_addr)
474 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
477 #ifndef RTLD_BOOTSTRAP
478 /* This is defined in rtld.c, but nowhere in the static libc.a; make the
479 reference weak so static programs can still link. This declaration
480 cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP)
481 because rtld.c contains the common defn for _dl_rtld_map, which is
482 incompatible with a weak decl in the same file. */
483 weak_extern (_dl_rtld_map);
486 /* We cannot use a switch here because we cannot locate the switch
487 jump table until we've self-relocated. */
489 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB))
492 #ifndef RTLD_BOOTSTRAP
493 /* Already done in dynamic linker. */
494 if (map != &_dl_rtld_map)
496 value += map->l_addr;
498 else if (r_type == R_IA64_NONE)
502 struct link_map *sym_map;
505 * RESOLVE_MAP() will return NULL if it fail to locate the symbol
507 if ((sym_map = RESOLVE_MAP (&sym, version, r_type)))
509 value = sym ? sym_map->l_addr + sym->st_value : 0;
510 value += reloc->r_addend;
512 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB))
513 ;/* No adjustment. */
514 else if (r_type == R_IA64_IPLTLSB)
516 elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
519 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_FPTR64LSB))
520 #ifndef RTLD_BOOTSTRAP
521 value = __ia64_make_fptr (sym_map, value, &__fptr_root, NULL);
524 struct ia64_fptr *p_boot_ldso_fptr;
525 struct ia64_fptr **p_fptr_root;
528 /* Special care must be taken to address these variables
529 during bootstrap. Further, since we don't know exactly
530 when __fptr_next will be relocated, we index directly
531 off __boot_ldso_fptr. */
532 asm ("addl %0 = @gprel(__boot_ldso_fptr#), gp\n\t"
533 "addl %1 = @gprel(__fptr_root#), gp\n\t"
534 "addl %2 = @gprel(__fptr_count#), gp"
535 : "=r"(p_boot_ldso_fptr),
540 * Go from the top - __ia64_make_fptr goes from the bottom,
541 * this way we will never clash.
543 value = __ia64_make_fptr (sym_map, value, p_fptr_root,
544 &p_boot_ldso_fptr[--*p_fptr_count]);
547 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB))
548 value -= (Elf64_Addr)reloc_addr & -16;
550 assert (! "unexpected dynamic reloc type");
556 /* ??? Ignore MSB and Instruction format for now. */
557 if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB)
559 else if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_32LSB)
560 *(int *)reloc_addr = value;
561 else if (r_type == R_IA64_IPLTLSB)
567 assert (! "unexpected dynamic reloc format");
571 /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt. */
573 elf_machine_lazy_rel (struct link_map *map,
574 Elf64_Addr l_addr, const Elf64_Rela *reloc)
576 Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
577 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
579 if (r_type == R_IA64_IPLTLSB)
581 reloc_addr[0] += l_addr;
582 reloc_addr[1] += l_addr;
584 else if (r_type == R_IA64_NONE)
587 assert (! "unexpected PLT reloc type");
590 #endif /* RESOLVE_MAP */