1 /* Machine-dependent ELF dynamic relocation inline functions.
2 Copyright (C) 2022-2023 Free Software Foundation, Inc.
3
4 This file is part of the GNU C Library.
5
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.
10
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.
15
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/>. */
19
20 #ifndef dl_machine_h
21 #define dl_machine_h
22
23 #define ELF_MACHINE_NAME "LoongArch"
24
25 #include <entry.h>
26 #include <elf/elf.h>
27 #include <sys/asm.h>
28 #include <dl-tls.h>
29 #include <dl-static-tls.h>
30 #include <dl-machine-rel.h>
31
32 #ifndef _RTLD_PROLOGUE
33 # define _RTLD_PROLOGUE(entry) \
34 ".globl\t" __STRING (entry) "\n\t" \
35 ".type\t" __STRING (entry) ", @function\n\t" \
36 CFI_STARTPROC "\n" \
37 __STRING (entry) ":\n"
38 #endif
39
40 #ifndef _RTLD_EPILOGUE
41 # define _RTLD_EPILOGUE(entry) \
42 CFI_ENDPROC "\n\t" \
43 ".size\t" __STRING (entry) ", . - " __STRING (entry) "\n"
44 #endif
45
46 #define ELF_MACHINE_JMP_SLOT R_LARCH_JUMP_SLOT
47 #define ELF_MACHINE_IRELATIVE R_LARCH_IRELATIVE
48
49 #define elf_machine_type_class(type) \
50 ((ELF_RTYPE_CLASS_PLT *((type) == ELF_MACHINE_JMP_SLOT)) \
51 | (ELF_RTYPE_CLASS_COPY *((type) == R_LARCH_COPY)))
52
53 #define ELF_MACHINE_NO_REL 1
54 #define ELF_MACHINE_NO_RELA 0
55
56 /* Return nonzero iff ELF header is compatible with the running host. */
57 static inline int
58 elf_machine_matches_host (const ElfW (Ehdr) *ehdr)
59 {
60 /* We can only run LoongArch binaries. */
61 if (ehdr->e_machine != EM_LOONGARCH)
62 return 0;
63
64 return 1;
65 }
66
67 /* Return the run-time load address of the shared object. */
68 static inline ElfW (Addr) elf_machine_load_address (void)
69 {
70 extern const ElfW(Ehdr) __ehdr_start attribute_hidden;
71 return (ElfW(Addr)) &__ehdr_start;
72 }
73
74 /* Return the link-time address of _DYNAMIC. */
75 static inline ElfW (Addr) elf_machine_dynamic (void)
76 {
77 extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
78 return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address ();
79 }
80
81 /* Initial entry point code for the dynamic linker.
82 The C function `_dl_start' is the real entry point;
83 its return value is the user program's entry point. */
84
85 #define RTLD_START asm (\
86 ".text\n\
87 " _RTLD_PROLOGUE (ENTRY_POINT) "\
88 .cfi_label .Ldummy \n\
89 " CFI_UNDEFINED (1) " \n\
90 or $a0, $sp, $zero \n\
91 bl _dl_start \n\
92 # Stash user entry point in s0. \n\
93 or $s0, $v0, $zero \n\
94 # Load the original argument count. \n\
95 ld.d $a1, $sp, 0 \n\
96 # Call _dl_init (struct link_map *main_map, int argc, \
97 char **argv, char **env) \n\
98 la $a0, _rtld_local \n\
99 ld.d $a0, $a0, 0 \n\
100 addi.d $a2, $sp, 8 \n\
101 slli.d $a3, $a1, 3 \n\
102 add.d $a3, $a3, $a2 \n\
103 addi.d $a3, $a3, 8 \n\
104 # Stash the stack pointer in s1.\n\
105 or $s1, $sp, $zero \n\
106 # Adjust $sp for 16-aligned \n\
107 bstrins.d $sp, $zero, 3, 0 \n\
108 # Call the function to run the initializers. \n\
109 bl _dl_init \n\
110 # Restore the stack pointer for _start.\n\
111 or $sp, $s1, $zero \n\
112 # Pass our finalizer function to _start. \n\
113 la $a0, _dl_fini \n\
114 # Jump to the user entry point. \n\
115 jirl $zero, $s0, 0 \n\
116 " _RTLD_EPILOGUE (ENTRY_POINT) "\
117 .previous");
118
119 /* Names of the architecture-specific auditing callback functions. */
120 #define ARCH_LA_PLTENTER loongarch_gnu_pltenter
121 #define ARCH_LA_PLTEXIT loongarch_gnu_pltexit
122
123 /* Bias .got.plt entry by the offset requested by the PLT header. */
124 #define elf_machine_plt_value(map, reloc, value) (value)
125
126 static inline ElfW (Addr)
127 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
128 const ElfW (Sym) *refsym, const ElfW (Sym) *sym,
129 const ElfW (Rela) *reloc, ElfW (Addr) *reloc_addr,
130 ElfW (Addr) value)
131 {
132 return *reloc_addr = value;
133 }
134
135 #endif /* !dl_machine_h */
136
137 #ifdef RESOLVE_MAP
138
139 /* Perform a relocation described by R_INFO at the location pointed to
140 by RELOC_ADDR. SYM is the relocation symbol specified by R_INFO and
141 MAP is the object containing the reloc. */
142
143 static inline void __attribute__ ((always_inline))
144 elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
145 const ElfW (Rela) *reloc,
146 const ElfW (Sym) *sym,
147 const struct r_found_version *version,
148 void *const reloc_addr, int skip_ifunc)
149 {
150 ElfW (Addr) r_info = reloc->r_info;
151 const unsigned long int r_type = ELFW (R_TYPE) (r_info);
152 ElfW (Addr) *addr_field = (ElfW (Addr) *) reloc_addr;
153 const ElfW (Sym) *const __attribute__ ((unused)) refsym = sym;
154 struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
155 ElfW (Addr) value = 0;
156 if (sym_map != NULL)
157 value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend;
158
159 if (sym != NULL
160 && __glibc_unlikely (ELFW (ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
161 && __glibc_likely (sym->st_shndx != SHN_UNDEF)
162 && __glibc_likely (!skip_ifunc))
163 value = ((ElfW (Addr) (*) (int)) value) (GLRO (dl_hwcap));
164
165 switch (r_type)
166 {
167
168 case R_LARCH_JUMP_SLOT:
169 case __WORDSIZE == 64 ? R_LARCH_64 : R_LARCH_32:
170 *addr_field = value;
171 break;
172
173 case R_LARCH_NONE:
174 break;
175
176 #ifndef RTLD_BOOTSTRAP
177 case __WORDSIZE == 64 ? R_LARCH_TLS_DTPMOD64 : R_LARCH_TLS_DTPMOD32:
178 *addr_field = sym_map->l_tls_modid;
179 break;
180
181 case __WORDSIZE == 64 ? R_LARCH_TLS_DTPREL64 : R_LARCH_TLS_DTPREL32:
182 *addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
183 break;
184
185 case __WORDSIZE == 64 ? R_LARCH_TLS_TPREL64 : R_LARCH_TLS_TPREL32:
186 CHECK_STATIC_TLS (map, sym_map);
187 *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
188 break;
189
190 case R_LARCH_COPY:
191 {
192 if (sym == NULL)
193 /* This can happen in trace mode if an object could not be
194 found. */
195 break;
196 if (__glibc_unlikely (sym->st_size > refsym->st_size)
197 || (__glibc_unlikely (sym->st_size < refsym->st_size)
198 && GLRO(dl_verbose)))
199 {
200 const char *strtab;
201
202 strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
203 _dl_error_printf ("\
204 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
205 rtld_progname ?: "<program name unknown>",
206 strtab + refsym->st_name);
207 }
208 memcpy (reloc_addr, (void *) value,
209 MIN (sym->st_size, refsym->st_size));
210 break;
211 }
212
213 case R_LARCH_RELATIVE:
214 *addr_field = map->l_addr + reloc->r_addend;
215 break;
216
217 case R_LARCH_IRELATIVE:
218 value = map->l_addr + reloc->r_addend;
219 if (__glibc_likely (!skip_ifunc))
220 value = ((ElfW (Addr) (*) (void)) value) ();
221 *addr_field = value;
222 break;
223
224 #endif
225
226 default:
227 _dl_reloc_bad_type (map, r_type, 0);
228 break;
229 }
230 }
231
232 static inline void __attribute__ ((always_inline))
233 elf_machine_rela_relative (ElfW (Addr) l_addr, const ElfW (Rela) *reloc,
234 void *const reloc_addr)
235 {
236 *(ElfW (Addr) *) reloc_addr = l_addr + reloc->r_addend;
237 }
238
239 static inline void __attribute__ ((always_inline))
240 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
241 ElfW (Addr) l_addr,
242 const ElfW (Rela) *reloc, int skip_ifunc)
243 {
244 ElfW (Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
245 const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
246
247 /* Check for unexpected PLT reloc type. */
248 if (__glibc_likely (r_type == R_LARCH_JUMP_SLOT))
249 {
250 if (__glibc_unlikely (map->l_mach.plt == 0))
251 {
252 if (l_addr)
253 *reloc_addr += l_addr;
254 }
255 else
256 *reloc_addr = map->l_mach.plt;
257 }
258 else
259 _dl_reloc_bad_type (map, r_type, 1);
260 }
261
262 /* Set up the loaded object described by L so its stub function
263 will jump to the on-demand fixup code __dl_runtime_resolve. */
264
265 static inline int __attribute__ ((always_inline))
266 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
267 int lazy, int profile)
268 {
269 #ifndef RTLD_BOOTSTRAP
270 /* If using PLTs, fill in the first two entries of .got.plt. */
271 if (l->l_info[DT_JMPREL])
272 {
273 #if HAVE_LOONGARCH_VEC_ASM && !defined __loongarch_soft_float
274 extern void _dl_runtime_resolve_lasx (void) attribute_hidden;
275 extern void _dl_runtime_resolve_lsx (void) attribute_hidden;
276 #endif
277 extern void _dl_runtime_resolve (void) attribute_hidden;
278 extern void _dl_runtime_profile (void) attribute_hidden;
279
280 ElfW (Addr) *gotplt = (ElfW (Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
281
282 /* The got[0] entry contains the address of a function which gets
283 called to get the address of a so far unresolved function and
284 jump to it. The profiling extension of the dynamic linker allows
285 to intercept the calls to collect information. In this case we
286 don't store the address in the GOT so that all future calls also
287 end in this function. */
288 if (profile != 0)
289 {
290 gotplt[0] = (ElfW(Addr)) &_dl_runtime_profile;
291
292 if (GLRO(dl_profile) != NULL
293 && _dl_name_match_p (GLRO(dl_profile), l))
294 /* Say that we really want profiling and the timers are
295 started. */
296 GL(dl_profile_map) = l;
297 }
298 else
299 {
300 /* This function will get called to fix up the GOT entry
301 indicated by the offset on the stack, and then jump to
302 the resolved address. */
303 #if HAVE_LOONGARCH_VEC_ASM && !defined __loongarch_soft_float
304 if (SUPPORT_LASX)
305 gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve_lasx;
306 else if (SUPPORT_LSX)
307 gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve_lsx;
308 else
309 #endif
310 gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve;
311 }
312 gotplt[1] = (ElfW (Addr)) l;
313 }
314 #endif
315
316 return lazy;
317 }
318
319 #endif /* RESOLVE_MAP */