rtld: Add glibc.rtld.enable_secure tunable.
[glibc.git] / sysdeps / riscv / dl-machine.h
blob0cbb476c0583f1fc6adc3e5052aff076b7c67f3b
1 /* Machine-dependent ELF dynamic relocation inline functions. RISC-V version.
2 Copyright (C) 2011-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
7 License as published by the Free Software Foundation; either
8 version 2.1 of the 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/>. */
19 #ifndef dl_machine_h
20 #define dl_machine_h
22 #define ELF_MACHINE_NAME "RISC-V"
24 #include <entry.h>
25 #include <elf/elf.h>
26 #include <sys/asm.h>
27 #include <dl-tls.h>
28 #include <dl-irel.h>
29 #include <dl-static-tls.h>
30 #include <dl-machine-rel.h>
32 #ifndef _RTLD_PROLOGUE
33 # define _RTLD_PROLOGUE(entry) \
34 ".globl\t" __STRING (entry) "\n\t" \
35 ".type\t" __STRING (entry) ", @function\n" \
36 __STRING (entry) ":\n\t"
37 #endif
39 #ifndef _RTLD_EPILOGUE
40 # define _RTLD_EPILOGUE(entry) \
41 ".size\t" __STRING (entry) ", . - " __STRING (entry) "\n\t"
42 #endif
44 #define ELF_MACHINE_JMP_SLOT R_RISCV_JUMP_SLOT
46 #define elf_machine_type_class(type) \
47 ((ELF_RTYPE_CLASS_PLT * ((type) == ELF_MACHINE_JMP_SLOT \
48 || (__WORDSIZE == 32 && (type) == R_RISCV_TLS_DTPREL32) \
49 || (__WORDSIZE == 32 && (type) == R_RISCV_TLS_DTPMOD32) \
50 || (__WORDSIZE == 32 && (type) == R_RISCV_TLS_TPREL32) \
51 || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_DTPREL64) \
52 || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_DTPMOD64) \
53 || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_TPREL64))) \
54 | (ELF_RTYPE_CLASS_COPY * ((type) == R_RISCV_COPY)))
56 /* Return nonzero iff ELF header is compatible with the running host. */
57 static inline int __attribute_used__
58 elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
60 /* We can only run RISC-V binaries. */
61 if (ehdr->e_machine != EM_RISCV)
62 return 0;
64 /* Ensure the library's floating-point ABI matches that of the running
65 system. For now we don't support mixing XLEN, so there's no need (or way)
66 to check it matches. */
67 #ifdef __riscv_float_abi_double
68 if ((ehdr->e_flags & EF_RISCV_FLOAT_ABI) != EF_RISCV_FLOAT_ABI_DOUBLE)
69 return 0;
70 #else
71 if ((ehdr->e_flags & EF_RISCV_FLOAT_ABI) != EF_RISCV_FLOAT_ABI_SOFT)
72 return 0;
73 #endif
75 return 1;
78 /* Return the run-time load address of the shared object. */
79 static inline ElfW(Addr)
80 elf_machine_load_address (void)
82 extern const ElfW(Ehdr) __ehdr_start attribute_hidden;
83 return (ElfW(Addr)) &__ehdr_start;
86 /* Return the link-time address of _DYNAMIC. */
87 static inline ElfW(Addr)
88 elf_machine_dynamic (void)
90 extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
91 return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address ();
94 #define STRINGXP(X) __STRING (X)
95 #define STRINGXV(X) STRINGV_ (X)
96 #define STRINGV_(...) # __VA_ARGS__
98 /* Initial entry point code for the dynamic linker.
99 The C function `_dl_start' is the real entry point;
100 its return value is the user program's entry point. */
102 #define RTLD_START asm (\
103 ".text\n\
104 " _RTLD_PROLOGUE (ENTRY_POINT) "\
105 mv a0, sp\n\
106 jal _dl_start\n\
107 " _RTLD_PROLOGUE (_dl_start_user) "\
108 # Stash user entry point in s0.\n\
109 mv s0, a0\n\
110 # Load the adjusted argument count.\n\
111 " STRINGXP (REG_L) " a1, 0(sp)\n\
112 # Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
113 " STRINGXP (REG_L) " a0, _rtld_local\n\
114 add a2, sp, " STRINGXP (SZREG) "\n\
115 sll a3, a1, " STRINGXP (PTRLOG) "\n\
116 add a3, a3, a2\n\
117 add a3, a3, " STRINGXP (SZREG) "\n\
118 # Stash the stack pointer in s1.\n\
119 mv s1, sp\n\
120 # Align stack to 128 bits for the _dl_init call.\n\
121 andi sp, sp,-16\n\
122 # Call the function to run the initializers.\n\
123 jal _dl_init\n\
124 # Restore the stack pointer for _start.\n\
125 mv sp, s1\n\
126 # Pass our finalizer function to _start.\n\
127 lla a0, _dl_fini\n\
128 # Jump to the user entry point.\n\
129 jr s0\n\
130 " _RTLD_EPILOGUE (ENTRY_POINT) \
131 _RTLD_EPILOGUE (_dl_start_user) "\
132 .previous" \
135 /* Names of the architecture-specific auditing callback functions. */
136 #define ARCH_LA_PLTENTER riscv_gnu_pltenter
137 #define ARCH_LA_PLTEXIT riscv_gnu_pltexit
139 /* Bias .got.plt entry by the offset requested by the PLT header. */
140 #define elf_machine_plt_value(map, reloc, value) (value)
142 static inline ElfW(Addr)
143 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
144 const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
145 const ElfW(Rela) *reloc,
146 ElfW(Addr) *reloc_addr, ElfW(Addr) value)
148 return *reloc_addr = value;
151 #endif /* !dl_machine_h */
153 #ifdef RESOLVE_MAP
155 static inline void
156 __attribute__ ((always_inline))
157 elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
158 void *const reloc_addr)
160 /* R_RISCV_RELATIVE might located in debug info section which might not
161 aligned to XLEN bytes. Also support relocations on unaligned offsets. */
162 ElfW(Addr) value = l_addr + reloc->r_addend;
163 memcpy (reloc_addr, &value, sizeof value);
166 /* Perform a relocation described by R_INFO at the location pointed to
167 by RELOC_ADDR. SYM is the relocation symbol specified by R_INFO and
168 MAP is the object containing the reloc. */
170 static inline void
171 __attribute__ ((always_inline))
172 elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
173 const ElfW(Rela) *reloc, const ElfW(Sym) *sym,
174 const struct r_found_version *version,
175 void *const reloc_addr, int skip_ifunc)
177 ElfW(Addr) r_info = reloc->r_info;
178 const unsigned long int r_type = ELFW (R_TYPE) (r_info);
179 ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
180 const ElfW(Sym) *const __attribute__ ((unused)) refsym = sym;
181 struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
182 ElfW(Addr) value = 0;
183 if (sym_map != NULL)
184 value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend;
186 if (sym != NULL
187 && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
188 && __glibc_likely (sym->st_shndx != SHN_UNDEF)
189 && __glibc_likely (!skip_ifunc))
190 value = elf_ifunc_invoke (value);
193 switch (r_type)
195 case R_RISCV_RELATIVE:
196 elf_machine_rela_relative (map->l_addr, reloc, addr_field);
197 break;
198 case R_RISCV_JUMP_SLOT:
199 case __WORDSIZE == 64 ? R_RISCV_64 : R_RISCV_32:
200 *addr_field = value;
201 break;
203 # ifndef RTLD_BOOTSTRAP
204 case __WORDSIZE == 64 ? R_RISCV_TLS_DTPMOD64 : R_RISCV_TLS_DTPMOD32:
205 if (sym_map)
206 *addr_field = sym_map->l_tls_modid;
207 break;
209 case __WORDSIZE == 64 ? R_RISCV_TLS_DTPREL64 : R_RISCV_TLS_DTPREL32:
210 if (sym != NULL)
211 *addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
212 break;
214 case __WORDSIZE == 64 ? R_RISCV_TLS_TPREL64 : R_RISCV_TLS_TPREL32:
215 if (sym != NULL)
217 CHECK_STATIC_TLS (map, sym_map);
218 *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
220 break;
222 case R_RISCV_COPY:
224 if (__glibc_unlikely (sym == NULL))
225 /* This can happen in trace mode if an object could not be
226 found. */
227 break;
229 /* Handle TLS copy relocations. */
230 if (__glibc_unlikely (ELFW (ST_TYPE) (sym->st_info) == STT_TLS))
232 /* There's nothing to do if the symbol is in .tbss. */
233 if (__glibc_likely (sym->st_value >= sym_map->l_tls_initimage_size))
234 break;
235 value += (ElfW(Addr)) sym_map->l_tls_initimage - sym_map->l_addr;
238 size_t size = sym->st_size;
239 if (__glibc_unlikely (sym->st_size != refsym->st_size))
241 const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
242 if (sym->st_size > refsym->st_size)
243 size = refsym->st_size;
244 if (sym->st_size > refsym->st_size || GLRO(dl_verbose))
245 _dl_error_printf ("\
246 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
247 rtld_progname ?: "<program name unknown>",
248 strtab + refsym->st_name);
251 memcpy (reloc_addr, (void *)value, size);
252 break;
255 case R_RISCV_IRELATIVE:
256 value = map->l_addr + reloc->r_addend;
257 if (__glibc_likely (!skip_ifunc))
258 value = elf_ifunc_invoke (value);
259 *addr_field = value;
260 break;
262 case R_RISCV_NONE:
263 break;
264 # endif /* !RTLD_BOOTSTRAP */
266 default:
267 _dl_reloc_bad_type (map, r_type, 0);
268 break;
272 static inline void
273 __attribute__ ((always_inline))
274 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
275 ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
276 int skip_ifunc)
278 ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
279 const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
281 /* Check for unexpected PLT reloc type. */
282 if (__glibc_likely (r_type == R_RISCV_JUMP_SLOT))
284 if (__glibc_unlikely (map->l_mach.plt == 0))
286 if (l_addr)
287 *reloc_addr += l_addr;
289 else
290 *reloc_addr = map->l_mach.plt;
292 else if (__glibc_unlikely (r_type == R_RISCV_IRELATIVE))
294 ElfW(Addr) value = map->l_addr + reloc->r_addend;
295 if (__glibc_likely (!skip_ifunc))
296 value = elf_ifunc_invoke (value);
297 *reloc_addr = value;
299 else
300 _dl_reloc_bad_type (map, r_type, 1);
303 /* Set up the loaded object described by L so its stub function
304 will jump to the on-demand fixup code __dl_runtime_resolve. */
306 static inline int
307 __attribute__ ((always_inline))
308 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
309 int lazy, int profile)
311 #ifndef RTLD_BOOTSTRAP
312 /* If using PLTs, fill in the first two entries of .got.plt. */
313 if (l->l_info[DT_JMPREL])
315 extern void _dl_runtime_resolve (void) __attribute__ ((visibility ("hidden")));
316 extern void _dl_runtime_profile (void) __attribute__ ((visibility ("hidden")));
317 ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
318 /* If a library is prelinked but we have to relocate anyway,
319 we have to be able to undo the prelinking of .got.plt.
320 The prelinker saved the address of .plt for us here. */
321 if (gotplt[1])
322 l->l_mach.plt = gotplt[1] + l->l_addr;
323 /* The gotplt[0] entry contains the address of a function which gets
324 called to get the address of a so far unresolved function and
325 jump to it. The profiling extension of the dynamic linker allows
326 to intercept the calls to collect information. In this case we
327 don't store the address in the GOT so that all future calls also
328 end in this function. */
329 #ifdef SHARED
330 if (profile != 0)
332 gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile;
334 if (GLRO(dl_profile) != NULL
335 && _dl_name_match_p (GLRO(dl_profile), l))
336 /* Say that we really want profiling and the timers are
337 started. */
338 GL(dl_profile_map) = l;
340 else
341 #endif
343 /* This function will get called to fix up the GOT entry
344 indicated by the offset on the stack, and then jump to
345 the resolved address. */
346 gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve;
348 gotplt[1] = (ElfW(Addr)) l;
351 if (l->l_type == lt_executable && l->l_scope != NULL)
353 /* The __global_pointer$ may not be defined by the linker if the
354 $gp register does not be used to access the global variable
355 in the executable program. Therefore, the search symbol is
356 set to a weak symbol to avoid we error out if the
357 __global_pointer$ is not found. */
358 ElfW(Sym) gp_sym = { 0 };
359 gp_sym.st_info = (unsigned char) ELFW (ST_INFO (STB_WEAK, STT_NOTYPE));
361 const ElfW(Sym) *ref = &gp_sym;
362 _dl_lookup_symbol_x ("__global_pointer$", l, &ref,
363 l->l_scope, NULL, 0, 0, NULL);
364 if (ref)
365 asm (
366 "mv gp, %0\n"
368 : "r" (ref->st_value)
371 #endif
372 return lazy;
375 #endif /* RESOLVE_MAP */