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/>. */
23 #define ELF_MACHINE_NAME "LoongArch"
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" \
39 __STRING (entry) ":\n"
42 #ifndef _RTLD_EPILOGUE
43 # define _RTLD_EPILOGUE(entry) \
45 ".size\t" __STRING (entry) ", . - " __STRING (entry) "\n"
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
;
68 /* init_cpu_features has been called early from __libc_start_main in
70 init_cpu_features (&GLRO(dl_larch_cpu_features
));
75 /* Return nonzero iff ELF header is compatible with the running host. */
77 elf_machine_matches_host (const ElfW (Ehdr
) *ehdr
)
79 /* We can only run LoongArch binaries. */
80 if (ehdr
->e_machine
!= EM_LOONGARCH
)
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 (\
106 " _RTLD_PROLOGUE (ENTRY_POINT) "\
107 .cfi_label .Ldummy \n\
108 " CFI_UNDEFINED (1) " \n\
109 or $a0, $sp, $zero \n\
111 # Stash user entry point in s0. \n\
112 or $s0, $a0, $zero \n\
113 # Load the original argument count. \n\
115 # Call _dl_init (struct link_map *main_map, int argc, \
116 char **argv, char **env) \n\
117 la $a0, _rtld_local \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\
129 # Restore the stack pointer for _start.\n\
130 or $sp, $s1, $zero \n\
131 # Pass our finalizer function to _start. \n\
133 # Jump to the user entry point. \n\
134 jirl $zero, $s0, 0 \n\
135 " _RTLD_EPILOGUE (ENTRY_POINT) "\
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
,
151 return *reloc_addr
= value
;
154 #endif /* !dl_machine_h */
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;
176 value
= SYMBOL_ADDRESS (sym_map
, sym
, true) + reloc
->r_addend
;
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
));
187 case R_LARCH_JUMP_SLOT
:
188 case __WORDSIZE
== 64 ? R_LARCH_64
: R_LARCH_32
:
195 #ifndef RTLD_BOOTSTRAP
196 case __WORDSIZE
== 64 ? R_LARCH_TLS_DTPMOD64
: R_LARCH_TLS_DTPMOD32
:
197 *addr_field
= sym_map
->l_tls_modid
;
200 case __WORDSIZE
== 64 ? R_LARCH_TLS_DTPREL64
: R_LARCH_TLS_DTPREL32
:
201 *addr_field
= TLS_DTPREL_VALUE (sym
) + reloc
->r_addend
;
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
;
209 case __WORDSIZE
== 64 ? R_LARCH_TLS_DESC64
: R_LARCH_TLS_DESC32
:
211 struct tlsdesc
volatile *td
= (struct tlsdesc
volatile *)addr_field
;
214 td
->arg
= (void*)reloc
->r_addend
;
215 td
->entry
= _dl_tlsdesc_undefweak
;
220 CHECK_STATIC_TLS (map
, sym_map
);
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
;
231 td
->arg
= (void *)(TLS_TPREL_VALUE (sym_map
, sym
)
233 td
->entry
= _dl_tlsdesc_return
;
242 /* This can happen in trace mode if an object could not be
245 if (__glibc_unlikely (sym
->st_size
> refsym
->st_size
)
246 || (__glibc_unlikely (sym
->st_size
< refsym
->st_size
)
247 && GLRO(dl_verbose
)))
251 strtab
= (const char *) D_PTR (map
, l_info
[DT_STRTAB
]);
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
));
262 case R_LARCH_RELATIVE
:
263 *addr_field
= map
->l_addr
+ reloc
->r_addend
;
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
) ();
276 _dl_reloc_bad_type (map
, r_type
, 0);
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
[],
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))
302 *reloc_addr
+= l_addr
;
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
,
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
;
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. */
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
;
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
375 GL(dl_profile_map
) = l
;
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
;
390 gotplt
[0] = (ElfW(Addr
)) &_dl_runtime_resolve
;
392 gotplt
[1] = (ElfW (Addr
)) l
;
399 #endif /* RESOLVE_MAP */