(root)/
glibc-2.38/
sysdeps/
loongarch/
dl-machine.h
       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 */