1 /* Machine-dependent ELF dynamic relocation inline functions.  RISC-V version.
2    Copyright (C) 2011-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
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.
9 
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.
14 
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/>.  */
18 
19 #ifndef dl_machine_h
20 #define dl_machine_h
21 
22 #define ELF_MACHINE_NAME "RISC-V"
23 
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>
31 
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
38 
39 #ifndef _RTLD_EPILOGUE
40 # define _RTLD_EPILOGUE(entry)						\
41 	".size\t" __STRING (entry) ", . - " __STRING (entry) "\n\t"
42 #endif
43 
44 #define ELF_MACHINE_JMP_SLOT R_RISCV_JUMP_SLOT
45 
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)))
55 
56 /* Return nonzero iff ELF header is compatible with the running host.  */
57 static inline int __attribute_used__
elf_machine_matches_host(const ElfW (Ehdr)* ehdr)58 elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
59 {
60   /* We can only run RISC-V binaries.  */
61   if (ehdr->e_machine != EM_RISCV)
62     return 0;
63 
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
74 
75   return 1;
76 }
77 
78 /* Return the run-time load address of the shared object.  */
79 static inline ElfW(Addr)
elf_machine_load_address(void)80 elf_machine_load_address (void)
81 {
82   extern const ElfW(Ehdr) __ehdr_start attribute_hidden;
83   return (ElfW(Addr)) &__ehdr_start;
84 }
85 
86 /* Return the link-time address of _DYNAMIC.  */
87 static inline ElfW(Addr)
elf_machine_dynamic(void)88 elf_machine_dynamic (void)
89 {
90   extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
91   return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address ();
92 }
93 
94 #define STRINGXP(X) __STRING (X)
95 #define STRINGXV(X) STRINGV_ (X)
96 #define STRINGV_(...) # __VA_ARGS__
97 
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.  */
101 
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 	# See if we were run as a command with the executable file\n\
111 	# name as an extra leading argument.\n\
112 	lw a0, _dl_skip_args\n\
113 	# Load the original argument count.\n\
114 	" STRINGXP (REG_L) " a1, 0(sp)\n\
115 	# Subtract _dl_skip_args from it.\n\
116 	sub a1, a1, a0\n\
117 	# Adjust the stack pointer to skip _dl_skip_args words.\n\
118 	sll a0, a0, " STRINGXP (PTRLOG) "\n\
119 	add sp, sp, a0\n\
120 	# Save back the modified argument count.\n\
121 	" STRINGXP (REG_S) " a1, 0(sp)\n\
122 	# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
123 	" STRINGXP (REG_L) " a0, _rtld_local\n\
124 	add a2, sp, " STRINGXP (SZREG) "\n\
125 	sll a3, a1, " STRINGXP (PTRLOG) "\n\
126 	add a3, a3, a2\n\
127 	add a3, a3, " STRINGXP (SZREG) "\n\
128 	# Stash the stack pointer in s1.\n\
129 	mv s1, sp\n\
130 	# Align stack to 128 bits for the _dl_init call.\n\
131 	andi sp, sp,-16\n\
132 	# Call the function to run the initializers.\n\
133 	jal _dl_init\n\
134 	# Restore the stack pointer for _start.\n\
135 	mv sp, s1\n\
136 	# Pass our finalizer function to _start.\n\
137 	lla a0, _dl_fini\n\
138 	# Jump to the user entry point.\n\
139 	jr s0\n\
140 	" _RTLD_EPILOGUE (ENTRY_POINT) \
141 	  _RTLD_EPILOGUE (_dl_start_user) "\
142 	.previous" \
143 );
144 
145 /* Names of the architecture-specific auditing callback functions.  */
146 #define ARCH_LA_PLTENTER riscv_gnu_pltenter
147 #define ARCH_LA_PLTEXIT riscv_gnu_pltexit
148 
149 /* Bias .got.plt entry by the offset requested by the PLT header.  */
150 #define elf_machine_plt_value(map, reloc, value) (value)
151 
152 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)153 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
154 		       const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
155 		       const ElfW(Rela) *reloc,
156 		       ElfW(Addr) *reloc_addr, ElfW(Addr) value)
157 {
158   return *reloc_addr = value;
159 }
160 
161 #endif /* !dl_machine_h */
162 
163 #ifdef RESOLVE_MAP
164 
165 /* Perform a relocation described by R_INFO at the location pointed to
166    by RELOC_ADDR.  SYM is the relocation symbol specified by R_INFO and
167    MAP is the object containing the reloc.  */
168 
169 static inline void
170 __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)171 elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
172 		  const ElfW(Rela) *reloc, const ElfW(Sym) *sym,
173 		  const struct r_found_version *version,
174 		  void *const reloc_addr, int skip_ifunc)
175 {
176   ElfW(Addr) r_info = reloc->r_info;
177   const unsigned long int r_type = ELFW (R_TYPE) (r_info);
178   ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
179   const ElfW(Sym) *const __attribute__ ((unused)) refsym = sym;
180   struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
181   ElfW(Addr) value = 0;
182   if (sym_map != NULL)
183     value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend;
184 
185   if (sym != NULL
186       && __glibc_unlikely (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
187       && __glibc_likely (sym->st_shndx != SHN_UNDEF)
188       && __glibc_likely (!skip_ifunc))
189     value = elf_ifunc_invoke (value);
190 
191 
192   switch (r_type)
193     {
194 #ifndef RTLD_BOOTSTRAP
195     case __WORDSIZE == 64 ? R_RISCV_TLS_DTPMOD64 : R_RISCV_TLS_DTPMOD32:
196       if (sym_map)
197 	*addr_field = sym_map->l_tls_modid;
198       break;
199 
200     case __WORDSIZE == 64 ? R_RISCV_TLS_DTPREL64 : R_RISCV_TLS_DTPREL32:
201       if (sym != NULL)
202 	*addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
203       break;
204 
205     case __WORDSIZE == 64 ? R_RISCV_TLS_TPREL64 : R_RISCV_TLS_TPREL32:
206       if (sym != NULL)
207 	{
208 	  CHECK_STATIC_TLS (map, sym_map);
209 	  *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
210 	}
211       break;
212 
213     case R_RISCV_COPY:
214       {
215 	if (__glibc_unlikely (sym == NULL))
216 	  /* This can happen in trace mode if an object could not be
217 	     found.  */
218 	  break;
219 
220 	/* Handle TLS copy relocations.  */
221 	if (__glibc_unlikely (ELFW (ST_TYPE) (sym->st_info) == STT_TLS))
222 	  {
223 	    /* There's nothing to do if the symbol is in .tbss.  */
224 	    if (__glibc_likely (sym->st_value >= sym_map->l_tls_initimage_size))
225 	      break;
226 	    value += (ElfW(Addr)) sym_map->l_tls_initimage - sym_map->l_addr;
227 	  }
228 
229 	size_t size = sym->st_size;
230 	if (__glibc_unlikely (sym->st_size != refsym->st_size))
231 	  {
232 	    const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
233 	    if (sym->st_size > refsym->st_size)
234 	      size = refsym->st_size;
235 	    if (sym->st_size > refsym->st_size || GLRO(dl_verbose))
236 	      _dl_error_printf ("\
237   %s: Symbol `%s' has different size in shared object, consider re-linking\n",
238 				rtld_progname ?: "<program name unknown>",
239 				strtab + refsym->st_name);
240 	  }
241 
242 	memcpy (reloc_addr, (void *)value, size);
243 	break;
244       }
245 #endif
246 
247 #if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
248     case R_RISCV_RELATIVE:
249       {
250 # if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
251 	/* This is defined in rtld.c, but nowhere in the static libc.a;
252 	   make the reference weak so static programs can still link.
253 	   This declaration cannot be done when compiling rtld.c
254 	   (i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
255 	   common defn for _dl_rtld_map, which is incompatible with a
256 	   weak decl in the same file.  */
257 #  ifndef SHARED
258 	weak_extern (GL(dl_rtld_map));
259 #  endif
260 	if (map != &GL(dl_rtld_map)) /* Already done in rtld itself.  */
261 # endif
262 	  *addr_field = map->l_addr + reloc->r_addend;
263       break;
264     }
265 #endif
266 
267     case R_RISCV_IRELATIVE:
268       value = map->l_addr + reloc->r_addend;
269       if (__glibc_likely (!skip_ifunc))
270         value = elf_ifunc_invoke (value);
271       *addr_field = value;
272       break;
273 
274     case R_RISCV_JUMP_SLOT:
275     case __WORDSIZE == 64 ? R_RISCV_64 : R_RISCV_32:
276       *addr_field = value;
277       break;
278 
279     case R_RISCV_NONE:
280       break;
281 
282     default:
283       _dl_reloc_bad_type (map, r_type, 0);
284       break;
285     }
286 }
287 
288 static inline void
289 __attribute__ ((always_inline))
elf_machine_rela_relative(ElfW (Addr)l_addr,const ElfW (Rela)* reloc,void * const reloc_addr)290 elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
291 			  void *const reloc_addr)
292 {
293   *(ElfW(Addr) *) reloc_addr = l_addr + reloc->r_addend;
294 }
295 
296 static inline void
297 __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)298 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
299 		      ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
300 		      int skip_ifunc)
301 {
302   ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
303   const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
304 
305   /* Check for unexpected PLT reloc type.  */
306   if (__glibc_likely (r_type == R_RISCV_JUMP_SLOT))
307     {
308       if (__glibc_unlikely (map->l_mach.plt == 0))
309 	{
310 	  if (l_addr)
311 	    *reloc_addr += l_addr;
312 	}
313       else
314 	*reloc_addr = map->l_mach.plt;
315     }
316   else if (__glibc_unlikely (r_type == R_RISCV_IRELATIVE))
317     {
318       ElfW(Addr) value = map->l_addr + reloc->r_addend;
319       if (__glibc_likely (!skip_ifunc))
320         value = elf_ifunc_invoke (value);
321       *reloc_addr = value;
322     }
323   else
324     _dl_reloc_bad_type (map, r_type, 1);
325 }
326 
327 /* Set up the loaded object described by L so its stub function
328    will jump to the on-demand fixup code __dl_runtime_resolve.  */
329 
330 static inline int
331 __attribute__ ((always_inline))
elf_machine_runtime_setup(struct link_map * l,struct r_scope_elem * scope[],int lazy,int profile)332 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
333 			   int lazy, int profile)
334 {
335 #ifndef RTLD_BOOTSTRAP
336   /* If using PLTs, fill in the first two entries of .got.plt.  */
337   if (l->l_info[DT_JMPREL])
338     {
339       extern void _dl_runtime_resolve (void) __attribute__ ((visibility ("hidden")));
340       ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
341       /* If a library is prelinked but we have to relocate anyway,
342 	 we have to be able to undo the prelinking of .got.plt.
343 	 The prelinker saved the address of .plt for us here.  */
344       if (gotplt[1])
345 	l->l_mach.plt = gotplt[1] + l->l_addr;
346       gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve;
347       gotplt[1] = (ElfW(Addr)) l;
348     }
349 
350   if (l->l_type == lt_executable)
351     {
352       /* The __global_pointer$ may not be defined by the linker if the
353 	 $gp register does not be used to access the global variable
354 	 in the executable program. Therefore, the search symbol is
355 	 set to a weak symbol to avoid we error out if the
356 	 __global_pointer$ is not found.  */
357       ElfW(Sym) gp_sym = { 0 };
358       gp_sym.st_info = (unsigned char) ELFW (ST_INFO (STB_WEAK, STT_NOTYPE));
359 
360       const ElfW(Sym) *ref = &gp_sym;
361       _dl_lookup_symbol_x ("__global_pointer$", l, &ref,
362 			   l->l_scope, NULL, 0, 0, NULL);
363       if (ref)
364         asm (
365           "mv gp, %0\n"
366           :
367           : "r" (ref->st_value)
368         );
369     }
370 #endif
371   return lazy;
372 }
373 
374 #endif /* RESOLVE_MAP */
375