Tue Jun 25 02:59:11 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[platform/upstream/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 <rth@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 got_addr,
88                   Elf64_Addr value)
89 {
90   const Elf64_Rela *rela_plt;
91   Elf64_Word *plte;
92   long edisp;
93
94   /* Recover the PLT entry address by calculating reloc's index into the
95      .rela.plt, and finding that entry in the .plt.  */
96
97   rela_plt = (void *)(l->l_addr + l->l_info[DT_JMPREL]->d_un.d_ptr);
98
99   plte = (void *)(l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr + 32);
100   plte += 3 * (reloc - rela_plt);
101
102   /* Find the displacement from the plt entry to the function.  */
103
104   edisp = (long)(value - (Elf64_Addr)&plte[3]) / 4;
105
106   if (edisp >= -0x100000 && edisp < 0x100000)
107     {
108       /* If we are in range, use br to perfect branch prediction and
109          elide the dependancy on the address load.  This case happens,
110          e.g., when a shared library call is resolved to the same library.  */
111
112       int hi, lo;
113       hi = value - (Elf64_Addr)&plte[0];
114       lo = (short)hi;
115       hi = (hi - lo) >> 16;
116
117       /* Emit "ldah $27,H($27)" */
118       plte[0] = 0x277b0000 | (hi & 0xffff);
119
120       /* Emit "lda $27,L($27)" */
121       plte[1] = 0x237b0000 | (lo & 0xffff);
122
123       /* Emit "br $31,function" */
124       plte[2] = 0xc3e00000 | (edisp & 0x1fffff);
125     }
126   else
127     {
128       /* Don't bother with the hint since we already know the hint is
129          wrong.  Eliding it prevents the wrong page from getting pulled
130          into the cache.  */
131
132       int hi, lo;
133       hi = got_addr - (Elf64_Addr)&plte[0];
134       lo = (short)hi;
135       hi = (hi - lo) >> 16;
136
137       /* Emit "ldah $27,H($27)" */
138       plte[0] = 0x277b0000 | (hi & 0xffff);
139
140       /* Emit "ldq $27,L($27)" */
141       plte[1] = 0xa77b0000 | (lo & 0xffff);
142
143       /* Emit "jmp $31,($27)" */
144       plte[2] = 0x6bfb0000;
145     }
146
147   /* Flush the instruction cache now that we've diddled.   Tag it as
148      modifying memory to checkpoint memory writes during optimization.  */
149   asm volatile("call_pal 0x86" : : : "memory");
150 }
151
152 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
153    MAP is the object containing the reloc.  */
154 static inline void
155 elf_machine_rela (struct link_map *map,
156                   const Elf64_Rela *reloc,
157                   const Elf64_Sym *sym,
158                   Elf64_Addr (*resolve) (const Elf64_Sym **ref,
159                                          Elf64_Addr reloc_addr,
160                                          int noplt))
161 {
162   Elf64_Addr *const reloc_addr = (void *)(map->l_addr + reloc->r_offset);
163   unsigned long r_info = ELF64_R_TYPE (reloc->r_info);
164
165   /* We cannot use a switch here because we cannot locate the switch
166      jump table until we've self-relocated.  */
167
168   if (r_info == R_ALPHA_RELATIVE)
169     {
170       /* Already done in dynamic linker.  */
171       if (!resolve || map != &_dl_rtld_map)
172         *reloc_addr += map->l_addr;
173     }
174   else if (r_info == R_ALPHA_NONE)
175     ;
176   else
177     {
178       Elf64_Addr loadbase, sym_value;
179
180       if (resolve)
181         {
182           loadbase = (*resolve)(&sym, (Elf64_Addr)reloc_addr,
183                                 r_info == R_ALPHA_JMP_SLOT);
184         }
185       else
186         loadbase = map->l_addr;
187
188       sym_value = sym ? loadbase + sym->st_value : 0;
189
190       if (r_info == R_ALPHA_GLOB_DAT)
191         {
192           *reloc_addr = sym_value;
193         }
194       else if (r_info == R_ALPHA_JMP_SLOT)
195         {
196           *reloc_addr = sym_value;
197           elf_alpha_fix_plt(map, reloc, (Elf64_Addr)reloc_addr, sym_value);
198         }
199       else if (r_info == R_ALPHA_REFQUAD)
200         {
201           sym_value += *reloc_addr;
202           if (resolve && map == &_dl_rtld_map)
203             {
204               /* Undo the relocation done here during bootstrapping.
205                  Now we will relocate anew, possibly using a binding
206                  found in the user program or a loaded library rather
207                  than the dynamic linker's built-in definitions used
208                  while loading those libraries.  */
209               const Elf64_Sym *const dlsymtab
210                 = (void *)(map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
211               sym_value -= map->l_addr;
212               sym_value -= dlsymtab[ELF64_R_SYM(reloc->r_info)].st_value;
213             }
214           else
215             sym_value += reloc->r_addend;
216           *reloc_addr = sym_value;
217         }
218       else if (r_info == R_ALPHA_COPY)
219         memcpy (reloc_addr, (void *) sym_value, sym->st_size);
220       else
221         assert (! "unexpected dynamic reloc type");
222     }
223 }
224
225 static inline void
226 elf_machine_lazy_rel (struct link_map *map, const Elf64_Rela *reloc)
227 {
228   Elf64_Addr *const reloc_addr = (void *)(map->l_addr + reloc->r_offset);
229   unsigned long r_info = ELF64_R_TYPE (reloc->r_info);
230
231   if (r_info == R_ALPHA_JMP_SLOT)
232     {
233       /* Perform a RELATIVE reloc on the .got entry that transfers
234          to the .plt.  */
235       *reloc_addr += map->l_addr;
236     }
237   else if (r_info == R_ALPHA_NONE)
238     ;
239   else
240     assert (! "unexpected PLT reloc type");
241 }
242
243 /* The alpha never uses Elf_Rel relocations.  */
244 #define ELF_MACHINE_NO_REL 1
245
246
247 /* Set up the loaded object described by L so its unrelocated PLT
248    entries will jump to the on-demand fixup code in dl-runtime.c.  */
249
250 static inline void
251 elf_machine_runtime_setup (struct link_map *l, int lazy)
252 {
253   Elf64_Addr plt;
254   extern void _dl_runtime_resolve (void);
255
256   if (l->l_info[DT_JMPREL] && lazy)
257     {
258       /* The GOT entries for the functions in the PLT have not been
259          filled in yet.  Their initial contents are directed to the
260          PLT which arranges for the dynamic linker to be called.  */
261       plt = l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr;
262
263       /* This function will be called to perform the relocation.  */
264       *(Elf64_Addr *)(plt + 16) = (Elf64_Addr) &_dl_runtime_resolve;
265
266       /* Identify this shared object */
267       *(Elf64_Addr *)(plt + 24) = (Elf64_Addr) l;
268     }
269 }
270
271 /* This code is used in dl-runtime.c to call the `fixup' function
272    and then redirect to the address it returns.  */
273 #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ( \
274 "/* Trampoline for _dl_runtime_resolver */
275         .globl _dl_runtime_resolve
276         .ent _dl_runtime_resolve
277 _dl_runtime_resolve:
278         lda     $sp, -168($sp)
279         .frame  $sp, 168, $26
280         /* Preserve all registers that C normally doesn't.  */
281         stq     $26, 0($sp)
282         stq     $0, 8($sp)
283         stq     $1, 16($sp)
284         stq     $2, 24($sp)
285         stq     $3, 32($sp)
286         stq     $4, 40($sp)
287         stq     $5, 48($sp)
288         stq     $6, 56($sp)
289         stq     $7, 64($sp)
290         stq     $8, 72($sp)
291         stq     $16, 80($sp)
292         stq     $17, 88($sp)
293         stq     $18, 96($sp)
294         stq     $19, 104($sp)
295         stq     $20, 112($sp)
296         stq     $21, 120($sp)
297         stq     $22, 128($sp)
298         stq     $23, 136($sp)
299         stq     $24, 144($sp)
300         stq     $25, 152($sp)
301         stq     $29, 160($sp)
302         .mask   0x27ff01ff, -168
303         /* Set up our $gp */
304         br      $gp, .+4
305         ldgp    $gp, 0($gp)
306         .prologue 1
307         /* Set up the arguments for _dl_runtime_resolve. */
308         /* $16 = link_map out of plt0 */
309         ldq     $16, 8($27)
310         /* $17 = offset of reloc entry */
311         mov     $28, $17
312         /* Do the fixup */
313         bsr     $26, fixup..ng
314         /* Move the destination address to a safe place.  */
315         mov     $0, $27
316         /* Restore program registers.  */
317         ldq     $26, 0($sp)
318         ldq     $0, 8($sp)
319         ldq     $1, 16($sp)
320         ldq     $2, 24($sp)
321         ldq     $3, 32($sp)
322         ldq     $4, 40($sp)
323         ldq     $5, 48($sp)
324         ldq     $6, 56($sp)
325         ldq     $7, 64($sp)
326         ldq     $8, 72($sp)
327         ldq     $16, 80($sp)
328         ldq     $17, 88($sp)
329         ldq     $18, 96($sp)
330         ldq     $19, 104($sp)
331         ldq     $20, 112($sp)
332         ldq     $21, 120($sp)
333         ldq     $22, 128($sp)
334         ldq     $23, 136($sp)
335         ldq     $24, 144($sp)
336         ldq     $25, 152($sp)
337         ldq     $29, 160($sp)
338         /* Clean up and turn control to the destination */
339         lda     $sp, 168($sp)
340         jmp     $31, ($27)
341         .end _dl_runtime_resolve");
342
343 /* The PLT uses Elf_Rel relocs.  */
344 #define elf_machine_relplt elf_machine_rela
345
346 /* Mask identifying addresses reserved for the user program,
347    where the dynamic linker should not map anything.  */
348 /* FIXME */
349 #define ELF_MACHINE_USER_ADDRESS_MASK   (~0x1FFFFFFFFUL)
350
351 /* Initial entry point code for the dynamic linker.
352    The C function `_dl_start' is the real entry point;
353    its return value is the user program's entry point.  */
354
355 #define RTLD_START asm ("\
356 .text
357         .globl _start
358         .globl _dl_start_user
359 _start:
360         br      $gp,.+4
361         ldgp    $gp, 0($gp)
362         /* Pass pointer to argument block to _dl_start.  */
363         mov     $sp, $16
364         bsr     $26, _dl_start..ng
365 _dl_start_user:
366         /* Save the user entry point address in s0.  */
367         mov     $0, $9
368         /* See if we were run as a command with the executable file
369            name as an extra leading argument.  If so, adjust the stack
370            pointer to skip _dl_skip_args words.  */
371         ldl     $1, _dl_skip_args
372         beq     $1, 0f
373         ldq     $2, 0($sp)
374         subq    $2, $1, $2
375         s8addq  $1, $sp, $sp
376         stq     $2, 0($sp)
377         /* Load _dl_default_scope[2] into s1 to pass to _dl_init_next.  */
378 0:      ldq     $10, _dl_default_scope+16
379         /* Call _dl_init_next to return the address of an initalizer
380            function to run.  */
381 1:      mov     $10, $16
382         jsr     $26, _dl_init_next
383         ldgp    $gp, 0($26)
384         beq     $0, 2f
385         mov     $0, $27
386         jsr     $26, ($0)
387         ldgp    $gp, 0($26)
388         br      1b
389 2:      /* Pass our finalizer function to the user in $0. */
390         lda     $0, _dl_fini
391         /* Jump to the user's entry point.  */
392         mov     $9, $27
393         jmp     ($9)");