]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/aarch64/dl-machine.h
PowerPC64 ELFv2 PPC64_OPT_LOCALENTRY
[thirdparty/glibc.git] / sysdeps / aarch64 / dl-machine.h
1 /* Copyright (C) 1995-2017 Free Software Foundation, Inc.
2
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 License as
7 published by the Free Software Foundation; either version 2.1 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 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 <http://www.gnu.org/licenses/>. */
18
19 #ifndef dl_machine_h
20 #define dl_machine_h
21
22 #define ELF_MACHINE_NAME "aarch64"
23
24 #include <sysdep.h>
25 #include <tls.h>
26 #include <dl-tlsdesc.h>
27 #include <dl-irel.h>
28 #include <cpu-features.c>
29
30 /* Return nonzero iff ELF header is compatible with the running host. */
31 static inline int __attribute__ ((unused))
32 elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
33 {
34 return ehdr->e_machine == EM_AARCH64;
35 }
36
37 /* Return the link-time address of _DYNAMIC. Conveniently, this is the
38 first element of the GOT. */
39 static inline ElfW(Addr) __attribute__ ((unused))
40 elf_machine_dynamic (void)
41 {
42 extern const ElfW(Addr) _GLOBAL_OFFSET_TABLE_[] attribute_hidden;
43 return _GLOBAL_OFFSET_TABLE_[0];
44 }
45
46 /* Return the run-time load address of the shared object. */
47
48 static inline ElfW(Addr) __attribute__ ((unused))
49 elf_machine_load_address (void)
50 {
51 /* To figure out the load address we use the definition that for any symbol:
52 dynamic_addr(symbol) = static_addr(symbol) + load_addr
53
54 The choice of symbol is arbitrary. The static address we obtain
55 by constructing a non GOT reference to the symbol, the dynamic
56 address of the symbol we compute using adrp/add to compute the
57 symbol's address relative to the PC.
58 This depends on 32/16bit relocations being resolved at link time
59 and that the static address fits in the 32/16 bits. */
60
61 ElfW(Addr) static_addr;
62 ElfW(Addr) dynamic_addr;
63
64 asm (" \n"
65 " adrp %1, _dl_start; \n"
66 #ifdef __LP64__
67 " add %1, %1, #:lo12:_dl_start \n"
68 #else
69 " add %w1, %w1, #:lo12:_dl_start \n"
70 #endif
71 " ldr %w0, 1f \n"
72 " b 2f \n"
73 "1: \n"
74 #ifdef __LP64__
75 " .word _dl_start \n"
76 #else
77 # ifdef __AARCH64EB__
78 " .short 0 \n"
79 # endif
80 " .short _dl_start \n"
81 # ifndef __AARCH64EB__
82 " .short 0 \n"
83 # endif
84 #endif
85 "2: \n"
86 : "=r" (static_addr), "=r" (dynamic_addr));
87 return dynamic_addr - static_addr;
88 }
89
90 /* Set up the loaded object described by L so its unrelocated PLT
91 entries will jump to the on-demand fixup code in dl-runtime.c. */
92
93 static inline int __attribute__ ((unused))
94 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
95 {
96 if (l->l_info[DT_JMPREL] && lazy)
97 {
98 ElfW(Addr) *got;
99 extern void _dl_runtime_resolve (ElfW(Word));
100 extern void _dl_runtime_profile (ElfW(Word));
101
102 got = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
103 if (got[1])
104 {
105 l->l_mach.plt = got[1] + l->l_addr;
106 }
107 got[1] = (ElfW(Addr)) l;
108
109 /* The got[2] entry contains the address of a function which gets
110 called to get the address of a so far unresolved function and
111 jump to it. The profiling extension of the dynamic linker allows
112 to intercept the calls to collect information. In this case we
113 don't store the address in the GOT so that all future calls also
114 end in this function. */
115 if ( profile)
116 {
117 got[2] = (ElfW(Addr)) &_dl_runtime_profile;
118
119 if (GLRO(dl_profile) != NULL
120 && _dl_name_match_p (GLRO(dl_profile), l))
121 /* Say that we really want profiling and the timers are
122 started. */
123 GL(dl_profile_map) = l;
124 }
125 else
126 {
127 /* This function will get called to fix up the GOT entry
128 indicated by the offset on the stack, and then jump to
129 the resolved address. */
130 got[2] = (ElfW(Addr)) &_dl_runtime_resolve;
131 }
132 }
133
134 if (l->l_info[ADDRIDX (DT_TLSDESC_GOT)] && lazy)
135 *(ElfW(Addr)*)(D_PTR (l, l_info[ADDRIDX (DT_TLSDESC_GOT)]) + l->l_addr)
136 = (ElfW(Addr)) &_dl_tlsdesc_resolve_rela;
137
138 return lazy;
139 }
140
141 /* Initial entry point for the dynamic linker. The C function
142 _dl_start is the real entry point, its return value is the user
143 program's entry point */
144 #ifdef __LP64__
145 # define RTLD_START RTLD_START_1 ("x", "3", "sp")
146 #else
147 # define RTLD_START RTLD_START_1 ("w", "2", "wsp")
148 #endif
149
150
151 #define RTLD_START_1(PTR, PTR_SIZE_LOG, PTR_SP) asm ("\
152 .text \n\
153 .globl _start \n\
154 .type _start, %function \n\
155 .globl _dl_start_user \n\
156 .type _dl_start_user, %function \n\
157 _start: \n\
158 mov " PTR "0, " PTR_SP " \n\
159 bl _dl_start \n\
160 // returns user entry point in x0 \n\
161 mov x21, x0 \n\
162 _dl_start_user: \n\
163 // get the original arg count \n\
164 ldr " PTR "1, [sp] \n\
165 // get the argv address \n\
166 add " PTR "2, " PTR_SP ", #(1<<" PTR_SIZE_LOG ") \n\
167 // get _dl_skip_args to see if we were \n\
168 // invoked as an executable \n\
169 adrp x4, _dl_skip_args \n\
170 ldr w4, [x4, #:lo12:_dl_skip_args] \n\
171 // do we need to adjust argc/argv \n\
172 cmp w4, 0 \n\
173 beq .L_done_stack_adjust \n\
174 // subtract _dl_skip_args from original arg count \n\
175 sub " PTR "1, " PTR "1, " PTR "4 \n\
176 // store adjusted argc back to stack \n\
177 str " PTR "1, [sp] \n\
178 // find the first unskipped argument \n\
179 mov " PTR "3, " PTR "2 \n\
180 add " PTR "4, " PTR "2, " PTR "4, lsl #" PTR_SIZE_LOG " \n\
181 // shuffle argv down \n\
182 1: ldr " PTR "5, [x4], #(1<<" PTR_SIZE_LOG ") \n\
183 str " PTR "5, [x3], #(1<<" PTR_SIZE_LOG ") \n\
184 cmp " PTR "5, #0 \n\
185 bne 1b \n\
186 // shuffle envp down \n\
187 1: ldr " PTR "5, [x4], #(1<<" PTR_SIZE_LOG ") \n\
188 str " PTR "5, [x3], #(1<<" PTR_SIZE_LOG ") \n\
189 cmp " PTR "5, #0 \n\
190 bne 1b \n\
191 // shuffle auxv down \n\
192 1: ldp " PTR "0, " PTR "5, [x4, #(2<<" PTR_SIZE_LOG ")]! \n\
193 stp " PTR "0, " PTR "5, [x3], #(2<<" PTR_SIZE_LOG ") \n\
194 cmp " PTR "0, #0 \n\
195 bne 1b \n\
196 // Update _dl_argv \n\
197 adrp x3, _dl_argv \n\
198 str " PTR "2, [x3, #:lo12:_dl_argv] \n\
199 .L_done_stack_adjust: \n\
200 // compute envp \n\
201 add " PTR "3, " PTR "2, " PTR "1, lsl #" PTR_SIZE_LOG " \n\
202 add " PTR "3, " PTR "3, #(1<<" PTR_SIZE_LOG ") \n\
203 adrp x16, _rtld_local \n\
204 add " PTR "16, " PTR "16, #:lo12:_rtld_local \n\
205 ldr " PTR "0, [x16] \n\
206 bl _dl_init \n\
207 // load the finalizer function \n\
208 adrp x0, _dl_fini \n\
209 add " PTR "0, " PTR "0, #:lo12:_dl_fini \n\
210 // jump to the user_s entry point \n\
211 br x21 \n\
212 ");
213
214 #define elf_machine_type_class(type) \
215 ((((type) == AARCH64_R(JUMP_SLOT) \
216 || (type) == AARCH64_R(TLS_DTPMOD) \
217 || (type) == AARCH64_R(TLS_DTPREL) \
218 || (type) == AARCH64_R(TLS_TPREL) \
219 || (type) == AARCH64_R(TLSDESC)) * ELF_RTYPE_CLASS_PLT) \
220 | (((type) == AARCH64_R(COPY)) * ELF_RTYPE_CLASS_COPY) \
221 | (((type) == AARCH64_R(GLOB_DAT)) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA))
222
223 #define ELF_MACHINE_JMP_SLOT AARCH64_R(JUMP_SLOT)
224
225 /* AArch64 uses RELA not REL */
226 #define ELF_MACHINE_NO_REL 1
227 #define ELF_MACHINE_NO_RELA 0
228
229 #define DL_PLATFORM_INIT dl_platform_init ()
230
231 static inline void __attribute__ ((unused))
232 dl_platform_init (void)
233 {
234 if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
235 /* Avoid an empty string which would disturb us. */
236 GLRO(dl_platform) = NULL;
237
238 #ifdef SHARED
239 /* init_cpu_features has been called early from __libc_start_main in
240 static executable. */
241 init_cpu_features (&GLRO(dl_aarch64_cpu_features));
242 #endif
243 }
244
245
246 static inline ElfW(Addr)
247 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
248 const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
249 const ElfW(Rela) *reloc,
250 ElfW(Addr) *reloc_addr,
251 ElfW(Addr) value)
252 {
253 return *reloc_addr = value;
254 }
255
256 /* Return the final value of a plt relocation. */
257 static inline ElfW(Addr)
258 elf_machine_plt_value (struct link_map *map,
259 const ElfW(Rela) *reloc,
260 ElfW(Addr) value)
261 {
262 return value;
263 }
264
265 #endif
266
267 /* Names of the architecture-specific auditing callback functions. */
268 #define ARCH_LA_PLTENTER aarch64_gnu_pltenter
269 #define ARCH_LA_PLTEXIT aarch64_gnu_pltexit
270
271 #ifdef RESOLVE_MAP
272
273 auto inline void
274 __attribute__ ((always_inline))
275 elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
276 const ElfW(Sym) *sym, const struct r_found_version *version,
277 void *const reloc_addr_arg, int skip_ifunc)
278 {
279 ElfW(Addr) *const reloc_addr = reloc_addr_arg;
280 const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
281
282 if (__builtin_expect (r_type == AARCH64_R(RELATIVE), 0))
283 *reloc_addr = map->l_addr + reloc->r_addend;
284 else if (__builtin_expect (r_type == R_AARCH64_NONE, 0))
285 return;
286 else
287 {
288 const ElfW(Sym) *const refsym = sym;
289 struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
290 ElfW(Addr) value = sym_map == NULL ? 0 : sym_map->l_addr + sym->st_value;
291
292 if (sym != NULL
293 && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
294 && __glibc_likely (sym->st_shndx != SHN_UNDEF)
295 && __glibc_likely (!skip_ifunc))
296 value = elf_ifunc_invoke (value);
297
298 switch (r_type)
299 {
300 case AARCH64_R(COPY):
301 if (sym == NULL)
302 break;
303
304 if (sym->st_size > refsym->st_size
305 || (GLRO(dl_verbose) && sym->st_size < refsym->st_size))
306 {
307 const char *strtab;
308
309 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
310 _dl_error_printf ("\
311 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
312 RTLD_PROGNAME, strtab + refsym->st_name);
313 }
314 memcpy (reloc_addr_arg, (void *) value,
315 MIN (sym->st_size, refsym->st_size));
316 break;
317
318 case AARCH64_R(RELATIVE):
319 case AARCH64_R(GLOB_DAT):
320 case AARCH64_R(JUMP_SLOT):
321 case AARCH64_R(ABS32):
322 #ifdef __LP64__
323 case AARCH64_R(ABS64):
324 #endif
325 *reloc_addr = value + reloc->r_addend;
326 break;
327
328 case AARCH64_R(TLSDESC):
329 {
330 struct tlsdesc volatile *td =
331 (struct tlsdesc volatile *)reloc_addr;
332 #ifndef RTLD_BOOTSTRAP
333 if (! sym)
334 {
335 td->arg = (void*)reloc->r_addend;
336 td->entry = _dl_tlsdesc_undefweak;
337 }
338 else
339 #endif
340 {
341 #ifndef RTLD_BOOTSTRAP
342 # ifndef SHARED
343 CHECK_STATIC_TLS (map, sym_map);
344 # else
345 if (!TRY_STATIC_TLS (map, sym_map))
346 {
347 td->arg = _dl_make_tlsdesc_dynamic
348 (sym_map, sym->st_value + reloc->r_addend);
349 td->entry = _dl_tlsdesc_dynamic;
350 }
351 else
352 # endif
353 #endif
354 {
355 td->arg = (void*)(sym->st_value + sym_map->l_tls_offset
356 + reloc->r_addend);
357 td->entry = _dl_tlsdesc_return;
358 }
359 }
360 break;
361 }
362
363 case AARCH64_R(TLS_DTPMOD):
364 #ifdef RTLD_BOOTSTRAP
365 *reloc_addr = 1;
366 #else
367 if (sym_map != NULL)
368 {
369 *reloc_addr = sym_map->l_tls_modid;
370 }
371 #endif
372 break;
373
374 case AARCH64_R(TLS_DTPREL):
375 if (sym)
376 *reloc_addr = sym->st_value + reloc->r_addend;
377 break;
378
379 case AARCH64_R(TLS_TPREL):
380 if (sym)
381 {
382 CHECK_STATIC_TLS (map, sym_map);
383 *reloc_addr =
384 sym->st_value + reloc->r_addend + sym_map->l_tls_offset;
385 }
386 break;
387
388 case AARCH64_R(IRELATIVE):
389 value = map->l_addr + reloc->r_addend;
390 value = elf_ifunc_invoke (value);
391 *reloc_addr = value;
392 break;
393
394 default:
395 _dl_reloc_bad_type (map, r_type, 0);
396 break;
397 }
398 }
399 }
400
401 inline void
402 __attribute__ ((always_inline))
403 elf_machine_rela_relative (ElfW(Addr) l_addr,
404 const ElfW(Rela) *reloc,
405 void *const reloc_addr_arg)
406 {
407 ElfW(Addr) *const reloc_addr = reloc_addr_arg;
408 *reloc_addr = l_addr + reloc->r_addend;
409 }
410
411 inline void
412 __attribute__ ((always_inline))
413 elf_machine_lazy_rel (struct link_map *map,
414 ElfW(Addr) l_addr,
415 const ElfW(Rela) *reloc,
416 int skip_ifunc)
417 {
418 ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
419 const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
420 /* Check for unexpected PLT reloc type. */
421 if (__builtin_expect (r_type == AARCH64_R(JUMP_SLOT), 1))
422 {
423 if (__builtin_expect (map->l_mach.plt, 0) == 0)
424 *reloc_addr += l_addr;
425 else
426 *reloc_addr = map->l_mach.plt;
427 }
428 else if (__builtin_expect (r_type == AARCH64_R(TLSDESC), 1))
429 {
430 struct tlsdesc volatile *td =
431 (struct tlsdesc volatile *)reloc_addr;
432
433 td->arg = (void*)reloc;
434 td->entry = (void*)(D_PTR (map, l_info[ADDRIDX (DT_TLSDESC_PLT)])
435 + map->l_addr);
436 }
437 else if (__glibc_unlikely (r_type == AARCH64_R(IRELATIVE)))
438 {
439 ElfW(Addr) value = map->l_addr + reloc->r_addend;
440 if (__glibc_likely (!skip_ifunc))
441 value = elf_ifunc_invoke (value);
442 *reloc_addr = value;
443 }
444 else
445 _dl_reloc_bad_type (map, r_type, 1);
446 }
447
448 #endif