Update.
[jlayton/glibc.git] / sysdeps / sparc / sparc32 / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  SPARC version.
2    Copyright (C) 1996, 1997 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 #define ELF_MACHINE_NAME "sparc"
21
22 #include <assert.h>
23 #include <string.h>
24 #include <link.h>
25 #include <sys/param.h>
26
27
28 /* Some SPARC opcodes we need to use for self-modifying code.  */
29 #define OPCODE_NOP      0x01000000 /* nop */
30 #define OPCODE_CALL     0x40000000 /* call ?; add PC-rel word address */
31 #define OPCODE_SETHI_G1 0x03000000 /* sethi ?, %g1; add value>>10 */
32 #define OPCODE_JMP_G1   0x81c06000 /* jmp %g1+?; add lo 10 bits of value */
33 #define OPCODE_SAVE_SP  0x9de3bfa8 /* save %sp, -(16+6)*4, %sp */
34
35
36 /* Return nonzero iff E_MACHINE is compatible with the running host.  */
37 static inline int
38 elf_machine_matches_host (Elf32_Half e_machine)
39 {
40   return e_machine == EM_SPARC;
41 }
42
43
44 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
45    first element of the GOT.  This must be inlined in a function which
46    uses global data.  */
47 static inline Elf32_Addr
48 elf_machine_dynamic (void)
49 {
50   register Elf32_Addr *got asm ("%l7");
51   return *got;
52 }
53
54 /* Return the run-time load address of the shared object.  */
55 static inline Elf32_Addr
56 elf_machine_load_address (void)
57 {
58   register Elf32_Addr pc __asm("%o7"), pic __asm("%l7"), got;
59
60   /* Utilize the fact that a local .got entry will be partially
61      initialized at startup awaiting its RELATIVE fixup.  */
62
63   __asm("sethi %%hi(.Load_address),%1\n"
64         ".Load_address:\n\t"
65         "call 1f\n\t"
66         "or %1,%%lo(.Load_address),%1\n"
67         "1:\tld [%2+%1],%1"
68         : "=r"(pc), "=r"(got) : "r"(pic));
69
70   return pc - got;
71 }
72
73 /* Set up the loaded object described by L so its unrelocated PLT
74    entries will jump to the on-demand fixup code in dl-runtime.c.  */
75
76 static inline int
77 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
78 {
79   Elf32_Addr *plt;
80   extern void _dl_runtime_resolve (Elf32_Word);
81
82   if (l->l_info[DT_JMPREL] && lazy)
83     {
84       /* The entries for functions in the PLT have not yet been filled in.
85          Their initial contents will arrange when called to set the high 22
86          bits of %g1 with an offset into the .rela.plt section and jump to
87          the beginning of the PLT.  */
88       plt = (Elf32_Addr *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr);
89
90       /* The beginning of the PLT does:
91
92                 save %sp, -64, %sp
93          pltpc: call _dl_runtime_resolve
94                 nop
95                 .word MAP
96
97          This saves the register window containing the arguments, and the
98          PC value (pltpc) implicitly saved in %o7 by the call points near the
99          location where we store the link_map pointer for this object.  */
100
101       plt[0] = OPCODE_SAVE_SP;
102       /* Construct PC-relative word address.  */
103       plt[1] = OPCODE_CALL | (((Elf32_Addr) &_dl_runtime_resolve -
104                                (Elf32_Addr) &plt[1]) >> 2);
105       plt[2] = OPCODE_NOP;      /* Fill call delay slot.  */
106       plt[3] = (Elf32_Addr) l;
107     }
108
109   return lazy;
110 }
111
112 /* This code is used in dl-runtime.c to call the `fixup' function
113    and then redirect to the address it returns.  */
114 #define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\
115         .globl _dl_runtime_resolve
116         .type _dl_runtime_resolve, @function
117 _dl_runtime_resolve:
118         /* Set up the arguments to fixup --
119            %o0 = link_map out of plt0
120            %o1 = offset of reloc entry  */
121         ld      [%o7 + 8], %o0
122         srl     %g1, 10, %o1
123         call    fixup
124          sub    %o1, 4*12, %o1
125         jmp     %o0
126          restore
127         .size _dl_runtime_resolve, . - _dl_runtime_resolve");
128
129 /* The address of the JMP_SLOT reloc is the .plt entry, thus we don't
130    dereference the reloc's addr to get the final destination.  Ideally
131    there would be a generic way to return the value of the symbol from
132    elf_machine_relplt, but as it is, the address of the .plt entry is
133    good enough.  */
134 #define ELF_FIXUP_RETURN_VALUE(map, result)  ((Elf32_Addr) &(result))
135
136 /* Nonzero iff TYPE should not be allowed to resolve to one of
137    the main executable's symbols, as for a COPY reloc.  */
138 #define elf_machine_lookup_noexec_p(type) ((type) == R_SPARC_COPY)
139
140 /* Nonzero iff TYPE describes relocation of a PLT entry, so
141    PLT entries should not be allowed to define the value.  */
142 #define elf_machine_lookup_noplt_p(type) ((type) == R_SPARC_JMP_SLOT)
143
144 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
145 #define ELF_MACHINE_RELOC_NOPLT R_SPARC_JMP_SLOT
146
147 /* The SPARC never uses Elf32_Rel relocations.  */
148 #define ELF_MACHINE_NO_REL 1
149
150 /* The SPARC overlaps DT_RELA and DT_PLTREL.  */
151 #define ELF_MACHINE_PLTREL_OVERLAP 1
152
153 /* The PLT uses Elf32_Rela relocs.  */
154 #define elf_machine_relplt elf_machine_rela
155
156 /* Initial entry point code for the dynamic linker.
157    The C function `_dl_start' is the real entry point;
158    its return value is the user program's entry point.  */
159
160 #define RTLD_START __asm__ ("\
161 .text
162         .globl _start
163         .type _start,@function
164 _start:
165   /* Allocate space for functions to drop their arguments.  */
166         sub     %sp, 6*4, %sp
167   /* Pass pointer to argument block to _dl_start.  */
168         call    _dl_start
169          add    %sp, 22*4, %o0
170         /* FALTHRU */
171         .globl _dl_start_user
172         .type _dl_start_user,@function
173 _dl_start_user:
174   /* Load the PIC register.  */
175 1:      call    2f
176          sethi  %hi(_GLOBAL_OFFSET_TABLE_-(1b-.)), %l7
177 2:      or      %l7, %lo(_GLOBAL_OFFSET_TABLE_-(1b-.)), %l7
178         add     %l7, %o7, %l7
179   /* Save the user entry point address in %l0 */
180         mov     %o0, %l0
181   /* See if we were run as a command with the executable file name as an
182      extra leading argument.  If so, adjust the contents of the stack.  */
183         sethi   %hi(_dl_skip_args), %g2
184         or      %g2, %lo(_dl_skip_args), %g2
185         ld      [%l7+%g2], %i0
186         ld      [%i0], %i0
187         tst     %i0
188         beq     3f
189          nop
190         /* Find out how far to shift.  */
191         ld      [%sp+22*4], %i1         /* load argc */
192         sub     %i1, %i0, %i1
193         sll     %i0, 2, %i2
194         st      %i1, [%sp+22*4]
195         add     %sp, 23*4, %i1
196         add     %i1, %i2, %i2
197         /* Copy down argv */
198 21:     ld      [%i2], %i3
199         add     %i2, 4, %i2
200         tst     %i3
201         st      %i3, [%i1]
202         bne     21b
203          add    %i1, 4, %i1
204         /* Copy down env */
205 22:     ld      [%i2], %i3
206         add     %i2, 4, %i2
207         tst     %i3
208         st      %i3, [%i1]
209         bne     22b
210          add    %i1, 4, %i1
211         /* Copy down auxiliary table.  */
212 23:     ld      [%i2], %i3
213         ld      [%i2+4], %i4
214         add     %i2, 8, %i2
215         tst     %i3
216         st      %i3, [%i1]
217         st      %i4, [%i1+4]
218         bne     23b
219          add    %i1, 8, %i1
220   /* Load _dl_default_scope[2] to pass to _dl_init_next.  */
221 3:      sethi   %hi(_dl_default_scope), %g1
222         or      %g1, %lo(_dl_default_scope), %g1
223         ld      [%l7+%g1], %l1
224         ld      [%l1+2*4], %l1
225   /* Call _dl_init_next to return the address of an initializer to run.  */
226 4:      call    _dl_init_next
227          mov    %l1, %o0
228         tst     %o0
229         beq     5f
230          nop
231         jmpl    %o0, %o7
232          nop
233         ba,a    4b
234   /* Clear the startup flag.  */
235 5:      sethi   %hi(_dl_starting_up), %g1
236         or      %g1, %lo(_dl_starting_up), %g1
237         ld      [%l7+%g1], %g1
238         st      %g0, [%g1]
239   /* Pass our finalizer function to the user in %g1.  */
240         sethi   %hi(_dl_fini), %g1
241         or      %g1, %lo(_dl_fini), %g1
242         ld      [%l7+%g1], %g1
243   /* Jump to the user's entry point and deallocate the extra stack we got.  */
244         jmp     %l0
245          add    %sp, 6*4, %sp
246         .size   _dl_start_user,.-_dl_start_user
247 .previous");
248
249 #ifdef RESOLVE
250 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
251    MAP is the object containing the reloc.  */
252
253 static inline void
254 elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
255                   const Elf32_Sym *sym, const struct r_found_version *version,
256                   Elf32_Addr *const reloc_addr)
257 {
258   extern unsigned long _dl_hwcap;
259
260   if (ELF32_R_TYPE (reloc->r_info) == R_SPARC_RELATIVE)
261     {
262 #ifndef RTLD_BOOTSTRAP
263       if (map != &_dl_rtld_map) /* Already done in rtld itself. */
264 #endif
265         *reloc_addr += map->l_addr + reloc->r_addend;
266     }
267   else
268     {
269       const Elf32_Sym *const refsym = sym;
270       Elf32_Addr value;
271       if (sym->st_shndx != SHN_UNDEF &&
272           ELF32_ST_BIND (sym->st_info) == STB_LOCAL)
273         value = map->l_addr;
274       else
275         {
276           value = RESOLVE (&sym, version, ELF32_R_TYPE (reloc->r_info));
277           if (sym)
278             value += sym->st_value;
279         }
280       value += reloc->r_addend; /* Assume copy relocs have zero addend.  */
281
282       switch (ELF32_R_TYPE (reloc->r_info))
283         {
284         case R_SPARC_COPY:
285 #ifndef RTLD_BOOTSTRAP
286           if (sym->st_size > refsym->st_size
287               || (_dl_verbose && sym->st_size < refsym->st_size))
288             {
289               extern char **_dl_argv;
290               const char *strtab;
291
292               strtab = ((void *) map->l_addr
293                         + map->l_info[DT_STRTAB]->d_un.d_ptr);
294               _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>",
295                                 ": Symbol `", strtab + refsym->st_name,
296                                 "' has different size in shared object, "
297                                 "consider re-linking\n", NULL);
298             }
299           memcpy (reloc_addr, (void *) value, MIN (sym->st_size,
300                                                    refsym->st_size));
301 #endif
302           break;
303         case R_SPARC_GLOB_DAT:
304         case R_SPARC_32:
305           *reloc_addr = value;
306           break;
307         case R_SPARC_JMP_SLOT:
308           /* For thread safety, write the instructions from the bottom and
309              flush before we overwrite the critical "b,a".  */
310           reloc_addr[2] = OPCODE_JMP_G1 | (value & 0x3ff);
311           if (_dl_hwcap & HWCAP_SPARC_FLUSH)
312             __asm __volatile ("flush %0+8" : : "r"(reloc_addr));
313           reloc_addr[1] = OPCODE_SETHI_G1 | (value >> 10);
314           if (_dl_hwcap & HWCAP_SPARC_FLUSH)
315             __asm __volatile ("flush %0+4" : : "r"(reloc_addr));
316           break;
317         case R_SPARC_8:
318           *(char *) reloc_addr = value;
319           break;
320         case R_SPARC_16:
321           *(short *) reloc_addr = value;
322           break;
323         case R_SPARC_DISP8:
324           *(char *) reloc_addr = (value - (Elf32_Addr) reloc_addr);
325           break;
326         case R_SPARC_DISP16:
327           *(short *) reloc_addr = (value - (Elf32_Addr) reloc_addr);
328           break;
329         case R_SPARC_DISP32:
330           *reloc_addr = (value - (Elf32_Addr) reloc_addr);
331           break;
332         case R_SPARC_LO10:
333           *reloc_addr = (*reloc_addr & ~0x3ff) | (value & 0x3ff);
334           break;
335         case R_SPARC_WDISP30:
336           *reloc_addr = ((*reloc_addr & 0xc0000000)
337                          | ((value - (unsigned int) reloc_addr) >> 2));
338           break;
339         case R_SPARC_HI22:
340           *reloc_addr = (*reloc_addr & 0xffc00000) | (value >> 10);
341           break;
342         case R_SPARC_NONE:              /* Alright, Wilbur.  */
343           break;
344         default:
345           assert (! "unexpected dynamic reloc type");
346           break;
347         }
348     }
349 }
350
351 static inline void
352 elf_machine_lazy_rel (struct link_map *map, const Elf32_Rela *reloc)
353 {
354   switch (ELF32_R_TYPE (reloc->r_info))
355     {
356     case R_SPARC_NONE:
357       break;
358     case R_SPARC_JMP_SLOT:
359       break;
360     default:
361       assert (! "unexpected PLT reloc type");
362       break;
363     }
364 }
365
366 #endif  /* RESOLVE */