IA-64 specific ELF definitions.
[kopensolaris-gnu/glibc.git] / sysdeps / ia64 / dl-machine.h
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.
4
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.
9
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.
14
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.  */
19
20 #ifndef dl_machine_h
21 #define dl_machine_h 1
22
23 #define ELF_MACHINE_NAME "ia64"
24
25 #include <assert.h>
26 #include <string.h>
27 #include <link.h>
28 #include <errno.h>
29
30
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)
34
35
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.  */
38
39 #define IA64_BOOT_FPTR_SIZE     256
40
41 struct ia64_fptr
42 {
43   Elf64_Addr func;
44   Elf64_Addr gp;
45   struct ia64_fptr *next;
46 };
47
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;
52
53 extern Elf64_Addr __ia64_make_fptr (const struct link_map *, Elf64_Addr,
54                                     struct ia64_fptr **, struct ia64_fptr *);
55
56 /* Return nonzero iff E_MACHINE is compatible with the running host.  */
57 static inline int
58 elf_machine_matches_host (Elf64_Word e_machine)
59 {
60   return e_machine == EM_IA_64;
61 }
62
63 void * _dl_symbol_address (const struct link_map *map, const ElfW(Sym) *ref);
64
65 #define DL_SYMBOL_ADDRESS(map, ref) _dl_symbol_address(map, ref)
66
67
68 /* Return the link-time address of _DYNAMIC.  */
69 static inline Elf64_Addr
70 elf_machine_dynamic (void)
71 {
72   Elf64_Addr *p;
73
74   __asm__(
75         ".section .sdata\n"
76         "       .type __dynamic_ltv#, @object\n"
77         "       .size __dynamic_ltv#, 8\n"
78         "__dynamic_ltv:\n"
79         "       data8   @ltv(_DYNAMIC#)\n"
80         ".previous\n"
81         "       addl    %0 = @gprel(__dynamic_ltv#), gp ;;"
82         : "=r"(p));
83
84   return *p;
85 }
86
87
88 /* Return the run-time load address of the shared object.  */
89 static inline Elf64_Addr
90 elf_machine_load_address (void)
91 {
92   Elf64_Addr ip;
93   int *p;
94
95   __asm__(
96         "1:     mov %0 = ip\n"
97         ".section .sdata\n"
98         "2:     data4   @ltv(1b)\n"
99         "       .align 8\n"
100         ".previous\n"
101         "       addl    %1 = @gprel(2b), gp ;;"
102         : "=r"(ip), "=r"(p));
103
104   return ip - (Elf64_Addr)*p;
105 }
106
107
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.  */
110
111 static inline int
112 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
113 {
114   extern void _dl_runtime_resolve (void);
115   extern void _dl_runtime_profile (void);
116
117   if (lazy)
118     {
119       register Elf64_Addr gp __asm__("gp");
120       Elf64_Addr *reserve, doit;
121
122       /*
123        * Careful with the typecast here or it will try to add l-l_addr
124        * pointer elements
125        */
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;
130
131       /* This function will be called to perform the relocation.  */
132       if (!profile)
133         doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_resolve)->func;
134       else
135         {
136           if (_dl_name_match_p (_dl_profile, l))
137             {
138               /* This is the object we are looking for.  Say that we really
139                  want profiling and the timers are started.  */
140               _dl_profile_map = l;
141             }
142           doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_profile)->func;
143         }
144
145       reserve[1] = doit;
146       reserve[2] = gp;
147     }
148
149   return lazy;
150 }
151
152
153 /*
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.
157
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.
161                                                              - 12/09/99 Jes
162  */
163 #define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \
164   extern void tramp_name (void); \
165   asm ( "\
166         .global " #tramp_name "#
167         .proc " #tramp_name "#
168 " #tramp_name ":
169         { .mmi
170           alloc loc0 = ar.pfs, 8, 2, 3, 0
171           adds r2 = -144, r12
172           adds r3 = -128, r12
173         }
174         { .mii
175           adds r12 = -160, r12
176           mov loc1 = b0
177           mov out2 = b0         /* needed by fixup_profile */
178           ;;
179         }
180         { .mmi
181           stf.spill [r2] = f8, 32
182           stf.spill [r3] = f9, 32
183           mov out0 = r16
184           ;;
185         }
186         { .mmi
187           stf.spill [r2] = f10, 32
188           stf.spill [r3] = f11, 32
189           shl out1 = r15, 4
190           ;;
191         }
192         { .mmi
193           stf.spill [r2] = f12, 32
194           stf.spill [r3] = f13, 32
195           shladd out1 = r15, 3, out1
196           ;;
197         }
198         { .mmb
199           stf.spill [r2] = f14
200           stf.spill [r3] = f15
201           br.call.sptk.many b0 = " #fixup_name "#
202         }
203         { .mii
204           ld8 r9 = [ret0], 8
205           adds r2 = 16, r12
206           adds r3 = 32, r12
207           ;;
208         }
209         { .mmi
210           ldf.fill f8 = [r2], 32
211           ldf.fill f9 = [r3], 32
212           mov b0 = loc1
213           ;;
214         }
215         { .mmi
216           ldf.fill f10 = [r2], 32
217           ldf.fill f11 = [r3], 32
218           mov b6 = r9
219           ;;
220         }
221         { .mmi
222           ldf.fill f12 = [r2], 32
223           ldf.fill f13 = [r3], 32
224           mov ar.pfs = loc0
225           ;;
226         }
227         { .mmi
228           ldf.fill f14 = [r2], 32
229           ldf.fill f15 = [r3], 32
230           adds r12 = 160, r12
231           ;;
232         }
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.  */
235         { .mmb
236           alloc r2 = ar.pfs, 0, 0, 8, 0
237           ld8 gp = [ret0]
238           br.sptk.many b6
239           ;;
240         }
241         .endp " #tramp_name "#")
242
243 #ifndef PROF
244 #define ELF_MACHINE_RUNTIME_TRAMPOLINE                          \
245   TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);             \
246   TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
247 #else
248 #define ELF_MACHINE_RUNTIME_TRAMPOLINE                          \
249   TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);             \
250   strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
251 #endif
252
253
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.  */
257
258 #define RTLD_START asm ("\
259 .text
260         .global _start#
261         .proc _start#
262 _start:
263 0:      { .mii
264           alloc loc0 = ar.pfs, 0, 3, 4, 0
265           mov r2 = ip
266           addl r3 = @gprel(0b), r0
267           ;;
268         }
269         { .mlx
270           /* Calculate the GP, and save a copy in loc1.  */
271           sub gp = r2, r3
272           movl r8 = 0x9804c0270033f
273           ;;
274         }
275         { .mii
276           mov ar.fpsr = r8
277           sub loc1 = r2, r3
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.  */
280           adds out0 = 16, sp
281         }
282         { .bbb
283           br.call.sptk.many b0 = _dl_start#
284           ;;
285         }
286         .endp _start#
287         /* FALLTHRU */
288         .global _dl_start_user#
289         .proc _dl_start_user#
290 _dl_start_user:
291         { .mii
292           /* Save the pointer to the user entry point fptr in loc2.  */
293           mov loc2 = ret0
294           /* Store the highest stack address.  */
295           addl r2 = @ltoff(__libc_stack_end#), gp
296           addl r3 = @gprel(_dl_skip_args), gp
297           ;;
298         }
299         { .mmi
300           ld8 r2 = [r2]
301           ld4 r3 = [r3]
302           adds r11 = 24, sp     /* Load the address of argv. */
303           ;;
304         }
305         { .mii
306           st8 [r2] = sp
307           adds r10 = 16, sp     /* Load the address of argc. */
308           mov out2 = r11
309           ;;
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
314
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
317              envp. H.J.  */
318         }
319         { .mmi
320           ld8 out1 = [r10]      /* is argc actually stored as a long
321                                    or as an int? */
322           ;;
323           sub out1 = out1, r3   /* Get the new argc. */
324           shladd r15 = r3, 3, r11 /* The address of the argv we move */
325           ;;
326         }
327         /* ??? Could probably merge these two loops into 3 bundles.
328            using predication to control which set of copies we're on.  */
329 1:      /* Copy argv. */
330         { .mfi
331           ld8 r16 = [r15], 8    /* Load the value in the old argv. */
332           ;;
333         }
334         { .mib
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
338           ;;
339         }
340         { .mib
341           mov out3 = r11
342           addl out0 = @ltoff(_dl_loaded), gp
343         }
344 1:      /* Copy env. */
345         { .mfi
346           ld8 r16 = [r15], 8    /* Load the value in the old env. */
347           ;;
348         }
349         { .mib
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
353           ;;
354         }
355         { .mmb
356           st8 [r10] = out1              /* Record the new argc. */
357           ld8 out0 = [out0]
358         }
359         { .mfb
360           ld8 out0 = [out0]             /* get the linkmap */
361           br.call.sptk.many b0 = _dl_init#
362           ;;
363         }
364         /* Pass our finializer function to the user,
365            and jump to the user's entry point.  */
366         { .mmi
367           ld8 r3 = [loc2], 8
368           mov b0 = r0
369         }
370         { .mmi
371           addl ret0 = @ltoff(@fptr(_dl_fini#)), gp
372           ;;
373           mov b6 = r3
374         }
375         { .mmi
376           ld8 ret0 = [ret0]
377           ld8 gp = [loc2]
378           mov ar.pfs = loc0
379           ;;
380         }
381         { .mfb
382           br.sptk.many b6
383           ;;
384         }
385         .endp _dl_start_user#
386 .previous");
387
388
389 #ifndef RTLD_START_SPECIAL_INIT
390 #define RTLD_START_SPECIAL_INIT /* nothing */
391 #endif
392
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)
397
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)
401
402 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
403 #define ELF_MACHINE_JMP_SLOT     R_IA64_IPLTLSB
404
405 /* According to the IA-64 specific documentation, Rela is always used.  */
406 #define ELF_MACHINE_NO_REL 1
407
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
411
412 /* Return the address of the entry point. */
413 extern ElfW(Addr) _dl_start_address (const struct link_map *map,
414                                      ElfW(Addr) start);
415
416 #define ELF_MACHINE_START_ADDRESS(map, start) \
417   _dl_start_address ((map), (start))
418
419 #define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
420   elf_machine_fixup_plt ((l), (reloc), (rel_addr), (value))
421
422 #define elf_machine_profile_plt(reloc_addr) ((Elf64_Addr) (reloc_addr))
423
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)
429 {
430   /* l is the link_map for the caller, t is the link_map for the object
431    * being called */
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;
436 }
437
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,
441                        Elf64_Addr value)
442 {
443   /* No need to handle rel vs rela since IA64 is rela only */
444   return value + reloc->r_addend;
445 }
446
447 #endif /* !dl_machine_h */
448
449 #ifdef RESOLVE_MAP
450
451 #define R_IA64_TYPE(R)   ((R) & -8)
452 #define R_IA64_FORMAT(R) ((R) & 7)
453
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
458
459
460 /* Perform the relocation specified by RELOC and SYM (which is fully
461    resolved).  MAP is the object containing the reloc.  */
462 static inline void
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)
468 {
469   unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
470   Elf64_Addr value;
471
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);
479 #endif
480
481   /* We cannot use a switch here because we cannot locate the switch
482      jump table until we've self-relocated.  */
483
484   if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB))
485     {
486       value = *reloc_addr;
487 #ifndef RTLD_BOOTSTRAP
488       /* Already done in dynamic linker.  */
489       if (map != &_dl_rtld_map)
490 #endif
491         value += map->l_addr;
492     }
493   else if (r_type == R_IA64_NONE)
494     return;
495   else
496     {
497       struct link_map *sym_map;
498
499       /*
500        * RESOLVE_MAP() will return NULL if it fail to locate the symbol
501        */
502       if ((sym_map = RESOLVE_MAP (&sym, version, r_type)))
503         {
504           value = sym ? sym_map->l_addr + sym->st_value : 0;
505           value += reloc->r_addend;
506
507           if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB))
508             ;/* No adjustment.  */
509           else if (r_type == R_IA64_IPLTLSB)
510             {
511               elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
512               return;
513             }
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);
517 #else
518           {
519             struct ia64_fptr *p_boot_ldso_fptr;
520             struct ia64_fptr **p_fptr_root;
521             int *p_fptr_count;
522
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),
531                  "=r"(p_fptr_root),
532                  "=r"(p_fptr_count));
533
534             /*
535              * Go from the top - __ia64_make_fptr goes from the bottom,
536              * this way we will never clash.
537              */
538             value = __ia64_make_fptr (sym_map, value, p_fptr_root,
539                                       &p_boot_ldso_fptr[--*p_fptr_count]);
540           }
541 #endif
542           else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB))
543             value -= (Elf64_Addr)reloc_addr & -16;
544           else
545             assert (! "unexpected dynamic reloc type");
546         }
547       else
548         value = 0;
549     }
550
551   /* ??? Ignore MSB and Instruction format for now.  */
552   if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB)
553     *reloc_addr = value;
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)
557     {
558       reloc_addr[0] = 0;
559       reloc_addr[1] = 0;
560     }
561   else
562     assert (! "unexpected dynamic reloc format");
563 }
564
565
566 /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt.  */
567 static inline void
568 elf_machine_lazy_rel (struct link_map *map,
569                       Elf64_Addr l_addr, const Elf64_Rela *reloc)
570 {
571   Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
572   unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
573
574   if (r_type == R_IA64_IPLTLSB)
575     {
576       reloc_addr[0] += l_addr;
577       reloc_addr[1] += l_addr;
578     }
579   else if (r_type == R_IA64_NONE)
580     return;
581   else
582     assert (! "unexpected PLT reloc type");
583 }
584
585 #endif /* RESOLVE_MAP */