1 /* Machine-dependent ELF dynamic relocation inline functions.
2 Copyright (C) 2022 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
elf_machine_matches_host(const ElfW (Ehdr)* ehdr)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. */
elf_machine_load_address(void)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. */
elf_machine_dynamic(void)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)
elf_machine_fixup_plt(struct link_map * map,lookup_t t,const ElfW (Sym)* refsym,const ElfW (Sym)* sym,const ElfW (Rela)* reloc,ElfW (Addr)* reloc_addr,ElfW (Addr)value)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))
elf_machine_rela(struct link_map * map,struct r_scope_elem * scope[],const ElfW (Rela)* reloc,const ElfW (Sym)* sym,const struct r_found_version * version,void * const reloc_addr,int skip_ifunc)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:
170 R_LARCH_32:
171 *addr_field = value;
172 break;
173
174 case R_LARCH_NONE:
175 break;
176
177 #ifndef RTLD_BOOTSTRAP
178 case __WORDSIZE == 64 ? R_LARCH_TLS_DTPMOD64:
179 R_LARCH_TLS_DTPMOD32:
180 *addr_field = sym_map->l_tls_modid;
181 break;
182
183 case __WORDSIZE == 64 ? R_LARCH_TLS_DTPREL64:
184 R_LARCH_TLS_DTPREL32:
185 *addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
186 break;
187
188 case __WORDSIZE == 64 ? R_LARCH_TLS_TPREL64:
189 R_LARCH_TLS_TPREL32:
190 CHECK_STATIC_TLS (map, sym_map);
191 *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
192 break;
193
194 case R_LARCH_COPY:
195 {
196 if (sym == NULL)
197 /* This can happen in trace mode if an object could not be
198 found. */
199 break;
200 if (__glibc_unlikely (sym->st_size > refsym->st_size)
201 || (__glibc_unlikely (sym->st_size < refsym->st_size)
202 && GLRO(dl_verbose)))
203 {
204 const char *strtab;
205
206 strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
207 _dl_error_printf ("\
208 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
209 rtld_progname ?: "<program name unknown>",
210 strtab + refsym->st_name);
211 }
212 memcpy (reloc_addr, (void *) value,
213 MIN (sym->st_size, refsym->st_size));
214 break;
215 }
216
217 case R_LARCH_RELATIVE:
218 *addr_field = map->l_addr + reloc->r_addend;
219 break;
220
221 case R_LARCH_IRELATIVE:
222 value = map->l_addr + reloc->r_addend;
223 if (__glibc_likely (!skip_ifunc))
224 value = ((ElfW (Addr) (*) (void)) value) ();
225 *addr_field = value;
226 break;
227
228 #endif
229
230 default:
231 _dl_reloc_bad_type (map, r_type, 0);
232 break;
233 }
234 }
235
236 static inline void __attribute__ ((always_inline))
elf_machine_rela_relative(ElfW (Addr)l_addr,const ElfW (Rela)* reloc,void * const reloc_addr)237 elf_machine_rela_relative (ElfW (Addr) l_addr, const ElfW (Rela) *reloc,
238 void *const reloc_addr)
239 {
240 *(ElfW (Addr) *) reloc_addr = l_addr + reloc->r_addend;
241 }
242
243 static inline void __attribute__ ((always_inline))
elf_machine_lazy_rel(struct link_map * map,struct r_scope_elem * scope[],ElfW (Addr)l_addr,const ElfW (Rela)* reloc,int skip_ifunc)244 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
245 ElfW (Addr) l_addr,
246 const ElfW (Rela) *reloc, int skip_ifunc)
247 {
248 ElfW (Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
249 const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
250
251 /* Check for unexpected PLT reloc type. */
252 if (__glibc_likely (r_type == R_LARCH_JUMP_SLOT))
253 {
254 if (__glibc_unlikely (map->l_mach.plt == 0))
255 {
256 if (l_addr)
257 *reloc_addr += l_addr;
258 }
259 else
260 *reloc_addr = map->l_mach.plt;
261 }
262 else
263 _dl_reloc_bad_type (map, r_type, 1);
264 }
265
266 /* Set up the loaded object described by L so its stub function
267 will jump to the on-demand fixup code __dl_runtime_resolve. */
268
269 static inline int __attribute__ ((always_inline))
elf_machine_runtime_setup(struct link_map * l,struct r_scope_elem * scope[],int lazy,int profile)270 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
271 int lazy, int profile)
272 {
273 #ifndef RTLD_BOOTSTRAP
274 /* If using PLTs, fill in the first two entries of .got.plt. */
275 if (l->l_info[DT_JMPREL])
276 {
277 extern void _dl_runtime_resolve (void)
278 __attribute__ ((visibility ("hidden")));
279 ElfW (Addr) *gotplt = (ElfW (Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
280 gotplt[0] = (ElfW (Addr)) & _dl_runtime_resolve;
281 gotplt[1] = (ElfW (Addr)) l;
282 }
283 #endif
284
285 return lazy;
286 }
287
288 #endif /* RESOLVE_MAP */
289