(RTLD_START): Adjust _dl_argv by _dl_skip_args. (ELF_MACHINE_FIXED_STACK): Removed.
[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 and adjust _dl_argv by _dl_skip_args.  H.J.  */
314         }
315         { .mib
316           ld8 out1 = [r10]      /* is argc actually stored as a long
317                                    or as an int? */
318           addl r2 = @ltoff(_dl_argv), gp
319           ;;
320         }
321         { .mmi
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
325           ;;
326         }
327         {
328           .mib
329           ld8 r17 = [r2]        /* Get _dl_argv. */
330           add r15 = r11, r3     /* The address of the argv we move */
331           ;;
332         }
333         /* ??? Could probably merge these two loops into 3 bundles.
334            using predication to control which set of copies we're on.  */
335 1:      /* Copy argv. */
336         { .mfi
337           ld8 r16 = [r15], 8    /* Load the value in the old argv. */
338           ;;
339         }
340         { .mib
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
344           ;;
345         }
346         { .mmi
347           mov out3 = r11
348           sub r17 = r17, r3     /* Substract _dl_skip_args. */
349           addl out0 = @ltoff(_dl_loaded), gp
350         }
351 1:      /* Copy env. */
352         { .mfi
353           ld8 r16 = [r15], 8    /* Load the value in the old env. */
354           ;;
355         }
356         { .mib
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
360           ;;
361         }
362         { .mmb
363           st8 [r10] = out1              /* Record the new argc. */
364           ld8 out0 = [out0]
365           ;;
366         }
367         { .mmb
368           ld8 out0 = [out0]             /* get the linkmap */
369           st8 [r2] = r17                /* Load the new _dl_argv. */
370           br.call.sptk.many b0 = _dl_init#
371           ;;
372         }
373         /* Pass our finializer function to the user,
374            and jump to the user's entry point.  */
375         { .mmi
376           ld8 r3 = [loc2], 8
377           mov b0 = r0
378         }
379         { .mmi
380           addl ret0 = @ltoff(@fptr(_dl_fini#)), gp
381           ;;
382           mov b6 = r3
383         }
384         { .mmi
385           ld8 ret0 = [ret0]
386           ld8 gp = [loc2]
387           mov ar.pfs = loc0
388           ;;
389         }
390         { .mfb
391           br.sptk.many b6
392           ;;
393         }
394         .endp _dl_start_user#
395 .previous");
396
397
398 #ifndef RTLD_START_SPECIAL_INIT
399 #define RTLD_START_SPECIAL_INIT /* nothing */
400 #endif
401
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)
406
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)
410
411 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
412 #define ELF_MACHINE_JMP_SLOT     R_IA64_IPLTLSB
413
414 /* According to the IA-64 specific documentation, Rela is always used.  */
415 #define ELF_MACHINE_NO_REL 1
416
417 /* Return the address of the entry point. */
418 extern ElfW(Addr) _dl_start_address (const struct link_map *map,
419                                      ElfW(Addr) start);
420
421 #define ELF_MACHINE_START_ADDRESS(map, start) \
422   _dl_start_address ((map), (start))
423
424 #define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
425   elf_machine_fixup_plt ((l), (reloc), (rel_addr), (value))
426
427 #define elf_machine_profile_plt(reloc_addr) ((Elf64_Addr) (reloc_addr))
428
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)
434 {
435   /* l is the link_map for the caller, t is the link_map for the object
436    * being called */
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;
441 }
442
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,
446                        Elf64_Addr value)
447 {
448   /* No need to handle rel vs rela since IA64 is rela only */
449   return value + reloc->r_addend;
450 }
451
452 #endif /* !dl_machine_h */
453
454 #ifdef RESOLVE_MAP
455
456 #define R_IA64_TYPE(R)   ((R) & -8)
457 #define R_IA64_FORMAT(R) ((R) & 7)
458
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
463
464
465 /* Perform the relocation specified by RELOC and SYM (which is fully
466    resolved).  MAP is the object containing the reloc.  */
467 static inline void
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)
473 {
474   unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
475   Elf64_Addr value;
476
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);
484 #endif
485
486   /* We cannot use a switch here because we cannot locate the switch
487      jump table until we've self-relocated.  */
488
489   if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB))
490     {
491       value = *reloc_addr;
492 #ifndef RTLD_BOOTSTRAP
493       /* Already done in dynamic linker.  */
494       if (map != &_dl_rtld_map)
495 #endif
496         value += map->l_addr;
497     }
498   else if (r_type == R_IA64_NONE)
499     return;
500   else
501     {
502       struct link_map *sym_map;
503
504       /*
505        * RESOLVE_MAP() will return NULL if it fail to locate the symbol
506        */
507       if ((sym_map = RESOLVE_MAP (&sym, version, r_type)))
508         {
509           value = sym ? sym_map->l_addr + sym->st_value : 0;
510           value += reloc->r_addend;
511
512           if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB))
513             ;/* No adjustment.  */
514           else if (r_type == R_IA64_IPLTLSB)
515             {
516               elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
517               return;
518             }
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);
522 #else
523           {
524             struct ia64_fptr *p_boot_ldso_fptr;
525             struct ia64_fptr **p_fptr_root;
526             int *p_fptr_count;
527
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),
536                  "=r"(p_fptr_root),
537                  "=r"(p_fptr_count));
538
539             /*
540              * Go from the top - __ia64_make_fptr goes from the bottom,
541              * this way we will never clash.
542              */
543             value = __ia64_make_fptr (sym_map, value, p_fptr_root,
544                                       &p_boot_ldso_fptr[--*p_fptr_count]);
545           }
546 #endif
547           else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB))
548             value -= (Elf64_Addr)reloc_addr & -16;
549           else
550             assert (! "unexpected dynamic reloc type");
551         }
552       else
553         value = 0;
554     }
555
556   /* ??? Ignore MSB and Instruction format for now.  */
557   if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB)
558     *reloc_addr = value;
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)
562     {
563       reloc_addr[0] = 0;
564       reloc_addr[1] = 0;
565     }
566   else
567     assert (! "unexpected dynamic reloc format");
568 }
569
570
571 /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt.  */
572 static inline void
573 elf_machine_lazy_rel (struct link_map *map,
574                       Elf64_Addr l_addr, const Elf64_Rela *reloc)
575 {
576   Elf64_Addr * const reloc_addr = (void *)(l_addr + reloc->r_offset);
577   unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
578
579   if (r_type == R_IA64_IPLTLSB)
580     {
581       reloc_addr[0] += l_addr;
582       reloc_addr[1] += l_addr;
583     }
584   else if (r_type == R_IA64_NONE)
585     return;
586   else
587     assert (! "unexpected PLT reloc type");
588 }
589
590 #endif /* RESOLVE_MAP */