Blame sysdeps/riscv/dl-machine.h

Packit 6c4009
/* Machine-dependent ELF dynamic relocation inline functions.  RISC-V version.
Packit 6c4009
   Copyright (C) 2011-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library.  If not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#ifndef dl_machine_h
Packit 6c4009
#define dl_machine_h
Packit 6c4009
Packit 6c4009
#define ELF_MACHINE_NAME "RISC-V"
Packit 6c4009
Packit 6c4009
#include <entry.h>
Packit 6c4009
#include <elf/elf.h>
Packit 6c4009
#include <sys/asm.h>
Packit 6c4009
#include <dl-tls.h>
Packit 6c4009
Packit 6c4009
#ifndef _RTLD_PROLOGUE
Packit 6c4009
# define _RTLD_PROLOGUE(entry)						\
Packit 6c4009
	".globl\t" __STRING (entry) "\n\t"				\
Packit 6c4009
	".type\t" __STRING (entry) ", @function\n"			\
Packit 6c4009
	__STRING (entry) ":\n\t"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef _RTLD_EPILOGUE
Packit 6c4009
# define _RTLD_EPILOGUE(entry)						\
Packit 6c4009
	".size\t" __STRING (entry) ", . - " __STRING (entry) "\n\t"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#define ELF_MACHINE_JMP_SLOT R_RISCV_JUMP_SLOT
Packit 6c4009
Packit 6c4009
#define elf_machine_type_class(type)				\
Packit 6c4009
  ((ELF_RTYPE_CLASS_PLT * ((type) == ELF_MACHINE_JMP_SLOT	\
Packit 6c4009
     || (__WORDSIZE == 32 && (type) == R_RISCV_TLS_DTPREL32)	\
Packit 6c4009
     || (__WORDSIZE == 32 && (type) == R_RISCV_TLS_DTPMOD32)	\
Packit 6c4009
     || (__WORDSIZE == 32 && (type) == R_RISCV_TLS_TPREL32)	\
Packit 6c4009
     || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_DTPREL64)	\
Packit 6c4009
     || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_DTPMOD64)	\
Packit 6c4009
     || (__WORDSIZE == 64 && (type) == R_RISCV_TLS_TPREL64)))	\
Packit 6c4009
   | (ELF_RTYPE_CLASS_COPY * ((type) == R_RISCV_COPY)))
Packit 6c4009
Packit 6c4009
#define ELF_MACHINE_NO_REL 1
Packit 6c4009
#define ELF_MACHINE_NO_RELA 0
Packit 6c4009
Packit 6c4009
/* Return nonzero iff ELF header is compatible with the running host.  */
Packit 6c4009
static inline int __attribute_used__
Packit 6c4009
elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
Packit 6c4009
{
Packit 6c4009
  /* We can only run RISC-V binaries.  */
Packit 6c4009
  if (ehdr->e_machine != EM_RISCV)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  /* Ensure the library's floating-point ABI matches that of the running
Packit 6c4009
     system.  For now we don't support mixing XLEN, so there's no need (or way)
Packit 6c4009
     to check it matches.  */
Packit 6c4009
#ifdef __riscv_float_abi_double
Packit 6c4009
  if ((ehdr->e_flags & EF_RISCV_FLOAT_ABI) != EF_RISCV_FLOAT_ABI_DOUBLE)
Packit 6c4009
    return 0;
Packit 6c4009
#else
Packit 6c4009
  if ((ehdr->e_flags & EF_RISCV_FLOAT_ABI) != EF_RISCV_FLOAT_ABI_SOFT)
Packit 6c4009
    return 0;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return the link-time address of _DYNAMIC.  */
Packit 6c4009
static inline ElfW(Addr)
Packit 6c4009
elf_machine_dynamic (void)
Packit 6c4009
{
Packit 6c4009
  extern ElfW(Addr) _GLOBAL_OFFSET_TABLE_ __attribute__ ((visibility ("hidden")));
Packit 6c4009
  return _GLOBAL_OFFSET_TABLE_;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#define STRINGXP(X) __STRING (X)
Packit 6c4009
#define STRINGXV(X) STRINGV_ (X)
Packit 6c4009
#define STRINGV_(...) # __VA_ARGS__
Packit 6c4009
Packit 6c4009
/* Return the run-time load address of the shared object.  */
Packit 6c4009
static inline ElfW(Addr)
Packit 6c4009
elf_machine_load_address (void)
Packit 6c4009
{
Packit 6c4009
  ElfW(Addr) load_addr;
Packit 6c4009
  asm ("lla %0, _DYNAMIC" : "=r" (load_addr));
Packit 6c4009
  return load_addr - elf_machine_dynamic ();
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Initial entry point code for the dynamic linker.
Packit 6c4009
   The C function `_dl_start' is the real entry point;
Packit 6c4009
   its return value is the user program's entry point.  */
Packit 6c4009
Packit 6c4009
#define RTLD_START asm (\
Packit 6c4009
	".text\n\
Packit 6c4009
	" _RTLD_PROLOGUE (ENTRY_POINT) "\
Packit 6c4009
	mv a0, sp\n\
Packit 6c4009
	jal _dl_start\n\
Packit 6c4009
	# Stash user entry point in s0.\n\
Packit 6c4009
	mv s0, a0\n\
Packit 6c4009
	# See if we were run as a command with the executable file\n\
Packit 6c4009
	# name as an extra leading argument.\n\
Packit 6c4009
	lw a0, _dl_skip_args\n\
Packit 6c4009
	# Load the original argument count.\n\
Packit 6c4009
	" STRINGXP (REG_L) " a1, 0(sp)\n\
Packit 6c4009
	# Subtract _dl_skip_args from it.\n\
Packit 6c4009
	sub a1, a1, a0\n\
Packit 6c4009
	# Adjust the stack pointer to skip _dl_skip_args words.\n\
Packit 6c4009
	sll a0, a0, " STRINGXP (PTRLOG) "\n\
Packit 6c4009
	add sp, sp, a0\n\
Packit 6c4009
	# Save back the modified argument count.\n\
Packit 6c4009
	" STRINGXP (REG_S) " a1, 0(sp)\n\
Packit 6c4009
	# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
Packit 6c4009
	" STRINGXP (REG_L) " a0, _rtld_local\n\
Packit 6c4009
	add a2, sp, " STRINGXP (SZREG) "\n\
Packit 6c4009
	sll a3, a1, " STRINGXP (PTRLOG) "\n\
Packit 6c4009
	add a3, a3, a2\n\
Packit 6c4009
	add a3, a3, " STRINGXP (SZREG) "\n\
Packit 6c4009
	# Call the function to run the initializers.\n\
Packit 6c4009
	jal _dl_init\n\
Packit 6c4009
	# Pass our finalizer function to _start.\n\
Packit 6c4009
	lla a0, _dl_fini\n\
Packit 6c4009
	# Jump to the user entry point.\n\
Packit 6c4009
	jr s0\n\
Packit 6c4009
	" _RTLD_EPILOGUE (ENTRY_POINT) "\
Packit 6c4009
	.previous" \
Packit 6c4009
);
Packit 6c4009
Packit 6c4009
/* Names of the architecture-specific auditing callback functions.  */
Packit 6c4009
#define ARCH_LA_PLTENTER riscv_gnu_pltenter
Packit 6c4009
#define ARCH_LA_PLTEXIT riscv_gnu_pltexit
Packit 6c4009
Packit 6c4009
/* Bias .got.plt entry by the offset requested by the PLT header.  */
Packit 6c4009
#define elf_machine_plt_value(map, reloc, value) (value)
Packit 6c4009
Packit 6c4009
static inline ElfW(Addr)
Packit 6c4009
elf_machine_fixup_plt (struct link_map *map, lookup_t t,
Packit 6c4009
		       const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
Packit 6c4009
		       const ElfW(Rela) *reloc,
Packit 6c4009
		       ElfW(Addr) *reloc_addr, ElfW(Addr) value)
Packit 6c4009
{
Packit 6c4009
  return *reloc_addr = value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#endif /* !dl_machine_h */
Packit 6c4009
Packit 6c4009
#ifdef RESOLVE_MAP
Packit 6c4009
Packit 6c4009
/* Perform a relocation described by R_INFO at the location pointed to
Packit 6c4009
   by RELOC_ADDR.  SYM is the relocation symbol specified by R_INFO and
Packit 6c4009
   MAP is the object containing the reloc.  */
Packit 6c4009
Packit 6c4009
auto inline void
Packit 6c4009
__attribute__ ((always_inline))
Packit 6c4009
elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
Packit 6c4009
		  const ElfW(Sym) *sym, const struct r_found_version *version,
Packit 6c4009
		  void *const reloc_addr, int skip_ifunc)
Packit 6c4009
{
Packit 6c4009
  ElfW(Addr) r_info = reloc->r_info;
Packit 6c4009
  const unsigned long int r_type = ELFW (R_TYPE) (r_info);
Packit 6c4009
  ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
Packit 6c4009
  const ElfW(Sym) *const __attribute__ ((unused)) refsym = sym;
Packit 6c4009
  struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
Packit 6c4009
  ElfW(Addr) value = 0;
Packit 6c4009
  if (sym_map != NULL)
Packit 6c4009
    value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend;
Packit 6c4009
Packit 6c4009
  switch (r_type)
Packit 6c4009
    {
Packit 6c4009
#ifndef RTLD_BOOTSTRAP
Packit 6c4009
    case __WORDSIZE == 64 ? R_RISCV_TLS_DTPMOD64 : R_RISCV_TLS_DTPMOD32:
Packit 6c4009
      if (sym_map)
Packit 6c4009
	*addr_field = sym_map->l_tls_modid;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case __WORDSIZE == 64 ? R_RISCV_TLS_DTPREL64 : R_RISCV_TLS_DTPREL32:
Packit 6c4009
      if (sym != NULL)
Packit 6c4009
	*addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case __WORDSIZE == 64 ? R_RISCV_TLS_TPREL64 : R_RISCV_TLS_TPREL32:
Packit 6c4009
      if (sym != NULL)
Packit 6c4009
	{
Packit 6c4009
	  CHECK_STATIC_TLS (map, sym_map);
Packit 6c4009
	  *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
Packit 6c4009
	}
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_RISCV_COPY:
Packit 6c4009
      {
Packit 6c4009
	if (__glibc_unlikely (sym == NULL))
Packit 6c4009
	  /* This can happen in trace mode if an object could not be
Packit 6c4009
	     found.  */
Packit 6c4009
	  break;
Packit 6c4009
Packit 6c4009
	/* Handle TLS copy relocations.  */
Packit 6c4009
	if (__glibc_unlikely (ELFW (ST_TYPE) (sym->st_info) == STT_TLS))
Packit 6c4009
	  {
Packit 6c4009
	    /* There's nothing to do if the symbol is in .tbss.  */
Packit 6c4009
	    if (__glibc_likely (sym->st_value >= sym_map->l_tls_initimage_size))
Packit 6c4009
	      break;
Packit 6c4009
	    value += (ElfW(Addr)) sym_map->l_tls_initimage - sym_map->l_addr;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	size_t size = sym->st_size;
Packit 6c4009
	if (__glibc_unlikely (sym->st_size != refsym->st_size))
Packit 6c4009
	  {
Packit 6c4009
	    const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
Packit 6c4009
	    if (sym->st_size > refsym->st_size)
Packit 6c4009
	      size = refsym->st_size;
Packit 6c4009
	    if (sym->st_size > refsym->st_size || GLRO(dl_verbose))
Packit 6c4009
	      _dl_error_printf ("\
Packit 6c4009
  %s: Symbol `%s' has different size in shared object, consider re-linking\n",
Packit 6c4009
				rtld_progname ?: "<program name unknown>",
Packit 6c4009
				strtab + refsym->st_name);
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	memcpy (reloc_addr, (void *)value, size);
Packit 6c4009
	break;
Packit 6c4009
      }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
Packit 6c4009
    case R_RISCV_RELATIVE:
Packit 6c4009
      {
Packit 6c4009
# if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
Packit 6c4009
	/* This is defined in rtld.c, but nowhere in the static libc.a;
Packit 6c4009
	   make the reference weak so static programs can still link.
Packit 6c4009
	   This declaration cannot be done when compiling rtld.c
Packit 6c4009
	   (i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
Packit 6c4009
	   common defn for _dl_rtld_map, which is incompatible with a
Packit 6c4009
	   weak decl in the same file.  */
Packit 6c4009
#  ifndef SHARED
Packit 6c4009
	weak_extern (GL(dl_rtld_map));
Packit 6c4009
#  endif
Packit 6c4009
	if (map != &GL(dl_rtld_map)) /* Already done in rtld itself.  */
Packit 6c4009
# endif
Packit 6c4009
	  *addr_field = map->l_addr + reloc->r_addend;
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
    case R_RISCV_JUMP_SLOT:
Packit 6c4009
    case __WORDSIZE == 64 ? R_RISCV_64 : R_RISCV_32:
Packit 6c4009
      *addr_field = value;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_RISCV_NONE:
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      _dl_reloc_bad_type (map, r_type, 0);
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
auto inline void
Packit 6c4009
__attribute__ ((always_inline))
Packit 6c4009
elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
Packit 6c4009
			  void *const reloc_addr)
Packit 6c4009
{
Packit 6c4009
  *(ElfW(Addr) *) reloc_addr = l_addr + reloc->r_addend;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
auto inline void
Packit 6c4009
__attribute__ ((always_inline))
Packit 6c4009
elf_machine_lazy_rel (struct link_map *map, ElfW(Addr) l_addr,
Packit 6c4009
		      const ElfW(Rela) *reloc, int skip_ifunc)
Packit 6c4009
{
Packit 6c4009
  ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
Packit 6c4009
  const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
Packit 6c4009
Packit 6c4009
  /* Check for unexpected PLT reloc type.  */
Packit 6c4009
  if (__glibc_likely (r_type == R_RISCV_JUMP_SLOT))
Packit 6c4009
    {
Packit 6c4009
      if (__glibc_unlikely (map->l_mach.plt == 0))
Packit 6c4009
	{
Packit 6c4009
	  if (l_addr)
Packit 6c4009
	    *reloc_addr += l_addr;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	*reloc_addr = map->l_mach.plt;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    _dl_reloc_bad_type (map, r_type, 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Set up the loaded object described by L so its stub function
Packit 6c4009
   will jump to the on-demand fixup code __dl_runtime_resolve.  */
Packit 6c4009
Packit 6c4009
auto inline int
Packit 6c4009
__attribute__ ((always_inline))
Packit 6c4009
elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
Packit 6c4009
{
Packit 6c4009
#ifndef RTLD_BOOTSTRAP
Packit 6c4009
  /* If using PLTs, fill in the first two entries of .got.plt.  */
Packit 6c4009
  if (l->l_info[DT_JMPREL])
Packit 6c4009
    {
Packit 6c4009
      extern void _dl_runtime_resolve (void) __attribute__ ((visibility ("hidden")));
Packit 6c4009
      ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
Packit 6c4009
      /* If a library is prelinked but we have to relocate anyway,
Packit 6c4009
	 we have to be able to undo the prelinking of .got.plt.
Packit 6c4009
	 The prelinker saved the address of .plt for us here.  */
Packit 6c4009
      if (gotplt[1])
Packit 6c4009
	l->l_mach.plt = gotplt[1] + l->l_addr;
Packit 6c4009
      gotplt[0] = (ElfW(Addr)) &_dl_runtime_resolve;
Packit 6c4009
      gotplt[1] = (ElfW(Addr)) l;
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  return lazy;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#endif /* RESOLVE_MAP */