1  /* Machine-dependent ELF dynamic relocation inline functions.  ARC version.
       2     Copyright (C) 2020-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 "arc"
      23  
      24  #include <entry.h>
      25  
      26  #ifndef ENTRY_POINT
      27  # error ENTRY_POINT needs to be defined for ARC
      28  #endif
      29  
      30  #include <string.h>
      31  #include <link.h>
      32  #include <dl-tls.h>
      33  #include <dl-static-tls.h>
      34  #include <dl-machine-rel.h>
      35  
      36  /* Dynamic Linking ABI for ARCv2 ISA.
      37  
      38                          PLT
      39            --------------------------------	<---- DT_PLTGOT
      40            |  ld r11, [pcl, off-to-GOT[1] |  0
      41            |                              |  4
      42     plt0   |  ld r10, [pcl, off-to-GOT[2] |  8
      43            |                              | 12
      44            |  j [r10]                     | 16
      45            --------------------------------
      46            |    Base address of GOT       | 20
      47            --------------------------------
      48            |  ld r12, [pcl, off-to-GOT[3] | 24
      49     plt1   |                              |
      50            |  j.d    [r12]                | 32
      51            |  mov    r12, pcl             | 36
      52            --------------------------------
      53            |                              | 40
      54            ~                              ~
      55            ~                              ~
      56            |                              |
      57            --------------------------------
      58  
      59                 .got
      60            --------------
      61            |    [0]     |
      62            |    ...     |  Runtime address for data symbols
      63            |    [n]     |
      64            --------------
      65  
      66              .got.plt
      67            --------------
      68            |    [0]     |  Build address of .dynamic
      69            --------------
      70            |    [1]     |  Module info - setup by ld.so
      71            --------------
      72            |    [2]     |  resolver entry point
      73            --------------
      74            |    [3]     |
      75            |    ...     |  Runtime address for function symbols
      76            |    [f]     |
      77            --------------
      78  
      79     For ARCompact, the PLT is 12 bytes due to short instructions
      80  
      81            --------------------------------
      82            |  ld r12, [pcl, off-to-GOT[3] | 24   (12 bytes each)
      83     plt1   |                              |
      84            |  j_s.d  [r12]                | 32
      85            |  mov_s  r12, pcl             | 34
      86            --------------------------------
      87            |                              | 36  */
      88  
      89  /* Return nonzero iff ELF header is compatible with the running host.  */
      90  static inline int
      91  elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
      92  {
      93    return (ehdr->e_machine == EM_ARCV2		 /* ARC HS.  */
      94  	  || ehdr->e_machine == EM_ARC_COMPACT); /* ARC 700.  */
      95  }
      96  
      97  /* Get build time address of .dynamic as setup in GOT[0]
      98     This is called very early in _dl_start so it has not been relocated to
      99     runtime value.  */
     100  static inline ElfW(Addr)
     101  elf_machine_dynamic (void)
     102  {
     103    extern const ElfW(Addr) _GLOBAL_OFFSET_TABLE_[] attribute_hidden;
     104    return _GLOBAL_OFFSET_TABLE_[0];
     105  }
     106  
     107  
     108  /* Return the run-time load address of the shared object.  */
     109  static inline ElfW(Addr)
     110  elf_machine_load_address (void)
     111  {
     112    ElfW(Addr) build_addr, run_addr;
     113  
     114    /* For build address, below generates
     115       ld  r0, [pcl, _GLOBAL_OFFSET_TABLE_@pcl].  */
     116    build_addr = elf_machine_dynamic ();
     117    __asm__ ("add %0, pcl, _DYNAMIC@pcl	\n" : "=r" (run_addr));
     118  
     119    return run_addr - build_addr;
     120  }
     121  
     122  /* Set up the loaded object described by L so its unrelocated PLT
     123     entries will jump to the on-demand fixup code in dl-runtime.c.  */
     124  
     125  static inline int
     126  __attribute__ ((always_inline))
     127  elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
     128  			   int lazy, int profile)
     129  {
     130    extern void _dl_runtime_resolve (void);
     131  
     132    if (l->l_info[DT_JMPREL] && lazy)
     133      {
     134        /* On ARC DT_PLTGOT point to .plt whose 5th word (after the PLT header)
     135           contains the address of .got.  */
     136        ElfW(Addr) *plt_base = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
     137        ElfW(Addr) *got = (ElfW(Addr) *) (plt_base[5] + l->l_addr);
     138  
     139        got[1] = (ElfW(Addr)) l;	/* Identify this shared object.  */
     140  
     141        /* This function will get called to fix up the GOT entry indicated by
     142  	 the offset on the stack, and then jump to the resolved address.  */
     143        got[2] = (ElfW(Addr)) &_dl_runtime_resolve;
     144      }
     145  
     146    return lazy;
     147  }
     148  
     149  /* What this code does:
     150      -ldso starts execution here when kernel returns from execve
     151      -calls into generic ldso entry point _dl_start
     152      -optionally adjusts argc for executable if exec passed as cmd
     153      -calls into app main with address of finaliser.  */
     154  
     155  #define RTLD_START asm ("\
     156  .text									\n\
     157  .globl __start								\n\
     158  .type __start, @function						\n\
     159  __start:								\n\
     160  	/* (1). bootstrap ld.so.  */					\n\
     161  	bl.d    _dl_start                                       	\n\
     162  	mov_s   r0, sp  /* pass ptr to aux vector tbl.    */    	\n\
     163  	mov r13, r0	/* safekeep app elf entry point.  */		\n\
     164  	ld_s    r1, [sp]       /* orig argc.  */			\n\
     165  									\n\
     166  	/* (2). call preinit stuff.  */					\n\
     167  	ld	r0, [pcl, _rtld_local@pcl]				\n\
     168  	add	r2, sp, 4	; argv					\n\
     169  	add2	r3, r2, r1						\n\
     170  	add	r3, r3, 4	; env					\n\
     171  	bl	_dl_init@plt						\n\
     172  									\n\
     173  	/* (3) call app elf entry point.  */				\n\
     174  	add     r0, pcl, _dl_fini@pcl					\n\
     175  	j	[r13]							\n\
     176  									\n\
     177  	.size  __start,.-__start                               		\n\
     178  	.previous                                               	\n\
     179  ");
     180  
     181  /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
     182     PLT entries should not be allowed to define the value.
     183     ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
     184     of the main executable's symbols, as for a COPY reloc.  */
     185  #define elf_machine_type_class(type)				\
     186    ((((type) == R_ARC_JMP_SLOT					\
     187       || (type) == R_ARC_TLS_DTPMOD				\
     188       || (type) == R_ARC_TLS_DTPOFF				\
     189       || (type) == R_ARC_TLS_TPOFF) * ELF_RTYPE_CLASS_PLT)	\
     190     | (((type) == R_ARC_COPY) * ELF_RTYPE_CLASS_COPY))
     191  
     192  /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
     193  #define ELF_MACHINE_JMP_SLOT  R_ARC_JMP_SLOT
     194  
     195  /* Fixup a PLT entry to bounce directly to the function at VALUE.  */
     196  
     197  static inline ElfW(Addr)
     198  elf_machine_fixup_plt (struct link_map *map, lookup_t t,
     199  		       const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
     200  		       const ElfW(Rela) *reloc,
     201  		       ElfW(Addr) *reloc_addr, ElfW(Addr) value)
     202  {
     203    return *reloc_addr = value;
     204  }
     205  
     206  /* Return the final value of a plt relocation.  */
     207  #define elf_machine_plt_value(map, reloc, value) (value)
     208  
     209  /* Names of the architecture-specific auditing callback functions.  */
     210  #define ARCH_LA_PLTENTER arc_gnu_pltenter
     211  #define ARCH_LA_PLTEXIT arc_gnu_pltexit
     212  
     213  #endif /* dl_machine_h */
     214  
     215  #ifdef RESOLVE_MAP
     216  
     217  static inline void
     218  __attribute__ ((always_inline))
     219  elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
     220  		  const ElfW(Rela) *reloc, const ElfW(Sym) *sym,
     221  		  const struct r_found_version *version,
     222                    void *const reloc_addr_arg, int skip_ifunc)
     223  {
     224    ElfW(Addr) r_info = reloc->r_info;
     225    const unsigned long int r_type = ELFW (R_TYPE) (r_info);
     226    ElfW(Addr) *const reloc_addr = reloc_addr_arg;
     227  
     228    if (__glibc_unlikely (r_type == R_ARC_RELATIVE))
     229      *reloc_addr += map->l_addr;
     230    else if (__glibc_unlikely (r_type == R_ARC_NONE))
     231      return;
     232    else
     233      {
     234        const ElfW(Sym) *const refsym = sym;
     235        struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
     236  					      r_type);
     237        ElfW(Addr) value = SYMBOL_ADDRESS (sym_map, sym, true);
     238  
     239        switch (r_type)
     240          {
     241          case R_ARC_COPY:
     242            if (__glibc_unlikely (sym == NULL))
     243              /* This can happen in trace mode if an object could not be
     244                 found.  */
     245              break;
     246  
     247            size_t size = sym->st_size;
     248            if (__glibc_unlikely (size != refsym->st_size))
     249              {
     250                const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
     251                if (sym->st_size > refsym->st_size)
     252                  size = refsym->st_size;
     253                if (sym->st_size > refsym->st_size || GLRO(dl_verbose))
     254                  _dl_error_printf ("\
     255    %s: Symbol `%s' has different size in shared object, consider re-linking\n",
     256                                    rtld_progname ?: "<program name unknown>",
     257                                    strtab + refsym->st_name);
     258              }
     259  
     260            memcpy (reloc_addr_arg, (void *) value, size);
     261            break;
     262  
     263          case R_ARC_GLOB_DAT:
     264          case R_ARC_JMP_SLOT:
     265              *reloc_addr = value;
     266            break;
     267  
     268          case R_ARC_TLS_DTPMOD:
     269            if (sym_map != NULL)
     270              /* Get the information from the link map returned by the
     271                 resolv function.  */
     272              *reloc_addr = sym_map->l_tls_modid;
     273            break;
     274  
     275          case R_ARC_TLS_DTPOFF:
     276            if (sym != NULL)
     277              /* Offset set by the linker in the GOT entry would be overwritten
     278                 by dynamic loader instead of added to the symbol location.
     279                 Other target have the same approach on DTPOFF relocs.  */
     280              *reloc_addr += sym->st_value;
     281            break;
     282  
     283          case R_ARC_TLS_TPOFF:
     284            if (sym != NULL)
     285              {
     286                CHECK_STATIC_TLS (map, sym_map);
     287                *reloc_addr = sym_map->l_tls_offset + sym->st_value + reloc->r_addend;
     288              }
     289            break;
     290  
     291          case R_ARC_32:
     292            *reloc_addr += value + reloc->r_addend;
     293            break;
     294  
     295          case R_ARC_PC32:
     296            *reloc_addr += value + reloc->r_addend - (unsigned long int) reloc_addr;
     297            break;
     298  
     299          default:
     300            _dl_reloc_bad_type (map, r_type, 0);
     301            break;
     302          }
     303      }
     304  }
     305  
     306  static inline void
     307  __attribute__ ((always_inline))
     308  elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
     309                             void *const reloc_addr_arg)
     310  {
     311    ElfW(Addr) *const reloc_addr = reloc_addr_arg;
     312    *reloc_addr += l_addr;
     313  }
     314  
     315  static inline void
     316  __attribute__ ((always_inline))
     317  elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
     318  		      ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
     319  		      int skip_ifunc)
     320  {
     321    ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
     322    const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
     323  
     324    if (r_type == R_ARC_JMP_SLOT)
     325      *reloc_addr += l_addr;
     326    else
     327      _dl_reloc_bad_type (map, r_type, 1);
     328  }
     329  
     330  #endif /* RESOLVE_MAP */