1 /* Copyright (C) 1995-2024 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 <https://www.gnu.org/licenses/>. */
22 #define ELF_MACHINE_NAME "aarch64"
26 #include <dl-tlsdesc.h>
27 #include <dl-static-tls.h>
29 #include <dl-machine-rel.h>
30 #include <cpu-features.c>
32 /* Translate a processor specific dynamic tag to the index in l_info array. */
33 #define DT_AARCH64(x) (DT_AARCH64_##x - DT_LOPROC + DT_NUM)
35 /* Return nonzero iff ELF header is compatible with the running host. */
36 static inline int __attribute__ ((unused
))
37 elf_machine_matches_host (const ElfW(Ehdr
) *ehdr
)
39 return ehdr
->e_machine
== EM_AARCH64
;
42 /* Return the run-time load address of the shared object. */
44 static inline ElfW(Addr
) __attribute__ ((unused
))
45 elf_machine_load_address (void)
47 extern const ElfW(Ehdr
) __ehdr_start attribute_hidden
;
48 return (ElfW(Addr
)) &__ehdr_start
;
51 /* Return the link-time address of _DYNAMIC. */
53 static inline ElfW(Addr
) __attribute__ ((unused
))
54 elf_machine_dynamic (void)
56 extern ElfW(Dyn
) _DYNAMIC
[] attribute_hidden
;
57 return (ElfW(Addr
)) _DYNAMIC
- elf_machine_load_address ();
60 /* Set up the loaded object described by L so its unrelocated PLT
61 entries will jump to the on-demand fixup code in dl-runtime.c. */
63 static inline int __attribute__ ((unused
))
64 elf_machine_runtime_setup (struct link_map
*l
, struct r_scope_elem
*scope
[],
65 int lazy
, int profile
)
67 if (l
->l_info
[DT_JMPREL
] && lazy
)
70 extern void _dl_runtime_resolve (ElfW(Word
));
72 got
= (ElfW(Addr
) *) D_PTR (l
, l_info
[DT_PLTGOT
]);
75 l
->l_mach
.plt
= got
[1] + l
->l_addr
;
77 got
[1] = (ElfW(Addr
)) l
;
79 /* The got[2] entry contains the address of a function which gets
80 called to get the address of a so far unresolved function and
81 jump to it. The profiling extension of the dynamic linker allows
82 to intercept the calls to collect information. In this case we
83 don't store the address in the GOT so that all future calls also
84 end in this function. */
86 extern void _dl_runtime_profile (ElfW(Word
));
89 got
[2] = (ElfW(Addr
)) &_dl_runtime_profile
;
91 if (GLRO(dl_profile
) != NULL
92 && _dl_name_match_p (GLRO(dl_profile
), l
))
93 /* Say that we really want profiling and the timers are
95 GL(dl_profile_map
) = l
;
100 /* This function will get called to fix up the GOT entry
101 indicated by the offset on the stack, and then jump to
102 the resolved address. */
103 got
[2] = (ElfW(Addr
)) &_dl_runtime_resolve
;
110 /* In elf/rtld.c _dl_start should be global so dl-start.S can reference it. */
111 #define RTLD_START asm (".globl _dl_start");
113 #define elf_machine_type_class(type) \
114 ((((type) == R_AARCH64_JUMP_SLOT || \
115 (type) == R_AARCH64_TLS_DTPMOD || \
116 (type) == R_AARCH64_TLS_DTPREL || \
117 (type) == R_AARCH64_TLS_TPREL || \
118 (type) == R_AARCH64_TLSDESC) * ELF_RTYPE_CLASS_PLT) \
119 | (((type) == R_AARCH64_COPY) * ELF_RTYPE_CLASS_COPY))
121 #define ELF_MACHINE_JMP_SLOT AARCH64_R(JUMP_SLOT)
123 #define DL_PLATFORM_INIT dl_platform_init ()
125 static inline void __attribute__ ((unused
))
126 dl_platform_init (void)
128 if (GLRO(dl_platform
) != NULL
&& *GLRO(dl_platform
) == '\0')
129 /* Avoid an empty string which would disturb us. */
130 GLRO(dl_platform
) = NULL
;
133 /* init_cpu_features has been called early from __libc_start_main in
134 static executable. */
135 init_cpu_features (&GLRO(dl_aarch64_cpu_features
));
140 static inline ElfW(Addr
)
141 elf_machine_fixup_plt (struct link_map
*map
, lookup_t t
,
142 const ElfW(Sym
) *refsym
, const ElfW(Sym
) *sym
,
143 const ElfW(Rela
) *reloc
,
144 ElfW(Addr
) *reloc_addr
,
147 return *reloc_addr
= value
;
150 /* Return the final value of a plt relocation. */
151 static inline ElfW(Addr
)
152 elf_machine_plt_value (struct link_map
*map
,
153 const ElfW(Rela
) *reloc
,
161 /* Names of the architecture-specific auditing callback functions. */
162 #define ARCH_LA_PLTENTER aarch64_gnu_pltenter
163 #define ARCH_LA_PLTEXIT aarch64_gnu_pltexit
168 __attribute__ ((always_inline
))
169 elf_machine_rela (struct link_map
*map
, struct r_scope_elem
*scope
[],
170 const ElfW(Rela
) *reloc
, const ElfW(Sym
) *sym
,
171 const struct r_found_version
*version
,
172 void *const reloc_addr_arg
, int skip_ifunc
)
174 ElfW(Addr
) *const reloc_addr
= reloc_addr_arg
;
175 const unsigned int r_type
= ELFW (R_TYPE
) (reloc
->r_info
);
177 if (__builtin_expect (r_type
== AARCH64_R(RELATIVE
), 0))
178 *reloc_addr
= map
->l_addr
+ reloc
->r_addend
;
179 else if (__builtin_expect (r_type
== R_AARCH64_NONE
, 0))
183 # ifndef RTLD_BOOTSTRAP
184 const ElfW(Sym
) *const refsym
= sym
;
186 struct link_map
*sym_map
= RESOLVE_MAP (map
, scope
, &sym
, version
,
188 ElfW(Addr
) value
= SYMBOL_ADDRESS (sym_map
, sym
, true);
191 && __glibc_unlikely (ELFW(ST_TYPE
) (sym
->st_info
) == STT_GNU_IFUNC
)
192 && __glibc_likely (sym
->st_shndx
!= SHN_UNDEF
)
193 && __glibc_likely (!skip_ifunc
))
194 value
= elf_ifunc_invoke (value
);
198 case AARCH64_R(GLOB_DAT
):
199 case AARCH64_R(JUMP_SLOT
):
200 *reloc_addr
= value
+ reloc
->r_addend
;
203 # ifndef RTLD_BOOTSTRAP
204 case AARCH64_R(ABS32
):
206 case AARCH64_R(ABS64
):
208 *reloc_addr
= value
+ reloc
->r_addend
;
210 case AARCH64_R(COPY
):
214 if (sym
->st_size
> refsym
->st_size
215 || (GLRO(dl_verbose
) && sym
->st_size
< refsym
->st_size
))
219 strtab
= (const void *) D_PTR (map
, l_info
[DT_STRTAB
]);
221 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
222 RTLD_PROGNAME
, strtab
+ refsym
->st_name
);
224 memcpy (reloc_addr_arg
, (void *) value
,
225 sym
->st_size
< refsym
->st_size
226 ? sym
->st_size
: refsym
->st_size
);
229 case AARCH64_R(TLSDESC
):
231 struct tlsdesc
volatile *td
=
232 (struct tlsdesc
volatile *)reloc_addr
;
235 td
->arg
= (void*)reloc
->r_addend
;
236 td
->entry
= _dl_tlsdesc_undefweak
;
241 CHECK_STATIC_TLS (map
, sym_map
);
243 if (!TRY_STATIC_TLS (map
, sym_map
))
245 td
->arg
= _dl_make_tlsdesc_dynamic
246 (sym_map
, sym
->st_value
+ reloc
->r_addend
);
247 td
->entry
= _dl_tlsdesc_dynamic
;
252 td
->arg
= (void*)(sym
->st_value
+ sym_map
->l_tls_offset
254 td
->entry
= _dl_tlsdesc_return
;
260 case AARCH64_R(TLS_DTPMOD
):
263 *reloc_addr
= sym_map
->l_tls_modid
;
267 case AARCH64_R(TLS_DTPREL
):
269 *reloc_addr
= sym
->st_value
+ reloc
->r_addend
;
272 case AARCH64_R(TLS_TPREL
):
275 CHECK_STATIC_TLS (map
, sym_map
);
277 sym
->st_value
+ reloc
->r_addend
+ sym_map
->l_tls_offset
;
281 case AARCH64_R(IRELATIVE
):
282 value
= map
->l_addr
+ reloc
->r_addend
;
283 if (__glibc_likely (!skip_ifunc
))
284 value
= elf_ifunc_invoke (value
);
287 # endif /* !RTLD_BOOTSTRAP */
290 _dl_reloc_bad_type (map
, r_type
, 0);
297 __attribute__ ((always_inline
))
298 elf_machine_rela_relative (ElfW(Addr
) l_addr
,
299 const ElfW(Rela
) *reloc
,
300 void *const reloc_addr_arg
)
302 ElfW(Addr
) *const reloc_addr
= reloc_addr_arg
;
303 *reloc_addr
= l_addr
+ reloc
->r_addend
;
307 __attribute__ ((always_inline
))
308 elf_machine_lazy_rel (struct link_map
*map
, struct r_scope_elem
*scope
[],
310 const ElfW(Rela
) *reloc
,
313 ElfW(Addr
) *const reloc_addr
= (void *) (l_addr
+ reloc
->r_offset
);
314 const unsigned int r_type
= ELFW (R_TYPE
) (reloc
->r_info
);
315 /* Check for unexpected PLT reloc type. */
316 if (__builtin_expect (r_type
== AARCH64_R(JUMP_SLOT
), 1))
318 if (__glibc_unlikely (map
->l_info
[DT_AARCH64 (VARIANT_PCS
)] != NULL
))
320 /* Check the symbol table for variant PCS symbols. */
321 const Elf_Symndx symndx
= ELFW (R_SYM
) (reloc
->r_info
);
322 const ElfW (Sym
) *symtab
=
323 (const void *)D_PTR (map
, l_info
[DT_SYMTAB
]);
324 const ElfW (Sym
) *sym
= &symtab
[symndx
];
325 if (__glibc_unlikely (sym
->st_other
& STO_AARCH64_VARIANT_PCS
))
327 /* Avoid lazy resolution of variant PCS symbols. */
328 const struct r_found_version
*version
= NULL
;
329 if (map
->l_info
[VERSYMIDX (DT_VERSYM
)] != NULL
)
331 const ElfW (Half
) *vernum
=
332 (const void *)D_PTR (map
, l_info
[VERSYMIDX (DT_VERSYM
)]);
333 version
= &map
->l_versions
[vernum
[symndx
] & 0x7fff];
335 elf_machine_rela (map
, scope
, reloc
, sym
, version
, reloc_addr
,
341 if (map
->l_mach
.plt
== 0)
342 *reloc_addr
+= l_addr
;
344 *reloc_addr
= map
->l_mach
.plt
;
346 else if (__builtin_expect (r_type
== AARCH64_R(TLSDESC
), 1))
348 const Elf_Symndx symndx
= ELFW (R_SYM
) (reloc
->r_info
);
349 const ElfW (Sym
) *symtab
= (const void *)D_PTR (map
, l_info
[DT_SYMTAB
]);
350 const ElfW (Sym
) *sym
= &symtab
[symndx
];
351 const struct r_found_version
*version
= NULL
;
353 if (map
->l_info
[VERSYMIDX (DT_VERSYM
)] != NULL
)
355 const ElfW (Half
) *vernum
=
356 (const void *)D_PTR (map
, l_info
[VERSYMIDX (DT_VERSYM
)]);
357 version
= &map
->l_versions
[vernum
[symndx
] & 0x7fff];
360 /* Always initialize TLS descriptors completely, because lazy
361 initialization requires synchronization at every TLS access. */
362 elf_machine_rela (map
, scope
, reloc
, sym
, version
, reloc_addr
,
365 else if (__glibc_unlikely (r_type
== AARCH64_R(IRELATIVE
)))
367 ElfW(Addr
) value
= map
->l_addr
+ reloc
->r_addend
;
368 if (__glibc_likely (!skip_ifunc
))
369 value
= elf_ifunc_invoke (value
);
373 _dl_reloc_bad_type (map
, r_type
, 1);