]> sourceware.org Git - glibc.git/blob - sysdeps/sparc/sparc32/dl-machine.h
Update.
[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 */
This page took 0.07385 seconds and 6 git commands to generate.