1 /* Copyright (C) 1995-2023 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
));
71 extern void _dl_runtime_profile (ElfW(Word
));
73 got
= (ElfW(Addr
) *) D_PTR (l
, l_info
[DT_PLTGOT
]);
76 l
->l_mach
.plt
= got
[1] + l
->l_addr
;
78 got
[1] = (ElfW(Addr
)) l
;
80 /* The got[2] entry contains the address of a function which gets
81 called to get the address of a so far unresolved function and
82 jump to it. The profiling extension of the dynamic linker allows
83 to intercept the calls to collect information. In this case we
84 don't store the address in the GOT so that all future calls also
85 end in this function. */
88 got
[2] = (ElfW(Addr
)) &_dl_runtime_profile
;
90 if (GLRO(dl_profile
) != NULL
91 && _dl_name_match_p (GLRO(dl_profile
), l
))
92 /* Say that we really want profiling and the timers are
94 GL(dl_profile_map
) = l
;
98 /* This function will get called to fix up the GOT entry
99 indicated by the offset on the stack, and then jump to
100 the resolved address. */
101 got
[2] = (ElfW(Addr
)) &_dl_runtime_resolve
;
108 /* In elf/rtld.c _dl_start should be global so dl-start.S can reference it. */
109 #define RTLD_START asm (".globl _dl_start");
111 #define elf_machine_type_class(type) \
112 ((((type) == R_AARCH64_JUMP_SLOT || \
113 (type) == R_AARCH64_TLS_DTPMOD || \
114 (type) == R_AARCH64_TLS_DTPREL || \
115 (type) == R_AARCH64_TLS_TPREL || \
116 (type) == R_AARCH64_TLSDESC) * ELF_RTYPE_CLASS_PLT) \
117 | (((type) == R_AARCH64_COPY) * ELF_RTYPE_CLASS_COPY))
119 #define ELF_MACHINE_JMP_SLOT AARCH64_R(JUMP_SLOT)
121 #define DL_PLATFORM_INIT dl_platform_init ()
123 static inline void __attribute__ ((unused
))
124 dl_platform_init (void)
126 if (GLRO(dl_platform
) != NULL
&& *GLRO(dl_platform
) == '\0')
127 /* Avoid an empty string which would disturb us. */
128 GLRO(dl_platform
) = NULL
;
131 /* init_cpu_features has been called early from __libc_start_main in
132 static executable. */
133 init_cpu_features (&GLRO(dl_aarch64_cpu_features
));
138 static inline ElfW(Addr
)
139 elf_machine_fixup_plt (struct link_map
*map
, lookup_t t
,
140 const ElfW(Sym
) *refsym
, const ElfW(Sym
) *sym
,
141 const ElfW(Rela
) *reloc
,
142 ElfW(Addr
) *reloc_addr
,
145 return *reloc_addr
= value
;
148 /* Return the final value of a plt relocation. */
149 static inline ElfW(Addr
)
150 elf_machine_plt_value (struct link_map
*map
,
151 const ElfW(Rela
) *reloc
,
159 /* Names of the architecture-specific auditing callback functions. */
160 #define ARCH_LA_PLTENTER aarch64_gnu_pltenter
161 #define ARCH_LA_PLTEXIT aarch64_gnu_pltexit
166 __attribute__ ((always_inline
))
167 elf_machine_rela (struct link_map
*map
, struct r_scope_elem
*scope
[],
168 const ElfW(Rela
) *reloc
, const ElfW(Sym
) *sym
,
169 const struct r_found_version
*version
,
170 void *const reloc_addr_arg
, int skip_ifunc
)
172 ElfW(Addr
) *const reloc_addr
= reloc_addr_arg
;
173 const unsigned int r_type
= ELFW (R_TYPE
) (reloc
->r_info
);
175 if (__builtin_expect (r_type
== AARCH64_R(RELATIVE
), 0))
176 *reloc_addr
= map
->l_addr
+ reloc
->r_addend
;
177 else if (__builtin_expect (r_type
== R_AARCH64_NONE
, 0))
181 # ifndef RTLD_BOOTSTRAP
182 const ElfW(Sym
) *const refsym
= sym
;
184 struct link_map
*sym_map
= RESOLVE_MAP (map
, scope
, &sym
, version
,
186 ElfW(Addr
) value
= SYMBOL_ADDRESS (sym_map
, sym
, true);
189 && __glibc_unlikely (ELFW(ST_TYPE
) (sym
->st_info
) == STT_GNU_IFUNC
)
190 && __glibc_likely (sym
->st_shndx
!= SHN_UNDEF
)
191 && __glibc_likely (!skip_ifunc
))
192 value
= elf_ifunc_invoke (value
);
196 case AARCH64_R(GLOB_DAT
):
197 case AARCH64_R(JUMP_SLOT
):
198 *reloc_addr
= value
+ reloc
->r_addend
;
201 # ifndef RTLD_BOOTSTRAP
202 case AARCH64_R(ABS32
):
204 case AARCH64_R(ABS64
):
206 *reloc_addr
= value
+ reloc
->r_addend
;
208 case AARCH64_R(COPY
):
212 if (sym
->st_size
> refsym
->st_size
213 || (GLRO(dl_verbose
) && sym
->st_size
< refsym
->st_size
))
217 strtab
= (const void *) D_PTR (map
, l_info
[DT_STRTAB
]);
219 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
220 RTLD_PROGNAME
, strtab
+ refsym
->st_name
);
222 memcpy (reloc_addr_arg
, (void *) value
,
223 sym
->st_size
< refsym
->st_size
224 ? sym
->st_size
: refsym
->st_size
);
227 case AARCH64_R(TLSDESC
):
229 struct tlsdesc
volatile *td
=
230 (struct tlsdesc
volatile *)reloc_addr
;
233 td
->arg
= (void*)reloc
->r_addend
;
234 td
->entry
= _dl_tlsdesc_undefweak
;
239 CHECK_STATIC_TLS (map
, sym_map
);
241 if (!TRY_STATIC_TLS (map
, sym_map
))
243 td
->arg
= _dl_make_tlsdesc_dynamic
244 (sym_map
, sym
->st_value
+ reloc
->r_addend
);
245 td
->entry
= _dl_tlsdesc_dynamic
;
250 td
->arg
= (void*)(sym
->st_value
+ sym_map
->l_tls_offset
252 td
->entry
= _dl_tlsdesc_return
;
258 case AARCH64_R(TLS_DTPMOD
):
261 *reloc_addr
= sym_map
->l_tls_modid
;
265 case AARCH64_R(TLS_DTPREL
):
267 *reloc_addr
= sym
->st_value
+ reloc
->r_addend
;
270 case AARCH64_R(TLS_TPREL
):
273 CHECK_STATIC_TLS (map
, sym_map
);
275 sym
->st_value
+ reloc
->r_addend
+ sym_map
->l_tls_offset
;
279 case AARCH64_R(IRELATIVE
):
280 value
= map
->l_addr
+ reloc
->r_addend
;
281 if (__glibc_likely (!skip_ifunc
))
282 value
= elf_ifunc_invoke (value
);
285 # endif /* !RTLD_BOOTSTRAP */
288 _dl_reloc_bad_type (map
, r_type
, 0);
295 __attribute__ ((always_inline
))
296 elf_machine_rela_relative (ElfW(Addr
) l_addr
,
297 const ElfW(Rela
) *reloc
,
298 void *const reloc_addr_arg
)
300 ElfW(Addr
) *const reloc_addr
= reloc_addr_arg
;
301 *reloc_addr
= l_addr
+ reloc
->r_addend
;
305 __attribute__ ((always_inline
))
306 elf_machine_lazy_rel (struct link_map
*map
, struct r_scope_elem
*scope
[],
308 const ElfW(Rela
) *reloc
,
311 ElfW(Addr
) *const reloc_addr
= (void *) (l_addr
+ reloc
->r_offset
);
312 const unsigned int r_type
= ELFW (R_TYPE
) (reloc
->r_info
);
313 /* Check for unexpected PLT reloc type. */
314 if (__builtin_expect (r_type
== AARCH64_R(JUMP_SLOT
), 1))
316 if (__glibc_unlikely (map
->l_info
[DT_AARCH64 (VARIANT_PCS
)] != NULL
))
318 /* Check the symbol table for variant PCS symbols. */
319 const Elf_Symndx symndx
= ELFW (R_SYM
) (reloc
->r_info
);
320 const ElfW (Sym
) *symtab
=
321 (const void *)D_PTR (map
, l_info
[DT_SYMTAB
]);
322 const ElfW (Sym
) *sym
= &symtab
[symndx
];
323 if (__glibc_unlikely (sym
->st_other
& STO_AARCH64_VARIANT_PCS
))
325 /* Avoid lazy resolution of variant PCS symbols. */
326 const struct r_found_version
*version
= NULL
;
327 if (map
->l_info
[VERSYMIDX (DT_VERSYM
)] != NULL
)
329 const ElfW (Half
) *vernum
=
330 (const void *)D_PTR (map
, l_info
[VERSYMIDX (DT_VERSYM
)]);
331 version
= &map
->l_versions
[vernum
[symndx
] & 0x7fff];
333 elf_machine_rela (map
, scope
, reloc
, sym
, version
, reloc_addr
,
339 if (map
->l_mach
.plt
== 0)
340 *reloc_addr
+= l_addr
;
342 *reloc_addr
= map
->l_mach
.plt
;
344 else if (__builtin_expect (r_type
== AARCH64_R(TLSDESC
), 1))
346 const Elf_Symndx symndx
= ELFW (R_SYM
) (reloc
->r_info
);
347 const ElfW (Sym
) *symtab
= (const void *)D_PTR (map
, l_info
[DT_SYMTAB
]);
348 const ElfW (Sym
) *sym
= &symtab
[symndx
];
349 const struct r_found_version
*version
= NULL
;
351 if (map
->l_info
[VERSYMIDX (DT_VERSYM
)] != NULL
)
353 const ElfW (Half
) *vernum
=
354 (const void *)D_PTR (map
, l_info
[VERSYMIDX (DT_VERSYM
)]);
355 version
= &map
->l_versions
[vernum
[symndx
] & 0x7fff];
358 /* Always initialize TLS descriptors completely, because lazy
359 initialization requires synchronization at every TLS access. */
360 elf_machine_rela (map
, scope
, reloc
, sym
, version
, reloc_addr
,
363 else if (__glibc_unlikely (r_type
== AARCH64_R(IRELATIVE
)))
365 ElfW(Addr
) value
= map
->l_addr
+ reloc
->r_addend
;
366 if (__glibc_likely (!skip_ifunc
))
367 value
= elf_ifunc_invoke (value
);
371 _dl_reloc_bad_type (map
, r_type
, 1);