aedf2a35e52c0131d1892aee564641cce2feb38c
[platform/upstream/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
64 /* Return the link-time address of _DYNAMIC.  */
65 static inline Elf64_Addr
66 elf_machine_dynamic (void)
67 {
68   Elf64_Addr *p;
69
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));
79
80   return *p;
81 }
82
83
84 /* Return the run-time load address of the shared object.  */
85 static inline Elf64_Addr
86 elf_machine_load_address (void)
87 {
88   Elf64_Addr ip;
89   int *p;
90
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));
99
100   return ip - (Elf64_Addr)*p;
101 }
102
103
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.  */
106
107 static inline int
108 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
109 {
110   extern void _dl_runtime_resolve (void);
111   extern void _dl_runtime_profile (void);
112
113   if (lazy)
114     {
115       register Elf64_Addr gp __asm__("gp");
116       Elf64_Addr *reserve, doit;
117
118       /*
119        * Careful with the typecast here or it will try to add l-l_addr
120        * pointer elements
121        */
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;
126
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
131         {
132           if (_dl_name_match_p (_dl_profile, l))
133             {
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;
137             }
138           doit = (Elf64_Addr) ((struct ia64_fptr *)&_dl_runtime_profile)->func;
139         }
140
141       reserve[1] = doit;
142       reserve[2] = gp;
143     }
144
145   return lazy;
146 }
147
148
149 /*
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.
153
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
158  */
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, 2, 3, 0
167           adds r2 = -144, r12
168           adds r3 = -128, r12
169         }
170         { .mii
171           adds r12 = -160, r12
172           mov loc1 = b0
173           mov out2 = b0         /* needed by fixup_profile */
174           ;;
175         }
176         { .mmi
177           stf.spill [r2] = f8, 32
178           stf.spill [r3] = f9, 32
179           mov out0 = r16
180           ;;
181         }
182         { .mmi
183           stf.spill [r2] = f10, 32
184           stf.spill [r3] = f11, 32
185           shl out1 = r15, 4
186           ;;
187         }
188         { .mmi
189           stf.spill [r2] = f12, 32
190           stf.spill [r3] = f13, 32
191           shladd out1 = r15, 3, out1
192           ;;
193         }
194         { .mmb
195           stf.spill [r2] = f14
196           stf.spill [r3] = f15
197           br.call.sptk.many b0 = " #fixup_name "#
198         }
199         { .mii
200           ld8 r9 = [ret0], 8
201           adds r2 = 16, r12
202           adds r3 = 32, r12
203           ;;
204         }
205         { .mmi
206           ldf.fill f8 = [r2], 32
207           ldf.fill f9 = [r3], 32
208           mov b0 = loc1
209           ;;
210         }
211         { .mmi
212           ldf.fill f10 = [r2], 32
213           ldf.fill f11 = [r3], 32
214           mov b6 = r9
215           ;;
216         }
217         { .mmi
218           ldf.fill f12 = [r2], 32
219           ldf.fill f13 = [r3], 32
220           mov ar.pfs = loc0
221           ;;
222         }
223         { .mmi
224           ldf.fill f14 = [r2], 32
225           ldf.fill f15 = [r3], 32
226           adds r12 = 160, r12
227           ;;
228         }
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.  */
231         { .mmb
232           alloc r2 = ar.pfs, 0, 0, 8, 0
233           ld8 gp = [ret0]
234           br.sptk.many b6
235           ;;
236         }
237         .endp " #tramp_name "#")
238
239 #ifndef PROF
240 #define ELF_MACHINE_RUNTIME_TRAMPOLINE                          \
241   TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);             \
242   TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
243 #else
244 #define ELF_MACHINE_RUNTIME_TRAMPOLINE                          \
245   TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);             \
246   strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
247 #endif
248
249
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.  */
253
254 #define RTLD_START asm ("\
255 .text
256         .global _start#
257         .proc _start#
258 _start:
259 0:      { .mii
260           alloc loc0 = ar.pfs, 0, 3, 4, 0
261           mov r2 = ip
262           addl r3 = @gprel(0b), r0
263           ;;
264         }
265         { .mlx
266           /* Calculate the GP, and save a copy in loc1.  */
267           sub gp = r2, r3
268           movl r8 = 0x9804c0270033f
269           ;;
270         }
271         { .mii
272           mov ar.fpsr = r8
273           sub loc1 = r2, r3
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.  */
276           adds out0 = 16, sp
277         }
278         { .bbb
279           br.call.sptk.many b0 = _dl_start#
280           ;;
281         }
282         .endp _start#
283         /* FALLTHRU */
284         .global _dl_start_user#
285         .proc _dl_start_user#
286 _dl_start_user:
287         { .mii
288           /* Save the pointer to the user entry point fptr in loc2.  */
289           mov loc2 = ret0
290           /* Store the highest stack address.  */
291           addl r2 = @ltoff(__libc_stack_end#), gp
292           addl r3 = @gprel(_dl_skip_args), gp
293           ;;
294         }
295         { .mmi
296           ld8 r2 = [r2]
297           ld4 r3 = [r3]
298           adds r11 = 24, sp     /* Load the address of argv. */
299           ;;
300         }
301         { .mii
302           st8 [r2] = sp
303           adds r10 = 16, sp     /* Load the address of argc. */
304           mov out2 = r11
305           ;;
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
310
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. H.J.  */
314         }
315         { .mmi
316           ld8 out1 = [r10]      /* is argc actually stored as a long
317                                    or as an int? */
318           ;;
319           sub out1 = out1, r3   /* Get the new argc. */
320           shladd r15 = r3, 3, r11 /* The address of the argv we move */
321           ;;
322         }
323         /* ??? Could probably merge these two loops into 3 bundles.
324            using predication to control which set of copies we're on.  */
325 1:      /* Copy argv. */
326         { .mfi
327           ld8 r16 = [r15], 8    /* Load the value in the old argv. */
328           ;;
329         }
330         { .mib
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
334           ;;
335         }
336         { .mib
337           mov out3 = r11
338           addl out0 = @ltoff(_dl_loaded), gp
339         }
340 1:      /* Copy env. */
341         { .mfi
342           ld8 r16 = [r15], 8    /* Load the value in the old env. */
343           ;;
344         }
345         { .mib
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
349           ;;
350         }
351         { .mmb
352           st8 [r10] = out1              /* Record the new argc. */
353           ld8 out0 = [out0]
354           ;;
355         }
356         { .mfb
357           ld8 out0 = [out0]             /* get the linkmap */
358           br.call.sptk.many b0 = _dl_init#
359           ;;
360         }
361         /* Pass our finializer function to the user,
362            and jump to the user's entry point.  */
363         { .mmi
364           ld8 r3 = [loc2], 8
365           mov b0 = r0
366         }
367         { .mmi
368           addl ret0 = @ltoff(@fptr(_dl_fini#)), gp
369           ;;
370           mov b6 = r3
371         }
372         { .mmi
373           ld8 ret0 = [ret0]
374           ld8 gp = [loc2]
375           mov ar.pfs = loc0
376           ;;
377         }
378         { .mfb
379           br.sptk.many b6
380           ;;
381         }
382         .endp _dl_start_user#
383 .previous");
384
385
386 #ifndef RTLD_START_SPECIAL_INIT
387 #define RTLD_START_SPECIAL_INIT /* nothing */
388 #endif
389
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)
394
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)
398
399 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
400 #define ELF_MACHINE_JMP_SLOT     R_IA64_IPLTLSB
401
402 /* According to the IA-64 specific documentation, Rela is always used.  */
403 #define ELF_MACHINE_NO_REL 1
404
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
408
409 /* Return the address of the entry point. */
410 extern ElfW(Addr) _dl_start_address (const struct link_map *map,
411                                      ElfW(Addr) start);
412
413 #define ELF_MACHINE_START_ADDRESS(map, start) \
414   _dl_start_address ((map), (start))
415
416 #define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
417   elf_machine_fixup_plt ((l), (reloc), (rel_addr), (value))
418
419 #define elf_machine_profile_plt(reloc_addr) ((Elf64_Addr) (reloc_addr))
420
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)
426 {
427   /* l is the link_map for the caller, t is the link_map for the object
428    * being called */
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;
433 }
434
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,
438                        Elf64_Addr value)
439 {
440   /* No need to handle rel vs rela since IA64 is rela only */
441   return value + reloc->r_addend;
442 }
443
444 #endif /* !dl_machine_h */
445
446 #ifdef RESOLVE_MAP
447
448 #define R_IA64_TYPE(R)   ((R) & -8)
449 #define R_IA64_FORMAT(R) ((R) & 7)
450
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
455
456
457 /* Perform the relocation specified by RELOC and SYM (which is fully
458    resolved).  MAP is the object containing the reloc.  */
459 static inline void
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)
465 {
466   unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
467   Elf64_Addr value;
468
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);
476 #endif
477
478   /* We cannot use a switch here because we cannot locate the switch
479      jump table until we've self-relocated.  */
480
481   if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB))
482     {
483       value = *reloc_addr;
484 #ifndef RTLD_BOOTSTRAP
485       /* Already done in dynamic linker.  */
486       if (map != &_dl_rtld_map)
487 #endif
488         value += map->l_addr;
489     }
490   else if (r_type == R_IA64_NONE)
491     return;
492   else
493     {
494       struct link_map *sym_map;
495
496       /*
497        * RESOLVE_MAP() will return NULL if it fail to locate the symbol
498        */
499       if ((sym_map = RESOLVE_MAP (&sym, version, r_type)))
500         {
501           value = sym ? sym_map->l_addr + sym->st_value : 0;
502           value += reloc->r_addend;
503
504           if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB))
505             ;/* No adjustment.  */
506           else if (r_type == R_IA64_IPLTLSB)
507             {
508               elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
509               return;
510             }
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);
514 #else
515           {
516             struct ia64_fptr *p_boot_ldso_fptr;
517             struct ia64_fptr **p_fptr_root;
518             int *p_fptr_count;
519
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),
528                  "=r"(p_fptr_root),
529                  "=r"(p_fptr_count));
530
531             /*
532              * Go from the top - __ia64_make_fptr goes from the bottom,
533              * this way we will never clash.
534              */
535             value = __ia64_make_fptr (sym_map, value, p_fptr_root,
536                                       &p_boot_ldso_fptr[--*p_fptr_count]);
537           }
538 #endif
539           else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB))
540             value -= (Elf64_Addr)reloc_addr & -16;
541           else
542             assert (! "unexpected dynamic reloc type");
543         }
544       else
545         value = 0;
546     }
547
548   /* ??? Ignore MSB and Instruction format for now.  */
549   if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB)
550     *reloc_addr = value;
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)
554     {
555       reloc_addr[0] = 0;
556       reloc_addr[1] = 0;
557     }
558   else
559     assert (! "unexpected dynamic reloc format");
560 }
561
562
563 /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt.  */
564 static inline void
565 elf_machine_lazy_rel (struct link_map *map,
566                       Elf64_Addr l_addr, const Elf64_Rela *reloc)
567 {
568   Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
569   unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
570
571   if (r_type == R_IA64_IPLTLSB)
572     {
573       reloc_addr[0] += l_addr;
574       reloc_addr[1] += l_addr;
575     }
576   else if (r_type == R_IA64_NONE)
577     return;
578   else
579     assert (! "unexpected PLT reloc type");
580 }
581
582 #endif /* RESOLVE_MAP */