1
/* Copyright (C) 1995-2012 Free Software Foundation, Inc.
3
This file is part of the GNU C Library.
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.
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.
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/>. */
22
#define ELF_MACHINE_NAME "aarch64"
25
#include <dl-tlsdesc.h>
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)
31
return ehdr->e_machine == EM_AARCH64;
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)
39
ElfW(Addr) addr = (ElfW(Addr)) &_DYNAMIC;
43
/* Return the run-time load address of the shared object. */
45
static inline ElfW(Addr) __attribute__ ((unused))
46
elf_machine_load_address (void)
48
/* To figure out the load address we use the definition that for any symbol:
49
dynamic_addr(symbol) = static_addr(symbol) + load_addr
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. */
56
ElfW(Addr) static_addr;
57
ElfW(Addr) dynamic_addr;
60
adrp %1, _dl_start; \n\
61
add %1, %1, #:lo12:_dl_start \n\
64
1: .word _dl_start \n\
66
" : "=r" (static_addr), "=r" (dynamic_addr));
67
return dynamic_addr - static_addr;
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. */
73
static inline int __attribute__ ((unused))
74
elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
76
if (l->l_info[DT_JMPREL] && lazy)
79
extern void _dl_runtime_resolve (ElfW(Word));
80
extern void _dl_runtime_profile (ElfW(Word));
82
got = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
85
l->l_mach.plt = got[1] + l->l_addr;
87
got[1] = (ElfW(Addr)) l;
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. */
97
got[2] = (ElfW(Addr)) &_dl_runtime_profile;
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
103
GL(dl_profile_map) = l;
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;
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;
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 */
125
#define RTLD_START asm ("\
128
.type _start, %function \n\
129
.globl _dl_start_user \n\
130
.type _dl_start_user, %function \n\
134
// returns user entry point in x0 \n\
137
// get the original arg count \n\
139
// get the argv address \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\
147
beq .L_done_stack_adjust \n\
148
// subtract _dl_skip_args from original arg count \n\
150
// store adjusted argc back to stack \n\
152
// find the first unskipped argument \n\
154
add x4, x2, x4, lsl #3 \n\
155
// shuffle argv down \n\
156
1: ldr x5, [x4], #8 \n\
160
// shuffle envp down \n\
161
1: ldr x5, [x4], #8 \n\
165
// shuffle auxv down \n\
166
1: ldp x0, x5, [x4, #16]! \n\
167
stp x0, x5, [x3], #16 \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\
175
add x3, x2, x1, lsl #3 \n\
177
adrp x16, _rtld_local \n\
178
add x16, x16, #:lo12:_rtld_local \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\
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))
196
#define ELF_MACHINE_JMP_SLOT R_AARCH64_JUMP_SLOT
198
/* AArch64 uses RELA not REL */
199
#define ELF_MACHINE_NO_REL 1
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,
207
return *reloc_addr = value;
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,
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
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)
233
ElfW(Addr) *const reloc_addr = reloc_addr_arg;
234
const unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
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))
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;
252
if (sym->st_size > refsym->st_size
253
|| (GLRO(dl_verbose) && sym->st_size < refsym->st_size))
257
strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
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);
263
memcpy (reloc_addr_arg, (void *) value,
264
MIN (sym->st_size, refsym->st_size));
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;
275
case R_AARCH64_TLSDESC:
277
struct tlsdesc volatile *td =
278
(struct tlsdesc volatile *)reloc_addr;
279
#ifndef RTLD_BOOTSTRAP
282
td->arg = (void*)reloc->r_addend;
283
td->entry = _dl_tlsdesc_undefweak;
288
#ifndef RTLD_BOOTSTRAP
290
CHECK_STATIC_TLS (map, sym_map);
292
if (!TRY_STATIC_TLS (map, sym_map))
294
td->arg = _dl_make_tlsdesc_dynamic
295
(sym_map, sym->st_value + reloc->r_addend);
296
td->entry = _dl_tlsdesc_dynamic;
302
td->arg = (void*)(sym->st_value + sym_map->l_tls_offset
304
td->entry = _dl_tlsdesc_return;
310
case R_AARCH64_TLS_DTPMOD64:
311
#ifdef RTLD_BOOTSTRAP
316
*reloc_addr = sym_map->l_tls_modid;
321
case R_AARCH64_TLS_DTPREL64:
323
*reloc_addr = sym->st_value + reloc->r_addend;
326
case R_AARCH64_TLS_TPREL64:
329
CHECK_STATIC_TLS (map, sym_map);
331
sym->st_value + reloc->r_addend + sym_map->l_tls_offset;
336
_dl_reloc_bad_type (map, r_type, 0);
343
__attribute__ ((always_inline))
344
elf_machine_rela_relative (ElfW(Addr) l_addr,
345
const ElfW(Rela) *reloc,
346
void *const reloc_addr_arg)
348
ElfW(Addr) *const reloc_addr = reloc_addr_arg;
349
*reloc_addr = l_addr + reloc->r_addend;
353
__attribute__ ((always_inline))
354
elf_machine_lazy_rel (struct link_map *map,
356
const ElfW(Rela) *reloc,
359
ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
360
const unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
361
/* Check for unexpected PLT reloc type. */
362
if (__builtin_expect (r_type == R_AARCH64_JUMP_SLOT, 1))
364
if (__builtin_expect (map->l_mach.plt, 0) == 0)
365
*reloc_addr += l_addr;
367
*reloc_addr = map->l_mach.plt;
369
else if (__builtin_expect (r_type == R_AARCH64_TLSDESC, 1))
371
struct tlsdesc volatile *td =
372
(struct tlsdesc volatile *)reloc_addr;
374
td->arg = (void*)reloc;
375
td->entry = (void*)(D_PTR (map, l_info[ADDRIDX (DT_TLSDESC_PLT)])
379
_dl_reloc_bad_type (map, r_type, 1);