(root)/
glibc-2.38/
sysdeps/
riscv/
dl-machine.h
       1  /* Machine-dependent ELF dynamic relocation inline functions.  RISC-V version.
       2     Copyright (C) 2011-2023 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__
      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)
      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)
      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  	# 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" \
     133  );
     134  
     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
     138  
     139  /* Bias .got.plt entry by the offset requested by the PLT header.  */
     140  #define elf_machine_plt_value(map, reloc, value) (value)
     141  
     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)
     147  {
     148    return *reloc_addr = value;
     149  }
     150  
     151  #endif /* !dl_machine_h */
     152  
     153  #ifdef RESOLVE_MAP
     154  
     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)
     159  {
     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);
     164  }
     165  
     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.  */
     169  
     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)
     176  {
     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;
     185  
     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);
     191  
     192  
     193    switch (r_type)
     194      {
     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;
     202  
     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;
     208  
     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;
     213  
     214      case __WORDSIZE == 64 ? R_RISCV_TLS_TPREL64 : R_RISCV_TLS_TPREL32:
     215        if (sym != NULL)
     216  	{
     217  	  CHECK_STATIC_TLS (map, sym_map);
     218  	  *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
     219  	}
     220        break;
     221  
     222      case R_RISCV_COPY:
     223        {
     224  	if (__glibc_unlikely (sym == NULL))
     225  	  /* This can happen in trace mode if an object could not be
     226  	     found.  */
     227  	  break;
     228  
     229  	/* Handle TLS copy relocations.  */
     230  	if (__glibc_unlikely (ELFW (ST_TYPE) (sym->st_info) == STT_TLS))
     231  	  {
     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;
     236  	  }
     237  
     238  	size_t size = sym->st_size;
     239  	if (__glibc_unlikely (sym->st_size != refsym->st_size))
     240  	  {
     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);
     249  	  }
     250  
     251  	memcpy (reloc_addr, (void *)value, size);
     252  	break;
     253        }
     254  
     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;
     261  
     262      case R_RISCV_NONE:
     263        break;
     264  # endif /* !RTLD_BOOTSTRAP */
     265  
     266      default:
     267        _dl_reloc_bad_type (map, r_type, 0);
     268        break;
     269      }
     270  }
     271  
     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)
     277  {
     278    ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
     279    const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
     280  
     281    /* Check for unexpected PLT reloc type.  */
     282    if (__glibc_likely (r_type == R_RISCV_JUMP_SLOT))
     283      {
     284        if (__glibc_unlikely (map->l_mach.plt == 0))
     285  	{
     286  	  if (l_addr)
     287  	    *reloc_addr += l_addr;
     288  	}
     289        else
     290  	*reloc_addr = map->l_mach.plt;
     291      }
     292    else if (__glibc_unlikely (r_type == R_RISCV_IRELATIVE))
     293      {
     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;
     298      }
     299    else
     300      _dl_reloc_bad_type (map, r_type, 1);
     301  }
     302  
     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.  */
     305  
     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)
     310  {
     311  #ifndef RTLD_BOOTSTRAP
     312    /* If using PLTs, fill in the first two entries of .got.plt.  */
     313    if (l->l_info[DT_JMPREL])
     314      {
     315        extern void _dl_runtime_resolve (void) __attribute__ ((visibility ("hidden")));
     316        ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
     317        /* If a library is prelinked but we have to relocate anyway,
     318  	 we have to be able to undo the prelinking of .got.plt.
     319  	 The prelinker saved the address of .plt for us here.  */
     320        if (gotplt[1])
     321  	l->l_mach.plt = gotplt[1] + l->l_addr;
     322        gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve;
     323        gotplt[1] = (ElfW(Addr)) l;
     324      }
     325  
     326    if (l->l_type == lt_executable)
     327      {
     328        /* The __global_pointer$ may not be defined by the linker if the
     329  	 $gp register does not be used to access the global variable
     330  	 in the executable program. Therefore, the search symbol is
     331  	 set to a weak symbol to avoid we error out if the
     332  	 __global_pointer$ is not found.  */
     333        ElfW(Sym) gp_sym = { 0 };
     334        gp_sym.st_info = (unsigned char) ELFW (ST_INFO (STB_WEAK, STT_NOTYPE));
     335  
     336        const ElfW(Sym) *ref = &gp_sym;
     337        _dl_lookup_symbol_x ("__global_pointer$", l, &ref,
     338  			   l->l_scope, NULL, 0, 0, NULL);
     339        if (ref)
     340          asm (
     341            "mv gp, %0\n"
     342            :
     343            : "r" (ref->st_value)
     344          );
     345      }
     346  #endif
     347    return lazy;
     348  }
     349  
     350  #endif /* RESOLVE_MAP */