* sysdeps/unix/sysv/linux/m68k/sysdep.h (INLINE_SYSCALL): Don't
[glibc.git] / sysdeps / ia64 / dl-machine.h
blob9ad777e99fa8c7c526502963be8777f5340a4dc6
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 Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #ifndef dl_machine_h
21 #define dl_machine_h 1
23 #define ELF_MACHINE_NAME "ia64"
25 #include <assert.h>
26 #include <string.h>
27 #include <link.h>
28 #include <errno.h>
31 /* Translate a processor specific dynamic tag to the index
32 in l_info array. */
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
41 struct ia64_fptr
43 Elf64_Addr func;
44 Elf64_Addr gp;
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 ELF header is compatible with the running host. */
57 static inline int
58 elf_machine_matches_host (const Elf64_Ehdr *ehdr)
60 return ehdr->e_machine == EM_IA_64;
64 /* Return the link-time address of _DYNAMIC. */
65 static inline Elf64_Addr
66 elf_machine_dynamic (void)
68 Elf64_Addr *p;
70 __asm__(
71 ".section .sdata\n"
72 " .type __dynamic_ltv#, @object\n"
73 " .size __dynamic_ltv#, 8\n"
74 "__dynamic_ltv:\n"
75 " data8 @ltv(_DYNAMIC#)\n"
76 ".previous\n"
77 " addl %0 = @gprel(__dynamic_ltv#), gp ;;"
78 : "=r"(p));
80 return *p;
84 /* Return the run-time load address of the shared object. */
85 static inline Elf64_Addr
86 elf_machine_load_address (void)
88 Elf64_Addr ip;
89 int *p;
91 __asm__(
92 "1: mov %0 = ip\n"
93 ".section .sdata\n"
94 "2: data4 @ltv(1b)\n"
95 " .align 8\n"
96 ".previous\n"
97 " addl %1 = @gprel(2b), gp ;;"
98 : "=r"(ip), "=r"(p));
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. */
107 static inline int
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);
113 if (lazy)
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
120 * pointer elements
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. */
128 if (!profile)
129 doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_resolve)->func;
130 else
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. */
136 _dl_profile_map = l;
138 doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_profile)->func;
141 reserve[1] = doit;
142 reserve[2] = gp;
145 return lazy;
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.
157 - 12/09/99 Jes
159 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \
160 extern void tramp_name (void); \
161 asm ( "\
162 .global " #tramp_name "#
163 .proc " #tramp_name "#
164 " #tramp_name ":
165 { .mmi
166 alloc loc0 = ar.pfs, 8, 6, 3, 0
167 adds r2 = -144, r12
168 adds r3 = -128, r12
170 { .mii
171 adds r12 = -160, r12
172 mov loc1 = b0
173 mov out2 = b0 /* needed by fixup_profile */
176 { .mfb
177 mov loc2 = r8 /* preserve struct value register */
178 nop.f 0
179 nop.b 0
181 { .mii
182 mov loc3 = r9 /* preserve language specific register */
183 mov loc4 = r10 /* preserve language specific register */
184 mov loc5 = r11 /* preserve language specific register */
186 { .mmi
187 stf.spill [r2] = f8, 32
188 stf.spill [r3] = f9, 32
189 mov out0 = r16
192 { .mmi
193 stf.spill [r2] = f10, 32
194 stf.spill [r3] = f11, 32
195 shl out1 = r15, 4
198 { .mmi
199 stf.spill [r2] = f12, 32
200 stf.spill [r3] = f13, 32
201 shladd out1 = r15, 3, out1
204 { .mmb
205 stf.spill [r2] = f14
206 stf.spill [r3] = f15
207 br.call.sptk.many b0 = " #fixup_name "#
209 { .mii
210 ld8 r9 = [ret0], 8
211 adds r2 = 16, r12
212 adds r3 = 32, r12
215 { .mmi
216 ldf.fill f8 = [r2], 32
217 ldf.fill f9 = [r3], 32
218 mov b0 = loc1
221 { .mmi
222 ldf.fill f10 = [r2], 32
223 ldf.fill f11 = [r3], 32
224 mov b6 = r9
227 { .mmi
228 ldf.fill f12 = [r2], 32
229 ldf.fill f13 = [r3], 32
230 mov ar.pfs = loc0
233 { .mmi
234 ldf.fill f14 = [r2], 32
235 ldf.fill f15 = [r3], 32
236 adds r12 = 160, r12
239 { .mii
240 mov r9 = loc3 /* restore language specific register */
241 mov r10 = loc4 /* restore language specific register */
242 mov r11 = loc5 /* restore language specific register */
244 { .mii
245 ld8 gp = [ret0]
246 mov r8 = loc2 /* restore struct value register */
249 /* An alloc is needed for the break system call to work.
250 We don't care about the old value of the pfs register. */
251 { .mmb
252 alloc r2 = ar.pfs, 0, 0, 8, 0
253 br.sptk.many b6
256 .endp " #tramp_name "#")
258 #ifndef PROF
259 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
260 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
261 TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
262 #else
263 #define ELF_MACHINE_RUNTIME_TRAMPOLINE \
264 TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup); \
265 strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
266 #endif
269 /* Initial entry point code for the dynamic linker.
270 The C function `_dl_start' is the real entry point;
271 its return value is the user program's entry point. */
273 #define RTLD_START asm ("\
274 .text
275 .global _start#
276 .proc _start#
277 _start:
278 0: { .mii
279 alloc loc0 = ar.pfs, 0, 3, 4, 0
280 mov r2 = ip
281 addl r3 = @gprel(0b), r0
284 { .mlx
285 /* Calculate the GP, and save a copy in loc1. */
286 sub gp = r2, r3
287 movl r8 = 0x9804c0270033f
290 { .mii
291 mov ar.fpsr = r8
292 sub loc1 = r2, r3
293 /* _dl_start wants a pointer to the pointer to the arg block
294 and the arg block starts with an integer, thus the magic 16. */
295 adds out0 = 16, sp
297 { .bbb
298 br.call.sptk.many b0 = _dl_start#
301 .endp _start#
302 /* FALLTHRU */
303 .global _dl_start_user#
304 .proc _dl_start_user#
305 _dl_start_user:
306 { .mii
307 /* Save the pointer to the user entry point fptr in loc2. */
308 mov loc2 = ret0
309 /* Store the highest stack address. */
310 addl r2 = @ltoff(__libc_stack_end#), gp
311 addl r3 = @gprel(_dl_skip_args), gp
314 { .mmi
315 ld8 r2 = [r2]
316 ld4 r3 = [r3]
317 adds r11 = 24, sp /* Load the address of argv. */
320 { .mii
321 st8 [r2] = sp
322 adds r10 = 16, sp /* Load the address of argc. */
323 mov out2 = r11
325 /* See if we were run as a command with the executable file
326 name as an extra leading argument. If so, adjust the argv
327 pointer to skip _dl_skip_args words.
328 Note that _dl_skip_args is an integer, not a long - Jes
330 The stack pointer has to be 16 byte aligned. We cannot simply
331 addjust the stack pointer. We have to move the whole argv and
332 envp and adjust _dl_argv by _dl_skip_args. H.J. */
334 { .mib
335 ld8 out1 = [r10] /* is argc actually stored as a long
336 or as an int? */
337 addl r2 = @ltoff(_dl_argv), gp
340 { .mmi
341 ld8 r2 = [r2] /* Get the address of _dl_argv. */
342 sub out1 = out1, r3 /* Get the new argc. */
343 shladd r3 = r3, 3, r0
347 .mib
348 ld8 r17 = [r2] /* Get _dl_argv. */
349 add r15 = r11, r3 /* The address of the argv we move */
352 /* ??? Could probably merge these two loops into 3 bundles.
353 using predication to control which set of copies we're on. */
354 1: /* Copy argv. */
355 { .mfi
356 ld8 r16 = [r15], 8 /* Load the value in the old argv. */
359 { .mib
360 st8 [r11] = r16, 8 /* Store it in the new argv. */
361 cmp.ne p6, p7 = 0, r16
362 (p6) br.cond.dptk.few 1b
365 { .mmi
366 mov out3 = r11
367 sub r17 = r17, r3 /* Substract _dl_skip_args. */
368 addl out0 = @ltoff(_dl_loaded), gp
370 1: /* Copy env. */
371 { .mfi
372 ld8 r16 = [r15], 8 /* Load the value in the old env. */
375 { .mib
376 st8 [r11] = r16, 8 /* Store it in the new env. */
377 cmp.ne p6, p7 = 0, r16
378 (p6) br.cond.dptk.few 1b
381 { .mmb
382 st8 [r10] = out1 /* Record the new argc. */
383 ld8 out0 = [out0]
386 { .mmb
387 ld8 out0 = [out0] /* get the linkmap */
388 st8 [r2] = r17 /* Load the new _dl_argv. */
389 br.call.sptk.many b0 = _dl_init#
392 /* Pass our finializer function to the user,
393 and jump to the user's entry point. */
394 { .mmi
395 ld8 r3 = [loc2], 8
396 mov b0 = r0
398 { .mmi
399 addl ret0 = @ltoff(@fptr(_dl_fini#)), gp
401 mov b6 = r3
403 { .mmi
404 ld8 ret0 = [ret0]
405 ld8 gp = [loc2]
406 mov ar.pfs = loc0
409 { .mfb
410 br.sptk.many b6
413 .endp _dl_start_user#
414 .previous");
417 #ifndef RTLD_START_SPECIAL_INIT
418 #define RTLD_START_SPECIAL_INIT /* nothing */
419 #endif
421 /* Nonzero iff TYPE describes relocation of a PLT entry, so
422 PLT entries should not be allowed to define the value. */
423 /* ??? Ignore IPLTMSB for now. */
424 #define elf_machine_lookup_noplt_p(type) ((type) == R_IA64_IPLTLSB)
426 /* Nonzero iff TYPE should not be allowed to resolve to one of
427 the main executable's symbols, as for a COPY reloc, which we don't use. */
428 #define elf_machine_lookup_noexec_p(type) (0)
430 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */
431 #define ELF_MACHINE_JMP_SLOT R_IA64_IPLTLSB
433 /* According to the IA-64 specific documentation, Rela is always used. */
434 #define ELF_MACHINE_NO_REL 1
436 /* Return the address of the entry point. */
437 #define ELF_MACHINE_START_ADDRESS(map, start) \
438 DL_FUNCTION_ADDRESS (map, start)
440 #define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
441 elf_machine_fixup_plt (l, reloc, rel_addr, value)
443 #define elf_machine_profile_plt(reloc_addr) ((Elf64_Addr) (reloc_addr))
445 /* Fixup a PLT entry to bounce directly to the function at VALUE. */
446 static inline Elf64_Addr
447 elf_machine_fixup_plt (struct link_map *l, lookup_t t,
448 const Elf64_Rela *reloc,
449 Elf64_Addr *reloc_addr, Elf64_Addr value)
451 /* l is the link_map for the caller, t is the link_map for the object
452 * being called */
453 /* got has already been relocated in elf_get_dynamic_info() */
454 reloc_addr[1] = t->l_info[DT_PLTGOT]->d_un.d_ptr;
455 reloc_addr[0] = value;
456 return (Elf64_Addr) reloc_addr;
459 /* Return the final value of a plt relocation. */
460 static inline Elf64_Addr
461 elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
462 Elf64_Addr value)
464 /* No need to handle rel vs rela since IA64 is rela only */
465 return value + reloc->r_addend;
468 #endif /* !dl_machine_h */
470 #ifdef RESOLVE_MAP
472 #define R_IA64_TYPE(R) ((R) & -8)
473 #define R_IA64_FORMAT(R) ((R) & 7)
475 #define R_IA64_FORMAT_32MSB 4
476 #define R_IA64_FORMAT_32LSB 5
477 #define R_IA64_FORMAT_64MSB 6
478 #define R_IA64_FORMAT_64LSB 7
481 /* Perform the relocation specified by RELOC and SYM (which is fully
482 resolved). MAP is the object containing the reloc. */
483 static inline void
484 elf_machine_rela (struct link_map *map,
485 const Elf64_Rela *reloc,
486 const Elf64_Sym *sym,
487 const struct r_found_version *version,
488 Elf64_Addr *const reloc_addr)
490 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
491 Elf64_Addr value;
493 #ifndef RTLD_BOOTSTRAP
494 /* This is defined in rtld.c, but nowhere in the static libc.a; make the
495 reference weak so static programs can still link. This declaration
496 cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP)
497 because rtld.c contains the common defn for _dl_rtld_map, which is
498 incompatible with a weak decl in the same file. */
499 weak_extern (_dl_rtld_map);
500 #endif
502 /* We cannot use a switch here because we cannot locate the switch
503 jump table until we've self-relocated. */
505 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB))
507 value = *reloc_addr;
508 #ifndef RTLD_BOOTSTRAP
509 /* Already done in dynamic linker. */
510 if (map != &_dl_rtld_map)
511 #endif
512 value += map->l_addr;
514 else if (r_type == R_IA64_NONE)
515 return;
516 else
518 struct link_map *sym_map;
521 * RESOLVE_MAP() will return NULL if it fail to locate the symbol
523 if ((sym_map = RESOLVE_MAP (&sym, version, r_type)))
525 value = sym ? sym_map->l_addr + sym->st_value : 0;
526 value += reloc->r_addend;
528 if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB))
529 ;/* No adjustment. */
530 else if (r_type == R_IA64_IPLTLSB)
532 elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
533 return;
535 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_FPTR64LSB))
536 #ifndef RTLD_BOOTSTRAP
537 value = __ia64_make_fptr (sym_map, value, &__fptr_root, NULL);
538 #else
540 struct ia64_fptr *p_boot_ldso_fptr;
541 struct ia64_fptr **p_fptr_root;
542 int *p_fptr_count;
544 /* Special care must be taken to address these variables
545 during bootstrap. Further, since we don't know exactly
546 when __fptr_next will be relocated, we index directly
547 off __boot_ldso_fptr. */
548 asm ("addl %0 = @gprel(__boot_ldso_fptr#), gp\n\t"
549 "addl %1 = @gprel(__fptr_root#), gp\n\t"
550 "addl %2 = @gprel(__fptr_count#), gp"
551 : "=r"(p_boot_ldso_fptr),
552 "=r"(p_fptr_root),
553 "=r"(p_fptr_count));
556 * Go from the top - __ia64_make_fptr goes from the bottom,
557 * this way we will never clash.
559 value = __ia64_make_fptr (sym_map, value, p_fptr_root,
560 &p_boot_ldso_fptr[--*p_fptr_count]);
562 #endif
563 else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB))
564 value -= (Elf64_Addr)reloc_addr & -16;
565 else
566 assert (! "unexpected dynamic reloc type");
568 else
569 value = 0;
572 /* ??? Ignore MSB and Instruction format for now. */
573 if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB)
574 *reloc_addr = value;
575 else if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_32LSB)
576 *(int *)reloc_addr = value;
577 else if (r_type == R_IA64_IPLTLSB)
579 reloc_addr[0] = 0;
580 reloc_addr[1] = 0;
582 else
583 assert (! "unexpected dynamic reloc format");
587 /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt. */
588 static inline void
589 elf_machine_lazy_rel (struct link_map *map,
590 Elf64_Addr l_addr, const Elf64_Rela *reloc)
592 Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
593 unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
595 if (r_type == R_IA64_IPLTLSB)
597 reloc_addr[0] += l_addr;
598 reloc_addr[1] += l_addr;
600 else if (r_type == R_IA64_NONE)
601 return;
602 else
603 assert (! "unexpected PLT reloc type");
606 #endif /* RESOLVE_MAP */