]>
Commit | Line | Data |
---|---|---|
d5efd131 | 1 | /* Machine-dependent ELF dynamic relocation inline functions. IA-64 version. |
dff8da6b | 2 | Copyright (C) 1995-2024 Free Software Foundation, Inc. |
d5efd131 MF |
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 Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the 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 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
75efb018 | 16 | License along with the GNU C Library; if not, see |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
d5efd131 MF |
18 | |
19 | #ifndef dl_machine_h | |
20 | #define dl_machine_h 1 | |
21 | ||
22 | #define ELF_MACHINE_NAME "ia64" | |
23 | ||
24 | #include <assert.h> | |
25 | #include <string.h> | |
26 | #include <link.h> | |
27 | #include <errno.h> | |
28 | #include <dl-fptr.h> | |
29 | #include <tls.h> | |
d6d89608 AZ |
30 | #include <dl-static-tls.h> |
31 | #include <dl-machine-rel.h> | |
d5efd131 MF |
32 | |
33 | /* Translate a processor specific dynamic tag to the index | |
34 | in l_info array. */ | |
35 | #define DT_IA_64(x) (DT_IA_64_##x - DT_LOPROC + DT_NUM) | |
36 | ||
37 | static inline void __attribute__ ((always_inline)) | |
38 | __ia64_init_bootstrap_fdesc_table (struct link_map *map) | |
39 | { | |
40 | Elf64_Addr *boot_table; | |
41 | ||
42 | /* careful: this will be called before got has been relocated... */ | |
43 | asm (";; addl %0 = @gprel (_dl_boot_fptr_table), gp" : "=r"(boot_table)); | |
44 | ||
45 | map->l_mach.fptr_table_len = ELF_MACHINE_BOOT_FPTR_TABLE_LEN; | |
46 | map->l_mach.fptr_table = boot_table; | |
47 | } | |
48 | ||
490e6c62 FS |
49 | #define ELF_MACHINE_BEFORE_RTLD_RELOC(map, dynamic_info) \ |
50 | __ia64_init_bootstrap_fdesc_table (map); | |
d5efd131 MF |
51 | |
52 | /* Return nonzero iff ELF header is compatible with the running host. */ | |
53 | static inline int __attribute__ ((unused)) | |
54 | elf_machine_matches_host (const Elf64_Ehdr *ehdr) | |
55 | { | |
56 | return ehdr->e_machine == EM_IA_64; | |
57 | } | |
58 | ||
59 | ||
60 | /* Return the link-time address of _DYNAMIC. */ | |
61 | static inline Elf64_Addr __attribute__ ((unused, const)) | |
62 | elf_machine_dynamic (void) | |
63 | { | |
64 | Elf64_Addr *p; | |
65 | ||
66 | __asm__ ( | |
67 | ".section .sdata\n" | |
68 | " .type __dynamic_ltv#, @object\n" | |
69 | " .size __dynamic_ltv#, 8\n" | |
70 | "__dynamic_ltv:\n" | |
71 | " data8 @ltv(_DYNAMIC#)\n" | |
72 | ".previous\n" | |
73 | " addl %0 = @gprel(__dynamic_ltv#), gp ;;" | |
74 | : "=r" (p)); | |
75 | ||
76 | return *p; | |
77 | } | |
78 | ||
79 | ||
80 | /* Return the run-time load address of the shared object. */ | |
81 | static inline Elf64_Addr __attribute__ ((unused)) | |
82 | elf_machine_load_address (void) | |
83 | { | |
84 | Elf64_Addr ip; | |
85 | int *p; | |
86 | ||
87 | __asm__ ( | |
88 | "1: mov %0 = ip\n" | |
89 | ".section .sdata\n" | |
90 | "2: data4 @ltv(1b)\n" | |
91 | " .align 8\n" | |
92 | ".previous\n" | |
93 | " addl %1 = @gprel(2b), gp ;;" | |
94 | : "=r" (ip), "=r" (p)); | |
95 | ||
96 | return ip - (Elf64_Addr) *p; | |
97 | } | |
98 | ||
99 | /* Set up the loaded object described by L so its unrelocated PLT | |
100 | entries will jump to the on-demand fixup code in dl-runtime.c. */ | |
101 | ||
102 | static inline int __attribute__ ((unused, always_inline)) | |
490e6c62 FS |
103 | elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[], |
104 | int lazy, int profile) | |
d5efd131 MF |
105 | { |
106 | extern void _dl_runtime_resolve (void); | |
107 | extern void _dl_runtime_profile (void); | |
108 | ||
109 | if (lazy) | |
110 | { | |
111 | register Elf64_Addr gp __asm__ ("gp"); | |
112 | Elf64_Addr *reserve, doit; | |
113 | ||
114 | /* | |
115 | * Careful with the typecast here or it will try to add l-l_addr | |
116 | * pointer elements | |
117 | */ | |
118 | reserve = ((Elf64_Addr *) | |
119 | (l->l_info[DT_IA_64 (PLT_RESERVE)]->d_un.d_ptr + l->l_addr)); | |
120 | /* Identify this shared object. */ | |
121 | reserve[0] = (Elf64_Addr) l; | |
122 | ||
123 | /* This function will be called to perform the relocation. */ | |
55f41ef8 AZ |
124 | #ifdef SHARED |
125 | if (__glibc_unlikely (profile)) | |
d5efd131 MF |
126 | { |
127 | if (GLRO(dl_profile) != NULL | |
128 | && _dl_name_match_p (GLRO(dl_profile), l)) | |
129 | { | |
130 | /* This is the object we are looking for. Say that we really | |
131 | want profiling and the timers are started. */ | |
132 | GL(dl_profile_map) = l; | |
133 | } | |
c5abd7ce | 134 | doit = (Elf64_Addr) ELF_PTR_TO_FDESC (&_dl_runtime_profile)->ip; |
d5efd131 | 135 | } |
55f41ef8 AZ |
136 | else |
137 | #endif | |
138 | { | |
139 | doit = (Elf64_Addr) ELF_PTR_TO_FDESC (&_dl_runtime_resolve)->ip; | |
140 | } | |
d5efd131 MF |
141 | |
142 | reserve[1] = doit; | |
143 | reserve[2] = gp; | |
144 | } | |
145 | ||
146 | return lazy; | |
147 | } | |
148 | ||
149 | /* Names of the architecture-specific auditing callback functions. */ | |
150 | #define ARCH_LA_PLTENTER ia64_gnu_pltenter | |
151 | #define ARCH_LA_PLTEXIT ia64_gnu_pltexit | |
152 | ||
153 | /* Undo the adds out0 = 16, sp below to get at the value we want in | |
154 | __libc_stack_end. */ | |
155 | #define DL_STACK_END(cookie) \ | |
156 | ((void *) (((long) (cookie)) - 16)) | |
157 | ||
158 | /* Initial entry point code for the dynamic linker. | |
159 | The C function `_dl_start' is the real entry point; | |
160 | its return value is the user program's entry point. */ | |
161 | ||
162 | #define RTLD_START asm ( \ | |
163 | ".text\n" \ | |
164 | " .global _start#\n" \ | |
165 | " .proc _start#\n" \ | |
166 | "_start:\n" \ | |
167 | "0: { .mii\n" \ | |
168 | " .prologue\n" \ | |
169 | " .save rp, r0\n" \ | |
170 | " .body\n" \ | |
171 | " .prologue\n" \ | |
172 | " .save ar.pfs, r32\n" \ | |
173 | " alloc loc0 = ar.pfs, 0, 3, 4, 0\n" \ | |
174 | " .body\n" \ | |
175 | " mov r2 = ip\n" \ | |
176 | " addl r3 = @gprel(0b), r0\n" \ | |
177 | " ;;\n" \ | |
178 | " }\n" \ | |
179 | " { .mlx\n" \ | |
180 | " /* Calculate the GP, and save a copy in loc1. */\n" \ | |
181 | " sub gp = r2, r3\n" \ | |
182 | " movl r8 = 0x9804c0270033f\n" \ | |
183 | " ;;\n" \ | |
184 | " }\n" \ | |
185 | " { .mii\n" \ | |
186 | " mov ar.fpsr = r8\n" \ | |
187 | " sub loc1 = r2, r3\n" \ | |
188 | " /* _dl_start wants a pointer to the pointer to the arg block and\n" \ | |
189 | " the arg block starts with an integer, thus the magic 16. */\n" \ | |
190 | " adds out0 = 16, sp\n" \ | |
191 | " }\n" \ | |
192 | " { .bbb\n" \ | |
193 | " br.call.sptk.many b0 = _dl_start#\n" \ | |
194 | " ;;\n" \ | |
195 | " }\n" \ | |
196 | " .endp _start#\n" \ | |
197 | " /* FALLTHRU */\n" \ | |
198 | " .global _dl_start_user#\n" \ | |
199 | " .proc _dl_start_user#\n" \ | |
200 | "_dl_start_user:\n" \ | |
201 | " .prologue\n" \ | |
202 | " .save rp, r0\n" \ | |
203 | " .body\n" \ | |
204 | " .prologue\n" \ | |
205 | " .save ar.pfs, r32\n" \ | |
206 | " .body\n" \ | |
207 | " { .mii\n" \ | |
d5efd131 MF |
208 | " /* Save the pointer to the user entry point fptr in loc2. */\n" \ |
209 | " mov loc2 = ret0\n" \ | |
57bb1e5b | 210 | " addl r2 = @ltoff(_dl_argc), gp\n" \ |
d5efd131 MF |
211 | " ;;\n" \ |
212 | " }\n" \ | |
213 | " { .mii\n" \ | |
57bb1e5b AZ |
214 | " ld8 out1 = [r2] /* Get the _dl_argc address. */\n" \ |
215 | " addl r3 = @ltoff(_dl_argv), gp\n" \ | |
d5efd131 MF |
216 | " ;;\n" \ |
217 | " }\n" \ | |
218 | " { .mmi\n" \ | |
57bb1e5b AZ |
219 | " ld8 out2 = [r3] /* Get the _dl_argv address. */\n" \ |
220 | " ld8 out1 = [out1] /* Get the adjusted _dl_argc. */\n" \ | |
221 | " addl r2 = @gprel(_rtld_local), gp\n" \ | |
d5efd131 MF |
222 | " ;;\n" \ |
223 | " }\n" \ | |
57bb1e5b AZ |
224 | " { .mmi\n" \ |
225 | " sxt4 out3 = out1 /* envp = argv + argc + 1 */\n" \ | |
d5efd131 MF |
226 | " ;;\n" \ |
227 | " }\n" \ | |
228 | " { .mmi\n" \ | |
57bb1e5b | 229 | " adds out3 = 1, out3\n" \ |
d5efd131 MF |
230 | " ;;\n" \ |
231 | " }\n" \ | |
57bb1e5b AZ |
232 | " { .mmi\n" \ |
233 | " ld8 out2 = [out2] /* Get the adjusted _dl_argv. */\n" \ | |
234 | " shladd out3 = out3, 3, r0\n" \ | |
d5efd131 MF |
235 | " ;;\n" \ |
236 | " }\n" \ | |
237 | " { .mmb\n" \ | |
57bb1e5b AZ |
238 | " add out3 = out3, out2\n" \ |
239 | " ld8 out0 = [r2] /* Get the linkmap. */\n" \ | |
c5684fdb | 240 | " br.call.sptk.many b0 = _dl_init#\n" \ |
d5efd131 MF |
241 | " }\n" \ |
242 | " /* Pass our finalizer function to the user,\n" \ | |
243 | " and jump to the user's entry point. */\n" \ | |
244 | " { .mmi\n" \ | |
245 | " ld8 r3 = [loc2], 8\n" \ | |
246 | " mov b0 = r0\n" \ | |
247 | " }\n" \ | |
248 | " { .mmi\n" \ | |
249 | " addl ret0 = @ltoff(@fptr(_dl_fini#)), gp\n" \ | |
250 | " ;;\n" \ | |
251 | " mov b6 = r3\n" \ | |
252 | " }\n" \ | |
253 | " { .mmi\n" \ | |
254 | " ld8 ret0 = [ret0]\n" \ | |
255 | " ld8 gp = [loc2]\n" \ | |
256 | " mov ar.pfs = loc0\n" \ | |
257 | " ;;\n" \ | |
258 | " }\n" \ | |
259 | " { .mfb\n" \ | |
260 | " br.sptk.many b6\n" \ | |
261 | " ;;\n" \ | |
262 | " }\n" \ | |
263 | " .endp _dl_start_user#\n" \ | |
264 | ".previous\n"); | |
265 | ||
266 | ||
267 | #ifndef RTLD_START_SPECIAL_INIT | |
268 | #define RTLD_START_SPECIAL_INIT /* nothing */ | |
269 | #endif | |
270 | ||
271 | /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or TLS | |
272 | variable, so undefined references should not be allowed to define the | |
273 | value. | |
209826bc | 274 | ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one |
d5efd131 MF |
275 | of the main executable's symbols, as for a COPY reloc, which we don't |
276 | use. */ | |
277 | /* ??? Ignore *MSB for now. */ | |
278 | #define elf_machine_type_class(type) \ | |
279 | (((type) == R_IA64_IPLTLSB || (type) == R_IA64_DTPMOD64LSB \ | |
280 | || (type) == R_IA64_DTPREL64LSB || (type) == R_IA64_TPREL64LSB) \ | |
281 | * ELF_RTYPE_CLASS_PLT) | |
282 | ||
283 | /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ | |
284 | #define ELF_MACHINE_JMP_SLOT R_IA64_IPLTLSB | |
285 | ||
d5efd131 | 286 | /* Return the address of the entry point. */ |
daf75146 GM |
287 | #define ELF_MACHINE_START_ADDRESS(map, start) \ |
288 | ({ \ | |
289 | ElfW(Addr) addr; \ | |
290 | DL_DT_FUNCTION_ADDRESS(map, start, static, addr) \ | |
291 | addr; \ | |
292 | }) | |
d5efd131 MF |
293 | |
294 | /* Fixup a PLT entry to bounce directly to the function at VALUE. */ | |
295 | static inline struct fdesc __attribute__ ((always_inline)) | |
296 | elf_machine_fixup_plt (struct link_map *l, lookup_t t, | |
0572433b | 297 | const ElfW(Sym) *refsym, const ElfW(Sym) *sym, |
d5efd131 MF |
298 | const Elf64_Rela *reloc, |
299 | Elf64_Addr *reloc_addr, struct fdesc value) | |
300 | { | |
301 | /* l is the link_map for the caller, t is the link_map for the object | |
302 | * being called */ | |
303 | /* got has already been relocated in elf_get_dynamic_info() */ | |
304 | reloc_addr[1] = value.gp; | |
305 | /* we need a "release" here to ensure that the gp is visible before | |
306 | the code entry point is updated: */ | |
307 | ((volatile Elf64_Addr *) reloc_addr)[0] = value.ip; | |
308 | return value; | |
309 | } | |
310 | ||
311 | /* Return the final value of a plt relocation. */ | |
312 | static inline struct fdesc | |
313 | elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc, | |
314 | struct fdesc value) | |
315 | { | |
316 | /* No need to handle rel vs rela since IA64 is rela only */ | |
317 | return (struct fdesc) { value.ip + reloc->r_addend, value.gp }; | |
318 | } | |
319 | ||
320 | #endif /* !dl_machine_h */ | |
321 | ||
322 | #ifdef RESOLVE_MAP | |
323 | ||
324 | #define R_IA64_TYPE(R) ((R) & -8) | |
325 | #define R_IA64_FORMAT(R) ((R) & 7) | |
326 | ||
327 | #define R_IA64_FORMAT_32MSB 4 | |
328 | #define R_IA64_FORMAT_32LSB 5 | |
329 | #define R_IA64_FORMAT_64MSB 6 | |
330 | #define R_IA64_FORMAT_64LSB 7 | |
331 | ||
332 | ||
333 | /* Perform the relocation specified by RELOC and SYM (which is fully | |
334 | resolved). MAP is the object containing the reloc. */ | |
490e6c62 | 335 | static inline void |
d5efd131 | 336 | __attribute ((always_inline)) |
490e6c62 | 337 | elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[], |
d5efd131 MF |
338 | const Elf64_Rela *reloc, |
339 | const Elf64_Sym *sym, | |
340 | const struct r_found_version *version, | |
341 | void *const reloc_addr_arg, | |
342 | int skip_ifunc) | |
343 | { | |
344 | Elf64_Addr *const reloc_addr = reloc_addr_arg; | |
345 | const unsigned long int r_type = ELF64_R_TYPE (reloc->r_info); | |
346 | Elf64_Addr value; | |
347 | ||
d5efd131 MF |
348 | /* We cannot use a switch here because we cannot locate the switch |
349 | jump table until we've self-relocated. */ | |
350 | ||
3ee318c9 | 351 | #if !defined RTLD_BOOTSTRAP |
d5efd131 MF |
352 | if (__builtin_expect (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB), |
353 | 0)) | |
354 | { | |
355 | assert (ELF64_R_TYPE (reloc->r_info) == R_IA64_REL64LSB); | |
3ee318c9 | 356 | value = *reloc_addr + map->l_addr; |
d5efd131 MF |
357 | } |
358 | else | |
359 | #endif | |
360 | if (__builtin_expect (r_type == R_IA64_NONE, 0)) | |
361 | return; | |
362 | else | |
363 | { | |
490e6c62 FS |
364 | struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, |
365 | r_type); | |
d5efd131 MF |
366 | |
367 | /* RESOLVE_MAP() will return NULL if it fail to locate the symbol. */ | |
490e6c62 | 368 | if (sym_map != NULL) |
d5efd131 | 369 | { |
10a446dd | 370 | value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend; |
d5efd131 MF |
371 | |
372 | if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB)) | |
373 | ;/* No adjustment. */ | |
374 | else if (r_type == R_IA64_IPLTLSB) | |
375 | { | |
0572433b | 376 | elf_machine_fixup_plt (NULL, NULL, NULL, NULL, reloc, reloc_addr, |
d5efd131 MF |
377 | DL_FIXUP_MAKE_VALUE (sym_map, value)); |
378 | return; | |
379 | } | |
380 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_FPTR64LSB)) | |
381 | value = _dl_make_fptr (sym_map, sym, value); | |
382 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB)) | |
383 | value -= (Elf64_Addr) reloc_addr & -16; | |
384 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DTPMOD64LSB)) | |
385 | #ifdef RTLD_BOOTSTRAP | |
386 | /* During startup the dynamic linker is always index 1. */ | |
387 | value = 1; | |
388 | #else | |
389 | /* Get the information from the link map returned by the | |
390 | resolv function. */ | |
391 | value = sym_map->l_tls_modid; | |
392 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DTPREL64LSB)) | |
393 | value -= sym_map->l_addr; | |
394 | #endif | |
395 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_TPREL64LSB)) | |
396 | { | |
397 | #ifndef RTLD_BOOTSTRAP | |
398 | CHECK_STATIC_TLS (map, sym_map); | |
399 | #endif | |
400 | value += sym_map->l_tls_offset - sym_map->l_addr; | |
401 | } | |
402 | else | |
403 | _dl_reloc_bad_type (map, r_type, 0); | |
404 | } | |
405 | else | |
406 | value = 0; | |
407 | } | |
408 | ||
409 | /* ??? Ignore MSB and Instruction format for now. */ | |
410 | if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB) | |
411 | *reloc_addr = value; | |
412 | else if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_32LSB) | |
413 | *(int *) reloc_addr = value; | |
414 | else if (r_type == R_IA64_IPLTLSB) | |
415 | { | |
416 | reloc_addr[0] = 0; | |
417 | reloc_addr[1] = 0; | |
418 | } | |
419 | else | |
420 | _dl_reloc_bad_type (map, r_type, 0); | |
421 | } | |
422 | ||
423 | /* Let do-rel.h know that on IA-64 if l_addr is 0, all RELATIVE relocs | |
424 | can be skipped. */ | |
425 | #define ELF_MACHINE_REL_RELATIVE 1 | |
426 | ||
490e6c62 | 427 | static inline void |
d5efd131 MF |
428 | __attribute ((always_inline)) |
429 | elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc, | |
430 | void *const reloc_addr_arg) | |
431 | { | |
432 | Elf64_Addr *const reloc_addr = reloc_addr_arg; | |
433 | /* ??? Ignore MSB and Instruction format for now. */ | |
434 | assert (ELF64_R_TYPE (reloc->r_info) == R_IA64_REL64LSB); | |
435 | ||
436 | *reloc_addr += l_addr; | |
437 | } | |
438 | ||
439 | /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt. */ | |
490e6c62 | 440 | static inline void |
d5efd131 | 441 | __attribute ((always_inline)) |
490e6c62 | 442 | elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[], |
d5efd131 MF |
443 | Elf64_Addr l_addr, const Elf64_Rela *reloc, |
444 | int skip_ifunc) | |
445 | { | |
446 | Elf64_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset); | |
447 | const unsigned long int r_type = ELF64_R_TYPE (reloc->r_info); | |
448 | ||
449 | if (r_type == R_IA64_IPLTLSB) | |
450 | { | |
451 | reloc_addr[0] += l_addr; | |
452 | reloc_addr[1] += l_addr; | |
453 | } | |
454 | else if (r_type == R_IA64_NONE) | |
455 | return; | |
456 | else | |
457 | _dl_reloc_bad_type (map, r_type, 1); | |
458 | } | |
459 | ||
460 | #endif /* RESOLVE_MAP */ |