Benchtests: Remove broken walk benchmarks
[glibc.git] / sysdeps / loongarch / dl-machine.h
blobab6f1da7c08701aa803b82395c24b0ccb983cf74
1 /* Machine-dependent ELF dynamic relocation inline functions.
2 Copyright (C) 2022-2024 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library. If not, see
18 <https://www.gnu.org/licenses/>. */
20 #ifndef dl_machine_h
21 #define dl_machine_h
23 #define ELF_MACHINE_NAME "LoongArch"
25 #include <entry.h>
26 #include <elf/elf.h>
27 #include <sys/asm.h>
28 #include <dl-tlsdesc.h>
29 #include <dl-static-tls.h>
30 #include <dl-machine-rel.h>
32 #include <cpu-features.c>
34 #ifndef _RTLD_PROLOGUE
35 # define _RTLD_PROLOGUE(entry) \
36 ".globl\t" __STRING (entry) "\n\t" \
37 ".type\t" __STRING (entry) ", @function\n\t" \
38 CFI_STARTPROC "\n" \
39 __STRING (entry) ":\n"
40 #endif
42 #ifndef _RTLD_EPILOGUE
43 # define _RTLD_EPILOGUE(entry) \
44 CFI_ENDPROC "\n\t" \
45 ".size\t" __STRING (entry) ", . - " __STRING (entry) "\n"
46 #endif
48 #define ELF_MACHINE_JMP_SLOT R_LARCH_JUMP_SLOT
49 #define ELF_MACHINE_IRELATIVE R_LARCH_IRELATIVE
51 #define elf_machine_type_class(type) \
52 ((ELF_RTYPE_CLASS_PLT *((type) == ELF_MACHINE_JMP_SLOT)) \
53 | (ELF_RTYPE_CLASS_COPY *((type) == R_LARCH_COPY)))
55 #define ELF_MACHINE_NO_REL 1
56 #define ELF_MACHINE_NO_RELA 0
58 #define DL_PLATFORM_INIT dl_platform_init ()
60 static inline void __attribute__ ((unused))
61 dl_platform_init (void)
63 if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
64 /* Avoid an empty string which would disturb us. */
65 GLRO(dl_platform) = NULL;
67 #ifdef SHARED
68 /* init_cpu_features has been called early from __libc_start_main in
69 static executable. */
70 init_cpu_features (&GLRO(dl_larch_cpu_features));
71 #endif
75 /* Return nonzero iff ELF header is compatible with the running host. */
76 static inline int
77 elf_machine_matches_host (const ElfW (Ehdr) *ehdr)
79 /* We can only run LoongArch binaries. */
80 if (ehdr->e_machine != EM_LOONGARCH)
81 return 0;
83 return 1;
86 /* Return the run-time load address of the shared object. */
87 static inline ElfW (Addr) elf_machine_load_address (void)
89 extern const ElfW(Ehdr) __ehdr_start attribute_hidden;
90 return (ElfW(Addr)) &__ehdr_start;
93 /* Return the link-time address of _DYNAMIC. */
94 static inline ElfW (Addr) elf_machine_dynamic (void)
96 extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
97 return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address ();
100 /* Initial entry point code for the dynamic linker.
101 The C function `_dl_start' is the real entry point;
102 its return value is the user program's entry point. */
104 #define RTLD_START asm (\
105 ".text\n\
106 " _RTLD_PROLOGUE (ENTRY_POINT) "\
107 .cfi_label .Ldummy \n\
108 " CFI_UNDEFINED (1) " \n\
109 or $a0, $sp, $zero \n\
110 bl _dl_start \n\
111 # Stash user entry point in s0. \n\
112 or $s0, $a0, $zero \n\
113 # Load the original argument count. \n\
114 ld.d $a1, $sp, 0 \n\
115 # Call _dl_init (struct link_map *main_map, int argc, \
116 char **argv, char **env) \n\
117 la $a0, _rtld_local \n\
118 ld.d $a0, $a0, 0 \n\
119 addi.d $a2, $sp, 8 \n\
120 slli.d $a3, $a1, 3 \n\
121 add.d $a3, $a3, $a2 \n\
122 addi.d $a3, $a3, 8 \n\
123 # Stash the stack pointer in s1.\n\
124 or $s1, $sp, $zero \n\
125 # Adjust $sp for 16-aligned \n\
126 bstrins.d $sp, $zero, 3, 0 \n\
127 # Call the function to run the initializers. \n\
128 bl _dl_init \n\
129 # Restore the stack pointer for _start.\n\
130 or $sp, $s1, $zero \n\
131 # Pass our finalizer function to _start. \n\
132 la $a0, _dl_fini \n\
133 # Jump to the user entry point. \n\
134 jirl $zero, $s0, 0 \n\
135 " _RTLD_EPILOGUE (ENTRY_POINT) "\
136 .previous");
138 /* Names of the architecture-specific auditing callback functions. */
139 #define ARCH_LA_PLTENTER loongarch_gnu_pltenter
140 #define ARCH_LA_PLTEXIT loongarch_gnu_pltexit
142 /* Bias .got.plt entry by the offset requested by the PLT header. */
143 #define elf_machine_plt_value(map, reloc, value) (value)
145 static inline ElfW (Addr)
146 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
147 const ElfW (Sym) *refsym, const ElfW (Sym) *sym,
148 const ElfW (Rela) *reloc, ElfW (Addr) *reloc_addr,
149 ElfW (Addr) value)
151 return *reloc_addr = value;
154 #endif /* !dl_machine_h */
156 #ifdef RESOLVE_MAP
158 /* Perform a relocation described by R_INFO at the location pointed to
159 by RELOC_ADDR. SYM is the relocation symbol specified by R_INFO and
160 MAP is the object containing the reloc. */
162 static inline void __attribute__ ((always_inline))
163 elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
164 const ElfW (Rela) *reloc,
165 const ElfW (Sym) *sym,
166 const struct r_found_version *version,
167 void *const reloc_addr, int skip_ifunc)
169 ElfW (Addr) r_info = reloc->r_info;
170 const unsigned long int r_type = ELFW (R_TYPE) (r_info);
171 ElfW (Addr) *addr_field = (ElfW (Addr) *) reloc_addr;
172 const ElfW (Sym) *const __attribute__ ((unused)) refsym = sym;
173 struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
174 ElfW (Addr) value = 0;
175 if (sym_map != NULL)
176 value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend;
178 if (sym != NULL
179 && __glibc_unlikely (ELFW (ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
180 && __glibc_likely (sym->st_shndx != SHN_UNDEF)
181 && __glibc_likely (!skip_ifunc))
182 value = ((ElfW (Addr) (*) (int)) value) (GLRO (dl_hwcap));
184 switch (r_type)
187 case R_LARCH_JUMP_SLOT:
188 case __WORDSIZE == 64 ? R_LARCH_64 : R_LARCH_32:
189 *addr_field = value;
190 break;
192 case R_LARCH_NONE:
193 break;
195 #ifndef RTLD_BOOTSTRAP
196 case __WORDSIZE == 64 ? R_LARCH_TLS_DTPMOD64 : R_LARCH_TLS_DTPMOD32:
197 *addr_field = sym_map->l_tls_modid;
198 break;
200 case __WORDSIZE == 64 ? R_LARCH_TLS_DTPREL64 : R_LARCH_TLS_DTPREL32:
201 *addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
202 break;
204 case __WORDSIZE == 64 ? R_LARCH_TLS_TPREL64 : R_LARCH_TLS_TPREL32:
205 CHECK_STATIC_TLS (map, sym_map);
206 *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
207 break;
209 case __WORDSIZE == 64 ? R_LARCH_TLS_DESC64 : R_LARCH_TLS_DESC32:
211 struct tlsdesc volatile *td = (struct tlsdesc volatile *)addr_field;
212 if (sym == NULL)
214 td->arg = (void*)reloc->r_addend;
215 td->entry = _dl_tlsdesc_undefweak;
217 else
219 # ifndef SHARED
220 CHECK_STATIC_TLS (map, sym_map);
221 # else
222 if (!TRY_STATIC_TLS (map, sym_map))
224 td->arg = _dl_make_tlsdesc_dynamic (sym_map,
225 sym->st_value + reloc->r_addend);
226 td->entry = _dl_tlsdesc_dynamic;
228 else
229 # endif
231 td->arg = (void *)(TLS_TPREL_VALUE (sym_map, sym)
232 + reloc->r_addend);
233 td->entry = _dl_tlsdesc_return;
236 break;
239 case R_LARCH_COPY:
241 if (sym == NULL)
242 /* This can happen in trace mode if an object could not be
243 found. */
244 break;
245 if (__glibc_unlikely (sym->st_size > refsym->st_size)
246 || (__glibc_unlikely (sym->st_size < refsym->st_size)
247 && GLRO(dl_verbose)))
249 const char *strtab;
251 strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
252 _dl_error_printf ("\
253 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
254 rtld_progname ?: "<program name unknown>",
255 strtab + refsym->st_name);
257 memcpy (reloc_addr, (void *) value,
258 MIN (sym->st_size, refsym->st_size));
259 break;
262 case R_LARCH_RELATIVE:
263 *addr_field = map->l_addr + reloc->r_addend;
264 break;
266 case R_LARCH_IRELATIVE:
267 value = map->l_addr + reloc->r_addend;
268 if (__glibc_likely (!skip_ifunc))
269 value = ((ElfW (Addr) (*) (void)) value) ();
270 *addr_field = value;
271 break;
273 #endif
275 default:
276 _dl_reloc_bad_type (map, r_type, 0);
277 break;
281 static inline void __attribute__ ((always_inline))
282 elf_machine_rela_relative (ElfW (Addr) l_addr, const ElfW (Rela) *reloc,
283 void *const reloc_addr)
285 *(ElfW (Addr) *) reloc_addr = l_addr + reloc->r_addend;
288 static inline void __attribute__ ((always_inline))
289 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
290 ElfW (Addr) l_addr,
291 const ElfW (Rela) *reloc, int skip_ifunc)
293 ElfW (Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
294 const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
296 /* Check for unexpected PLT reloc type. */
297 if (__glibc_likely (r_type == R_LARCH_JUMP_SLOT))
299 if (__glibc_unlikely (map->l_mach.plt == 0))
301 if (l_addr)
302 *reloc_addr += l_addr;
304 else
305 *reloc_addr = map->l_mach.plt;
307 else if (__glibc_likely (r_type == R_LARCH_TLS_DESC64)
308 || __glibc_likely (r_type == R_LARCH_TLS_DESC32))
310 const Elf_Symndx symndx = ELFW (R_SYM) (reloc->r_info);
311 const ElfW (Sym) *symtab = (const void *)D_PTR (map, l_info[DT_SYMTAB]);
312 const ElfW (Sym) *sym = &symtab[symndx];
313 const struct r_found_version *version = NULL;
315 if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
317 const ElfW (Half) *vernum = (const void *)D_PTR (map,
318 l_info[VERSYMIDX (DT_VERSYM)]);
319 version = &map->l_versions[vernum[symndx] & 0x7fff];
322 /* Always initialize TLS descriptors completely, because lazy
323 initialization requires synchronization at every TLS access. */
324 elf_machine_rela (map, scope, reloc, sym, version, reloc_addr,
325 skip_ifunc);
327 else
328 _dl_reloc_bad_type (map, r_type, 1);
331 /* Set up the loaded object described by L so its stub function
332 will jump to the on-demand fixup code __dl_runtime_resolve. */
334 static inline int __attribute__ ((always_inline))
335 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
336 int lazy, int profile)
338 #ifndef RTLD_BOOTSTRAP
339 /* If using PLTs, fill in the first two entries of .got.plt. */
340 if (l->l_info[DT_JMPREL])
342 #if !defined __loongarch_soft_float
343 extern void _dl_runtime_resolve_lasx (void) attribute_hidden;
344 extern void _dl_runtime_resolve_lsx (void) attribute_hidden;
345 extern void _dl_runtime_profile_lasx (void) attribute_hidden;
346 extern void _dl_runtime_profile_lsx (void) attribute_hidden;
347 #endif
348 extern void _dl_runtime_resolve (void) attribute_hidden;
349 extern void _dl_runtime_profile (void) attribute_hidden;
351 ElfW (Addr) *gotplt = (ElfW (Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
353 /* The got[0] entry contains the address of a function which gets
354 called to get the address of a so far unresolved function and
355 jump to it. The profiling extension of the dynamic linker allows
356 to intercept the calls to collect information. In this case we
357 don't store the address in the GOT so that all future calls also
358 end in this function. */
359 #ifdef SHARED
360 if (profile != 0)
362 #if !defined __loongarch_soft_float
363 if (RTLD_SUPPORT_LASX)
364 gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile_lasx;
365 else if (RTLD_SUPPORT_LSX)
366 gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile_lsx;
367 else
368 # endif
369 gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile;
371 if (GLRO(dl_profile) != NULL
372 && _dl_name_match_p (GLRO(dl_profile), l))
373 /* Say that we really want profiling and the timers are
374 started. */
375 GL(dl_profile_map) = l;
377 else
378 #endif
380 /* This function will get called to fix up the GOT entry
381 indicated by the offset on the stack, and then jump to
382 the resolved address. */
383 #if !defined __loongarch_soft_float
384 if (RTLD_SUPPORT_LASX)
385 gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve_lasx;
386 else if (RTLD_SUPPORT_LSX)
387 gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve_lsx;
388 else
389 #endif
390 gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve;
392 gotplt[1] = (ElfW (Addr)) l;
394 #endif
396 return lazy;
399 #endif /* RESOLVE_MAP */