Warning: This file is not a C or C++ file. It does not have highlighting.
1 | /* Machine-dependent ELF dynamic relocation inline functions. IA-64 version. |
---|---|
2 | Copyright (C) 1995-2022 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 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 |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
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> |
30 | #include <dl-static-tls.h> |
31 | #include <dl-machine-rel.h> |
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 | |
49 | #define ELF_MACHINE_BEFORE_RTLD_RELOC(map, dynamic_info) \ |
50 | __ia64_init_bootstrap_fdesc_table (map); |
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)) |
103 | elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[], |
104 | int lazy, int profile) |
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. */ |
124 | if (!profile) |
125 | doit = (Elf64_Addr) ELF_PTR_TO_FDESC (&_dl_runtime_resolve)->ip; |
126 | else |
127 | { |
128 | if (GLRO(dl_profile) != NULL |
129 | && _dl_name_match_p (GLRO(dl_profile), l)) |
130 | { |
131 | /* This is the object we are looking for. Say that we really |
132 | want profiling and the timers are started. */ |
133 | GL(dl_profile_map) = l; |
134 | } |
135 | doit = (Elf64_Addr) ELF_PTR_TO_FDESC (&_dl_runtime_profile)->ip; |
136 | } |
137 | |
138 | reserve[1] = doit; |
139 | reserve[2] = gp; |
140 | } |
141 | |
142 | return lazy; |
143 | } |
144 | |
145 | /* Names of the architecture-specific auditing callback functions. */ |
146 | #define ARCH_LA_PLTENTER ia64_gnu_pltenter |
147 | #define ARCH_LA_PLTEXIT ia64_gnu_pltexit |
148 | |
149 | /* Undo the adds out0 = 16, sp below to get at the value we want in |
150 | __libc_stack_end. */ |
151 | #define DL_STACK_END(cookie) \ |
152 | ((void *) (((long) (cookie)) - 16)) |
153 | |
154 | /* Initial entry point code for the dynamic linker. |
155 | The C function `_dl_start' is the real entry point; |
156 | its return value is the user program's entry point. */ |
157 | |
158 | #define RTLD_START asm ( \ |
159 | ".text\n" \ |
160 | " .global _start#\n" \ |
161 | " .proc _start#\n" \ |
162 | "_start:\n" \ |
163 | "0: { .mii\n" \ |
164 | " .prologue\n" \ |
165 | " .save rp, r0\n" \ |
166 | " .body\n" \ |
167 | " .prologue\n" \ |
168 | " .save ar.pfs, r32\n" \ |
169 | " alloc loc0 = ar.pfs, 0, 3, 4, 0\n" \ |
170 | " .body\n" \ |
171 | " mov r2 = ip\n" \ |
172 | " addl r3 = @gprel(0b), r0\n" \ |
173 | " ;;\n" \ |
174 | " }\n" \ |
175 | " { .mlx\n" \ |
176 | " /* Calculate the GP, and save a copy in loc1. */\n" \ |
177 | " sub gp = r2, r3\n" \ |
178 | " movl r8 = 0x9804c0270033f\n" \ |
179 | " ;;\n" \ |
180 | " }\n" \ |
181 | " { .mii\n" \ |
182 | " mov ar.fpsr = r8\n" \ |
183 | " sub loc1 = r2, r3\n" \ |
184 | " /* _dl_start wants a pointer to the pointer to the arg block and\n" \ |
185 | " the arg block starts with an integer, thus the magic 16. */\n" \ |
186 | " adds out0 = 16, sp\n" \ |
187 | " }\n" \ |
188 | " { .bbb\n" \ |
189 | " br.call.sptk.many b0 = _dl_start#\n" \ |
190 | " ;;\n" \ |
191 | " }\n" \ |
192 | " .endp _start#\n" \ |
193 | " /* FALLTHRU */\n" \ |
194 | " .global _dl_start_user#\n" \ |
195 | " .proc _dl_start_user#\n" \ |
196 | "_dl_start_user:\n" \ |
197 | " .prologue\n" \ |
198 | " .save rp, r0\n" \ |
199 | " .body\n" \ |
200 | " .prologue\n" \ |
201 | " .save ar.pfs, r32\n" \ |
202 | " .body\n" \ |
203 | " { .mii\n" \ |
204 | " addl r3 = @gprel(_dl_skip_args), gp\n" \ |
205 | " adds r11 = 24, sp /* Load the address of argv. */\n" \ |
206 | " /* Save the pointer to the user entry point fptr in loc2. */\n" \ |
207 | " mov loc2 = ret0\n" \ |
208 | " ;;\n" \ |
209 | " }\n" \ |
210 | " { .mii\n" \ |
211 | " ld4 r3 = [r3]\n" \ |
212 | " adds r10 = 16, sp /* Load the address of argc. */\n" \ |
213 | " mov out2 = r11\n" \ |
214 | " ;;\n" \ |
215 | " /* See if we were run as a command with the executable file\n" \ |
216 | " name as an extra leading argument. If so, adjust the argv\n" \ |
217 | " pointer to skip _dl_skip_args words.\n" \ |
218 | " Note that _dl_skip_args is an integer, not a long - Jes\n" \ |
219 | "\n" \ |
220 | " The stack pointer has to be 16 byte aligned. We cannot simply\n" \ |
221 | " addjust the stack pointer. We have to move the whole argv and\n" \ |
222 | " envp and adjust _dl_argv by _dl_skip_args. H.J. */\n" \ |
223 | " }\n" \ |
224 | " { .mib\n" \ |
225 | " ld8 out1 = [r10] /* is argc actually stored as a long\n" \ |
226 | " or as an int? */\n" \ |
227 | " addl r2 = @ltoff(_dl_argv), gp\n" \ |
228 | " ;;\n" \ |
229 | " }\n" \ |
230 | " { .mmi\n" \ |
231 | " ld8 r2 = [r2] /* Get the address of _dl_argv. */\n" \ |
232 | " sub out1 = out1, r3 /* Get the new argc. */\n" \ |
233 | " shladd r3 = r3, 3, r0\n" \ |
234 | " ;;\n" \ |
235 | " }\n" \ |
236 | " {\n" \ |
237 | " .mib\n" \ |
238 | " ld8 r17 = [r2] /* Get _dl_argv. */\n" \ |
239 | " add r15 = r11, r3 /* The address of the argv we move */\n" \ |
240 | " ;;\n" \ |
241 | " }\n" \ |
242 | " /* ??? Could probably merge these two loops into 3 bundles.\n" \ |
243 | " using predication to control which set of copies we're on. */\n" \ |
244 | "1: /* Copy argv. */\n" \ |
245 | " { .mfi\n" \ |
246 | " ld8 r16 = [r15], 8 /* Load the value in the old argv. */\n" \ |
247 | " ;;\n" \ |
248 | " }\n" \ |
249 | " { .mib\n" \ |
250 | " st8 [r11] = r16, 8 /* Store it in the new argv. */\n" \ |
251 | " cmp.ne p6, p7 = 0, r16\n" \ |
252 | "(p6) br.cond.dptk.few 1b\n" \ |
253 | " ;;\n" \ |
254 | " }\n" \ |
255 | " { .mmi\n" \ |
256 | " mov out3 = r11\n" \ |
257 | " sub r17 = r17, r3 /* Substract _dl_skip_args. */\n" \ |
258 | " addl out0 = @gprel(_rtld_local), gp\n" \ |
259 | " }\n" \ |
260 | "1: /* Copy env. */\n" \ |
261 | " { .mfi\n" \ |
262 | " ld8 r16 = [r15], 8 /* Load the value in the old env. */\n" \ |
263 | " ;;\n" \ |
264 | " }\n" \ |
265 | " { .mib\n" \ |
266 | " st8 [r11] = r16, 8 /* Store it in the new env. */\n" \ |
267 | " cmp.ne p6, p7 = 0, r16\n" \ |
268 | "(p6) br.cond.dptk.few 1b\n" \ |
269 | " ;;\n" \ |
270 | " }\n" \ |
271 | " { .mmb\n" \ |
272 | " st8 [r10] = out1 /* Record the new argc. */\n" \ |
273 | " ld8 out0 = [out0] /* get the linkmap */\n" \ |
274 | " }\n" \ |
275 | " { .mmb\n" \ |
276 | " st8 [r2] = r17 /* Load the new _dl_argv. */\n" \ |
277 | " br.call.sptk.many b0 = _dl_init#\n" \ |
278 | " ;;\n" \ |
279 | " }\n" \ |
280 | " /* Pass our finalizer function to the user,\n" \ |
281 | " and jump to the user's entry point. */\n" \ |
282 | " { .mmi\n" \ |
283 | " ld8 r3 = [loc2], 8\n" \ |
284 | " mov b0 = r0\n" \ |
285 | " }\n" \ |
286 | " { .mmi\n" \ |
287 | " addl ret0 = @ltoff(@fptr(_dl_fini#)), gp\n" \ |
288 | " ;;\n" \ |
289 | " mov b6 = r3\n" \ |
290 | " }\n" \ |
291 | " { .mmi\n" \ |
292 | " ld8 ret0 = [ret0]\n" \ |
293 | " ld8 gp = [loc2]\n" \ |
294 | " mov ar.pfs = loc0\n" \ |
295 | " ;;\n" \ |
296 | " }\n" \ |
297 | " { .mfb\n" \ |
298 | " br.sptk.many b6\n" \ |
299 | " ;;\n" \ |
300 | " }\n" \ |
301 | " .endp _dl_start_user#\n" \ |
302 | ".previous\n"); |
303 | |
304 | |
305 | #ifndef RTLD_START_SPECIAL_INIT |
306 | #define RTLD_START_SPECIAL_INIT /* nothing */ |
307 | #endif |
308 | |
309 | /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or TLS |
310 | variable, so undefined references should not be allowed to define the |
311 | value. |
312 | ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one |
313 | of the main executable's symbols, as for a COPY reloc, which we don't |
314 | use. */ |
315 | /* ??? Ignore *MSB for now. */ |
316 | #define elf_machine_type_class(type) \ |
317 | (((type) == R_IA64_IPLTLSB || (type) == R_IA64_DTPMOD64LSB \ |
318 | || (type) == R_IA64_DTPREL64LSB || (type) == R_IA64_TPREL64LSB) \ |
319 | * ELF_RTYPE_CLASS_PLT) |
320 | |
321 | /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ |
322 | #define ELF_MACHINE_JMP_SLOT R_IA64_IPLTLSB |
323 | |
324 | /* Return the address of the entry point. */ |
325 | #define ELF_MACHINE_START_ADDRESS(map, start) \ |
326 | ({ \ |
327 | ElfW(Addr) addr; \ |
328 | DL_DT_FUNCTION_ADDRESS(map, start, static, addr) \ |
329 | addr; \ |
330 | }) |
331 | |
332 | /* Fixup a PLT entry to bounce directly to the function at VALUE. */ |
333 | static inline struct fdesc __attribute__ ((always_inline)) |
334 | elf_machine_fixup_plt (struct link_map *l, lookup_t t, |
335 | const ElfW(Sym) *refsym, const ElfW(Sym) *sym, |
336 | const Elf64_Rela *reloc, |
337 | Elf64_Addr *reloc_addr, struct fdesc value) |
338 | { |
339 | /* l is the link_map for the caller, t is the link_map for the object |
340 | * being called */ |
341 | /* got has already been relocated in elf_get_dynamic_info() */ |
342 | reloc_addr[1] = value.gp; |
343 | /* we need a "release" here to ensure that the gp is visible before |
344 | the code entry point is updated: */ |
345 | ((volatile Elf64_Addr *) reloc_addr)[0] = value.ip; |
346 | return value; |
347 | } |
348 | |
349 | /* Return the final value of a plt relocation. */ |
350 | static inline struct fdesc |
351 | elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc, |
352 | struct fdesc value) |
353 | { |
354 | /* No need to handle rel vs rela since IA64 is rela only */ |
355 | return (struct fdesc) { value.ip + reloc->r_addend, value.gp }; |
356 | } |
357 | |
358 | #endif /* !dl_machine_h */ |
359 | |
360 | #ifdef RESOLVE_MAP |
361 | |
362 | #define R_IA64_TYPE(R) ((R) & -8) |
363 | #define R_IA64_FORMAT(R) ((R) & 7) |
364 | |
365 | #define R_IA64_FORMAT_32MSB 4 |
366 | #define R_IA64_FORMAT_32LSB 5 |
367 | #define R_IA64_FORMAT_64MSB 6 |
368 | #define R_IA64_FORMAT_64LSB 7 |
369 | |
370 | |
371 | /* Perform the relocation specified by RELOC and SYM (which is fully |
372 | resolved). MAP is the object containing the reloc. */ |
373 | static inline void |
374 | __attribute ((always_inline)) |
375 | elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[], |
376 | const Elf64_Rela *reloc, |
377 | const Elf64_Sym *sym, |
378 | const struct r_found_version *version, |
379 | void *const reloc_addr_arg, |
380 | int skip_ifunc) |
381 | { |
382 | Elf64_Addr *const reloc_addr = reloc_addr_arg; |
383 | const unsigned long int r_type = ELF64_R_TYPE (reloc->r_info); |
384 | Elf64_Addr value; |
385 | |
386 | #if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC && !defined SHARED |
387 | /* This is defined in rtld.c, but nowhere in the static libc.a; make the |
388 | reference weak so static programs can still link. This declaration |
389 | cannot be done when compiling rtld.c (i.e. #ifdef RTLD_BOOTSTRAP) |
390 | because rtld.c contains the common defn for _dl_rtld_map, which is |
391 | incompatible with a weak decl in the same file. */ |
392 | weak_extern (_dl_rtld_map); |
393 | #endif |
394 | |
395 | /* We cannot use a switch here because we cannot locate the switch |
396 | jump table until we've self-relocated. */ |
397 | |
398 | #if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC |
399 | if (__builtin_expect (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_REL64LSB), |
400 | 0)) |
401 | { |
402 | assert (ELF64_R_TYPE (reloc->r_info) == R_IA64_REL64LSB); |
403 | value = *reloc_addr; |
404 | # if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC |
405 | /* Already done in dynamic linker. */ |
406 | if (map != &GL(dl_rtld_map)) |
407 | # endif |
408 | value += map->l_addr; |
409 | } |
410 | else |
411 | #endif |
412 | if (__builtin_expect (r_type == R_IA64_NONE, 0)) |
413 | return; |
414 | else |
415 | { |
416 | struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, |
417 | r_type); |
418 | |
419 | /* RESOLVE_MAP() will return NULL if it fail to locate the symbol. */ |
420 | if (sym_map != NULL) |
421 | { |
422 | value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend; |
423 | |
424 | if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DIR64LSB)) |
425 | ;/* No adjustment. */ |
426 | else if (r_type == R_IA64_IPLTLSB) |
427 | { |
428 | elf_machine_fixup_plt (NULL, NULL, NULL, NULL, reloc, reloc_addr, |
429 | DL_FIXUP_MAKE_VALUE (sym_map, value)); |
430 | return; |
431 | } |
432 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_FPTR64LSB)) |
433 | value = _dl_make_fptr (sym_map, sym, value); |
434 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_PCREL64LSB)) |
435 | value -= (Elf64_Addr) reloc_addr & -16; |
436 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DTPMOD64LSB)) |
437 | #ifdef RTLD_BOOTSTRAP |
438 | /* During startup the dynamic linker is always index 1. */ |
439 | value = 1; |
440 | #else |
441 | /* Get the information from the link map returned by the |
442 | resolv function. */ |
443 | value = sym_map->l_tls_modid; |
444 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_DTPREL64LSB)) |
445 | value -= sym_map->l_addr; |
446 | #endif |
447 | else if (R_IA64_TYPE (r_type) == R_IA64_TYPE (R_IA64_TPREL64LSB)) |
448 | { |
449 | #ifndef RTLD_BOOTSTRAP |
450 | CHECK_STATIC_TLS (map, sym_map); |
451 | #endif |
452 | value += sym_map->l_tls_offset - sym_map->l_addr; |
453 | } |
454 | else |
455 | _dl_reloc_bad_type (map, r_type, 0); |
456 | } |
457 | else |
458 | value = 0; |
459 | } |
460 | |
461 | /* ??? Ignore MSB and Instruction format for now. */ |
462 | if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_64LSB) |
463 | *reloc_addr = value; |
464 | else if (R_IA64_FORMAT (r_type) == R_IA64_FORMAT_32LSB) |
465 | *(int *) reloc_addr = value; |
466 | else if (r_type == R_IA64_IPLTLSB) |
467 | { |
468 | reloc_addr[0] = 0; |
469 | reloc_addr[1] = 0; |
470 | } |
471 | else |
472 | _dl_reloc_bad_type (map, r_type, 0); |
473 | } |
474 | |
475 | /* Let do-rel.h know that on IA-64 if l_addr is 0, all RELATIVE relocs |
476 | can be skipped. */ |
477 | #define ELF_MACHINE_REL_RELATIVE 1 |
478 | |
479 | static inline void |
480 | __attribute ((always_inline)) |
481 | elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc, |
482 | void *const reloc_addr_arg) |
483 | { |
484 | Elf64_Addr *const reloc_addr = reloc_addr_arg; |
485 | /* ??? Ignore MSB and Instruction format for now. */ |
486 | assert (ELF64_R_TYPE (reloc->r_info) == R_IA64_REL64LSB); |
487 | |
488 | *reloc_addr += l_addr; |
489 | } |
490 | |
491 | /* Perform a RELATIVE reloc on the .got entry that transfers to the .plt. */ |
492 | static inline void |
493 | __attribute ((always_inline)) |
494 | elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[], |
495 | Elf64_Addr l_addr, const Elf64_Rela *reloc, |
496 | int skip_ifunc) |
497 | { |
498 | Elf64_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset); |
499 | const unsigned long int r_type = ELF64_R_TYPE (reloc->r_info); |
500 | |
501 | if (r_type == R_IA64_IPLTLSB) |
502 | { |
503 | reloc_addr[0] += l_addr; |
504 | reloc_addr[1] += l_addr; |
505 | } |
506 | else if (r_type == R_IA64_NONE) |
507 | return; |
508 | else |
509 | _dl_reloc_bad_type (map, r_type, 1); |
510 | } |
511 | |
512 | #endif /* RESOLVE_MAP */ |
513 |
Warning: This file is not a C or C++ file. It does not have highlighting.