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