Thu Jun 13 00:02:25 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[kopensolaris-gnu/glibc.git] / sysdeps / alpha / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  Alpha version.
2 Copyright (C) 1996 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Richard Henderson <rht@tamu.edu>.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB.  If
18 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
19 Cambridge, MA 02139, USA.  */
20
21 /* This was written in the absense of an ABI -- don't expect
22    it to remain unchanged.  */
23
24 #define ELF_MACHINE_NAME "alpha"
25
26 #include <assert.h>
27 #include <string.h>
28 #include <link.h>
29
30
31 /* Return nonzero iff E_MACHINE is compatible with the running host.  */
32 static inline int
33 elf_machine_matches_host (Elf64_Word e_machine)
34 {
35   return e_machine == EM_ALPHA;
36 }
37
38
39 /* Return the run-time address of the _GLOBAL_OFFSET_TABLE_.
40    Must be inlined in a function which uses global data.  */
41 static inline Elf64_Addr *
42 elf_machine_got (void)
43 {
44   register Elf64_Addr gp __asm__("$29");
45   return (Elf64_Addr *)(gp - 0x8000);
46 }
47
48
49 /* Return the run-time load address of the shared object.  */
50 static inline Elf64_Addr
51 elf_machine_load_address (void)
52 {
53   /* NOTE: While it is generally unfriendly to put data in the text
54      segment, it is only slightly less so when the "data" is an
55      instruction.  While we don't have to worry about GLD just yet, an
56      optimizing linker might decide that our "data" is an unreachable
57      instruction and throw it away -- with the right switches, DEC's
58      linker will do this.  What ought to happen is we should add
59      something to GAS to allow us access to the new GPREL_HI32/LO32
60      relocation types stolen from OSF/1 3.0.  */
61   /* This code relies on the fact that BRADDR relocations do not
62      appear in dynamic relocation tables.  Not that that would be very
63      useful anyway -- br/bsr has a 4MB range and the shared libraries
64      are usually many many terabytes away.  */
65
66   Elf64_Addr dot;
67   long zero_disp;
68
69   asm("br %0, 1f\n\t"
70       ".weak __load_address_undefined\n\t"
71       "br $0, __load_address_undefined\n"
72       "1:"
73       : "=r"(dot));
74
75   zero_disp = *(int *)dot;
76   zero_disp = (zero_disp << 43) >> 41;
77
78   return dot + 4 + zero_disp;
79 }
80
81
82 /* Fix up the instructions of a PLT entry to invoke the function
83    rather than the dynamic linker.  */
84 static inline void
85 elf_alpha_fix_plt(struct link_map *l,
86                   const Elf64_Rela *reloc,
87                   Elf64_Addr value)
88 {
89   const Elf64_Rela *rela_plt;
90   Elf64_Word *plte;
91   long disp;
92
93   /* Recover the PLT entry address by calculating reloc's index into the
94      .rela.plt, and finding that entry in the .plt.  */
95
96   rela_plt = (void *)(l->l_addr + l->l_info[DT_JMPREL]->d_un.d_ptr);
97
98   plte = (void *)(l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr);
99   plte += 2*(reloc - rela_plt) + 8;
100
101   /* Find the displacement from the plt entry to the function.  */
102
103   disp = value - (Elf64_Addr)&plte[2];
104
105   /* Change "lda $27, ofs($31)" to "ldq $27, ofs($gp)" */
106   plte[0] = 0xa77d0000 | (plte[0] & 0xffff);
107
108   if (disp >= -0x100000 && disp < 0x100000)
109     {
110       /* If we are in range, use br to perfect branch prediction and
111          elide the dependancy on the address load.  This case happens,
112          e.g., when a shared library call is resolved to the same library.  */
113       /* Change "br $0, plt0" to "br $31,function" */
114       plte[1] = 0xc3e00000 | (disp & 0x1fffff);
115     }
116   else
117     {
118       /* Don't bother with the hint since we already know the hint is
119          wrong.  Eliding it prevents the wrong page from getting pulled
120          into the cache.  */
121       /* Change "br $0, plt0" to "jmp $31,($27)" */
122       plte[1] = 0x6bfb0000;
123     }
124
125   /* Flush the instruction cache now that we've diddled.   Tag it as
126      modifying memory to checkpoint memory writes during optimization.  */
127   asm volatile("call_pal 0x86" : : : "memory");
128 }
129
130 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
131    MAP is the object containing the reloc.  */
132 static inline void
133 elf_machine_rela (struct link_map *map,
134                   const Elf64_Rela *reloc,
135                   const Elf64_Sym *sym,
136                   Elf64_Addr (*resolve) (const Elf64_Sym **ref,
137                                          Elf64_Addr reloc_addr,
138                                          int noplt))
139 {
140   Elf64_Addr *const reloc_addr = (void *)(map->l_addr + reloc->r_offset);
141   unsigned long r_info = ELF64_R_TYPE (reloc->r_info);
142
143   /* We cannot use a switch here because we cannot locate the switch
144      jump table until we've self-relocated.  */
145
146   if (r_info == R_ALPHA_RELATIVE)
147     {
148       /* Already done in dynamic linker.  */
149       if (!resolve || map != &_dl_rtld_map)
150         *reloc_addr += map->l_addr;
151     }
152   else if (r_info == R_ALPHA_NONE)
153     ;
154   else
155     {
156       Elf64_Addr loadbase, sym_value;
157
158       if (resolve)
159         {
160           loadbase = (*resolve)(&sym, (Elf64_Addr)reloc_addr,
161                                 r_info == R_ALPHA_JMP_SLOT);
162         }
163       else
164         loadbase = map->l_addr;
165
166       sym_value = sym ? loadbase + sym->st_value : 0;
167
168       if (r_info == R_ALPHA_GLOB_DAT)
169         {
170           *reloc_addr = sym_value;
171         }
172       else if (r_info == R_ALPHA_JMP_SLOT)
173         {
174           *reloc_addr = sym_value;
175           elf_alpha_fix_plt(map, reloc, sym_value);
176         }
177       else if (r_info == R_ALPHA_REFQUAD)
178         {
179           sym_value += *reloc_addr;
180           if (resolve && map == &_dl_rtld_map)
181             {
182               /* Undo the relocation done here during bootstrapping.
183                  Now we will relocate anew, possibly using a binding
184                  found in the user program or a loaded library rather
185                  than the dynamic linker's built-in definitions used
186                  while loading those libraries.  */
187               const Elf64_Sym *const dlsymtab
188                 = (void *)(map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
189               sym_value -= map->l_addr;
190               sym_value -= dlsymtab[ELF64_R_SYM(reloc->r_info)].st_value;
191             }
192           else
193             sym_value += reloc->r_addend;
194           *reloc_addr = sym_value;
195         }
196       else if (r_info == R_ALPHA_COPY)
197         memcpy (reloc_addr, (void *) sym_value, sym->st_size);
198       else
199         assert (! "unexpected dynamic reloc type");
200     }
201 }
202
203 static inline void
204 elf_machine_lazy_rel (struct link_map *map, const Elf64_Rela *reloc)
205 {
206   Elf64_Addr *const reloc_addr = (void *)(map->l_addr + reloc->r_offset);
207   unsigned long r_info = ELF64_R_TYPE (reloc->r_info);
208
209   if (r_info == R_ALPHA_JMP_SLOT)
210     {
211       /* Perform a RELATIVE reloc on the .got entry that transfers
212          to the .plt.  */
213       *reloc_addr += map->l_addr;
214     }
215   else if (r_info == R_ALPHA_NONE)
216     ;
217   else
218     assert (! "unexpected PLT reloc type");
219 }
220
221 /* The alpha never uses Elf_Rel relocations.  */
222 #define ELF_MACHINE_NO_REL 1
223
224
225 /* Set up the loaded object described by L so its unrelocated PLT
226    entries will jump to the on-demand fixup code in dl-runtime.c.  */
227
228 static inline void
229 elf_machine_runtime_setup (struct link_map *l, int lazy)
230 {
231   Elf64_Addr plt;
232   extern void _dl_runtime_resolve (void);
233
234   if (l->l_info[DT_JMPREL] && lazy)
235     {
236       /* The GOT entries for the functions in the PLT have not been
237          filled in yet.  Their initial contents are directed to the
238          PLT which arranges for the dynamic linker to be called.  */
239       plt = l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr;
240
241       /* This function will be called to perform the relocation.  */
242       *(Elf64_Addr *)(plt + 16) = (Elf64_Addr) &_dl_runtime_resolve;
243
244       /* Identify this shared object */
245       *(Elf64_Addr *)(plt + 24) = (Elf64_Addr) l;
246     }
247 }
248
249 /* This code is used in dl-runtime.c to call the `fixup' function
250    and then redirect to the address it returns.  */
251 #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ( \
252 "/* Trampoline for _dl_runtime_resolver */
253         .globl _dl_runtime_resolve
254         .ent _dl_runtime_resolve
255 _dl_runtime_resolve:
256         lda     $sp, -168($sp)
257         .frame  $sp, 168, $26
258         /* Preserve all registers that C normally doesn't.  */
259         stq     $26, 0($sp)
260         stq     $0, 8($sp)
261         stq     $1, 16($sp)
262         stq     $2, 24($sp)
263         stq     $3, 32($sp)
264         stq     $4, 40($sp)
265         stq     $5, 48($sp)
266         stq     $6, 56($sp)
267         stq     $7, 64($sp)
268         stq     $8, 72($sp)
269         stq     $16, 80($sp)
270         stq     $17, 88($sp)
271         stq     $18, 96($sp)
272         stq     $19, 104($sp)
273         stq     $20, 112($sp)
274         stq     $21, 120($sp)
275         stq     $22, 128($sp)
276         stq     $23, 136($sp)
277         stq     $24, 144($sp)
278         stq     $25, 152($sp)
279         stq     $29, 160($sp)
280         .mask   0x27ff01ff, -168
281         /* Set up our $gp */
282         br      $gp, .+4
283         ldgp    $gp, 0($gp)
284         .prologue 1
285         /* Set up the arguments for _dl_runtime_resolve. */
286         /* $16 = link_map out of plt0 */
287         ldq     $16, 8($27)
288         /* $17 = (($0 - 8) - ($1 + 16)) / 8 * sizeof(Elf_Rela) */
289         subq    $28, $27, $28
290         subq    $28, 24, $28
291         addq    $28, $28, $17
292         addq    $28, $17, $17
293         /* Do the fixup */
294         bsr     $26, fixup..ng
295         /* Move the destination address to a safe place.  */
296         mov     $0, $27
297         /* Restore program registers.  */
298         ldq     $26, 0($sp)
299         ldq     $0, 8($sp)
300         ldq     $1, 16($sp)
301         ldq     $2, 24($sp)
302         ldq     $3, 32($sp)
303         ldq     $4, 40($sp)
304         ldq     $5, 48($sp)
305         ldq     $6, 56($sp)
306         ldq     $7, 64($sp)
307         ldq     $8, 72($sp)
308         ldq     $16, 80($sp)
309         ldq     $17, 88($sp)
310         ldq     $18, 96($sp)
311         ldq     $19, 104($sp)
312         ldq     $20, 112($sp)
313         ldq     $21, 120($sp)
314         ldq     $22, 128($sp)
315         ldq     $23, 136($sp)
316         ldq     $24, 144($sp)
317         ldq     $25, 152($sp)
318         ldq     $29, 160($sp)
319         /* Clean up and turn control to the destination */
320         lda     $sp, 168($sp)
321         jmp     $31, ($27)
322         .end _dl_runtime_resolve");
323
324 /* The PLT uses Elf_Rel relocs.  */
325 #define elf_machine_relplt elf_machine_rela
326
327 /* Mask identifying addresses reserved for the user program,
328    where the dynamic linker should not map anything.  */
329 /* FIXME */
330 #define ELF_MACHINE_USER_ADDRESS_MASK   (~0x1FFFFFFFFUL)
331
332 /* Initial entry point code for the dynamic linker.
333    The C function `_dl_start' is the real entry point;
334    its return value is the user program's entry point.  */
335
336 #define RTLD_START asm ("\
337 .text
338         .globl _start
339         .globl _dl_start_user
340 _start:
341         br      $gp,.+4
342         ldgp    $gp, 0($gp)
343         /* Pass pointer to argument block to _dl_start.  */
344         mov     $sp, $16
345         bsr     $26, _dl_start..ng
346 _dl_start_user:
347         /* Save the user entry point address in s0.  */
348         mov     $0, $9
349         /* See if we were run as a command with the executable file
350            name as an extra leading argument.  If so, adjust the stack
351            pointer to skip _dl_skip_args words.  */
352         ldl     $1, _dl_skip_args
353         beq     $1, 0f
354         ldq     $2, 0($sp)
355         subq    $2, $1, $2
356         s8addq  $1, $sp, $sp
357         stq     $2, 0($sp)
358         /* Load _dl_default_scope[2] into s1 to pass to _dl_init_next.  */
359 0:      ldq     $10, _dl_default_scope+16
360         /* Call _dl_init_next to return the address of an initalizer
361            function to run.  */
362 1:      mov     $10, $16
363         jsr     $26, _dl_init_next
364         ldgp    $gp, 0($26)
365         beq     $0, 2f
366         mov     $0, $27
367         jsr     $26, ($0)
368         ldgp    $gp, 0($26)
369         br      1b
370 2:      /* Pass our finalizer function to the user in $0. */
371         lda     $0, _dl_fini
372         /* Jump to the user's entry point.  */
373         jmp     ($9)");