1  /* PLT trampoline.  MIPS version.
       2     Copyright (C) 1996-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  /*  FIXME: Profiling of shared libraries is not implemented yet.  */
      20  
      21  #include <sysdep.h>
      22  #include <link.h>
      23  #include <elf.h>
      24  #include <ldsodefs.h>
      25  #include <dl-machine.h>
      26  #include <sysdep-cancel.h>
      27  
      28  /* Get link map for callers object containing STUB_PC.  */
      29  static inline struct link_map *
      30  elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
      31  {
      32    extern int _dl_mips_gnu_objects;
      33  
      34    /* got[1] is reserved to keep its link map address for the shared
      35       object generated by the gnu linker.  If all are such objects, we
      36       can find the link map from current GPREG simply.  If not so, get
      37       the link map for caller's object containing STUB_PC.  */
      38  
      39    if (_dl_mips_gnu_objects)
      40      {
      41        ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
      42        ElfW(Word) g1;
      43  
      44        g1 = ((ElfW(Word) *) got)[1];
      45  
      46        if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
      47  	{
      48  	  struct link_map *l =
      49  	    (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK);
      50  	  ElfW(Addr) base, limit;
      51  	  const ElfW(Phdr) *p = l->l_phdr;
      52  	  ElfW(Half) this, nent = l->l_phnum;
      53  
      54  	  /* For the common case of a stub being called from the containing
      55  	     object, STUB_PC will point to somewhere within the object that
      56  	     is described by the link map fetched via got[1].  Otherwise we
      57  	     have to scan all maps.  */
      58  	  for (this = 0; this < nent; this++)
      59  	    {
      60  	      if (p[this].p_type == PT_LOAD)
      61  		{
      62  		  base = p[this].p_vaddr + l->l_addr;
      63  		  limit = base + p[this].p_memsz;
      64  		  if (stub_pc >= base && stub_pc < limit)
      65  		    return l;
      66  		}
      67  	    }
      68  	}
      69      }
      70  
      71      struct link_map *l;
      72      Lmid_t nsid;
      73  
      74      for (nsid = 0; nsid < DL_NNS; ++nsid)
      75        for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
      76  	{
      77  	  ElfW(Addr) base, limit;
      78  	  const ElfW(Phdr) *p = l->l_phdr;
      79  	  ElfW(Half) this, nent = l->l_phnum;
      80  
      81  	  for (this = 0; this < nent; ++this)
      82  	    {
      83  	      if (p[this].p_type == PT_LOAD)
      84  		{
      85  		  base = p[this].p_vaddr + l->l_addr;
      86  		  limit = base + p[this].p_memsz;
      87  		  if (stub_pc >= base && stub_pc < limit)
      88  		    return l;
      89  		}
      90  	    }
      91  	}
      92  
      93    _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
      94    return NULL;
      95  }
      96  
      97  /* Define mips specific runtime resolver. The function __dl_runtime_resolve
      98     is called from assembler function _dl_runtime_resolve which converts
      99     special argument registers t7 ($15) and t8 ($24):
     100       t7  address to return to the caller of the function
     101       t8  index for this function symbol in .dynsym
     102     to usual c arguments.
     103  
     104     Other architectures call fixup from dl-runtime.c in
     105     _dl_runtime_resolve.  MIPS instead calls __dl_runtime_resolve.  We
     106     have to use our own version because of the way the got section is
     107     treated on MIPS (we've also got ELF_MACHINE_PLT defined).  */
     108  
     109  /* The flag _dl_mips_gnu_objects is set if all dynamic objects are
     110     generated by the gnu linker. */
     111  int _dl_mips_gnu_objects = 1;
     112  
     113  /* This is called from assembly stubs below which the compiler can't see.  */
     114  static ElfW(Addr)
     115  __dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))
     116  		  __attribute_used__;
     117  
     118  static ElfW(Addr)
     119  __dl_runtime_resolve (ElfW(Word) sym_index,
     120  		      ElfW(Word) return_address,
     121  		      ElfW(Addr) old_gpreg,
     122  		      ElfW(Addr) stub_pc)
     123  {
     124    struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc);
     125    const ElfW(Sym) *const symtab
     126      = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]);
     127    const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
     128    ElfW(Addr) *got
     129      = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
     130    const ElfW(Word) local_gotno
     131      = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
     132    const ElfW(Word) gotsym
     133      = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
     134    const ElfW(Sym) *sym = &symtab[sym_index];
     135    struct link_map *sym_map;
     136    ElfW(Addr) value;
     137  
     138    /* FIXME: The symbol versioning stuff is not tested yet.  */
     139    if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
     140      {
     141        switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL ? 1 : 0)
     142  	{
     143  	default:
     144  	  {
     145  	    const ElfW(Half) *vernum =
     146  	      (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
     147  	    ElfW(Half) ndx = vernum[sym_index] & 0x7fff;
     148  	    const struct r_found_version *version = &l->l_versions[ndx];
     149  
     150  	    if (version->hash != 0)
     151  	      {
     152                  /* We need to keep the scope around so do some locking.  This is
     153  		   not necessary for objects which cannot be unloaded or when
     154  		   we are not using any threads (yet).  */
     155  		if (!RTLD_SINGLE_THREAD_P)
     156  		  THREAD_GSCOPE_SET_FLAG ();
     157  
     158  		sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l,
     159  					       &sym, l->l_scope, version,
     160  					       ELF_RTYPE_CLASS_PLT, 0, 0);
     161  
     162                  /* We are done with the global scope.  */
     163  		if (!RTLD_SINGLE_THREAD_P)
     164  		  THREAD_GSCOPE_RESET_FLAG ();
     165  
     166  		break;
     167  	      }
     168  	  }
     169  	  /* Fall through.  */
     170  	case 0:
     171  	  {
     172            /* We need to keep the scope around so do some locking.  This is
     173  	     not necessary for objects which cannot be unloaded or when
     174  	     we are not using any threads (yet).  */
     175  	  int flags = DL_LOOKUP_ADD_DEPENDENCY;
     176  	  if (!RTLD_SINGLE_THREAD_P)
     177  	    {
     178  	      THREAD_GSCOPE_SET_FLAG ();
     179  	      flags |= DL_LOOKUP_GSCOPE_LOCK;
     180  	    }
     181  
     182  	  sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
     183  					 l->l_scope, 0, ELF_RTYPE_CLASS_PLT,
     184  					 flags, 0);
     185  
     186            /* We are done with the global scope.  */
     187  	  if (!RTLD_SINGLE_THREAD_P)
     188  	    THREAD_GSCOPE_RESET_FLAG ();
     189  	  }
     190  	}
     191  
     192        /* Currently value contains the base load address of the object
     193  	 that defines sym.  Now add in the symbol offset.  */
     194        value = SYMBOL_ADDRESS (sym_map, sym, true);
     195      }
     196    else
     197      /* We already found the symbol.  The module (and therefore its load
     198         address) is also known.  */
     199      value = SYMBOL_ADDRESS (l, sym, true);
     200  
     201    /* Apply the relocation with that value.  */
     202    *(got + local_gotno + sym_index - gotsym) = value;
     203  
     204    return value;
     205  }
     206  
     207  #if _MIPS_SIM == _ABIO32
     208  #define ELF_DL_FRAME_SIZE 40
     209  
     210  #define ELF_DL_SAVE_ARG_REGS "\
     211  	sw	$15, 36($29)\n						      \
     212  	sw	$4, 16($29)\n						      \
     213  	sw	$5, 20($29)\n						      \
     214  	sw	$6, 24($29)\n						      \
     215  	sw	$7, 28($29)\n						      \
     216  "
     217  
     218  #define ELF_DL_RESTORE_ARG_REGS "\
     219  	lw	$31, 36($29)\n						      \
     220  	lw	$4, 16($29)\n						      \
     221  	lw	$5, 20($29)\n						      \
     222  	lw	$6, 24($29)\n						      \
     223  	lw	$7, 28($29)\n						      \
     224  "
     225  
     226  /* The PLT resolver should also save and restore $2 and $3, which are used
     227     as arguments to MIPS16 stub functions.  */
     228  #define ELF_DL_PLT_FRAME_SIZE 48
     229  
     230  #define ELF_DL_PLT_SAVE_ARG_REGS \
     231  	ELF_DL_SAVE_ARG_REGS "\
     232  	sw	$2, 40($29)\n						      \
     233  	sw	$3, 44($29)\n						      \
     234  "
     235  
     236  #define ELF_DL_PLT_RESTORE_ARG_REGS \
     237  	ELF_DL_RESTORE_ARG_REGS "\
     238  	lw	$2, 40($29)\n						      \
     239  	lw	$3, 44($29)\n						      \
     240  "
     241  
     242  #define IFABIO32(X) X
     243  #define IFNEWABI(X)
     244  
     245  #else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
     246  
     247  #define ELF_DL_FRAME_SIZE 80
     248  
     249  #define ELF_DL_SAVE_ARG_REGS "\
     250  	sd	$15, 72($29)\n						      \
     251  	sd	$4, 8($29)\n						      \
     252  	sd	$5, 16($29)\n						      \
     253  	sd	$6, 24($29)\n						      \
     254  	sd	$7, 32($29)\n						      \
     255  	sd	$8, 40($29)\n						      \
     256  	sd	$9, 48($29)\n						      \
     257  	sd	$10, 56($29)\n						      \
     258  	sd	$11, 64($29)\n						      \
     259  "
     260  
     261  #define ELF_DL_RESTORE_ARG_REGS "\
     262  	ld	$31, 72($29)\n						      \
     263  	ld	$4, 8($29)\n						      \
     264  	ld	$5, 16($29)\n						      \
     265  	ld	$6, 24($29)\n						      \
     266  	ld	$7, 32($29)\n						      \
     267  	ld	$8, 40($29)\n						      \
     268  	ld	$9, 48($29)\n						      \
     269  	ld	$10, 56($29)\n						      \
     270  	ld	$11, 64($29)\n						      \
     271  "
     272  
     273  /* The PLT resolver should also save and restore $2 and $3, which are used
     274     as arguments to MIPS16 stub functions.  */
     275  #define ELF_DL_PLT_FRAME_SIZE 96
     276  
     277  #define ELF_DL_PLT_SAVE_ARG_REGS \
     278  	ELF_DL_SAVE_ARG_REGS "\
     279  	sd	$2, 80($29)\n						      \
     280  	sd	$3, 88($29)\n						      \
     281  "
     282  
     283  #define ELF_DL_PLT_RESTORE_ARG_REGS \
     284  	ELF_DL_RESTORE_ARG_REGS "\
     285  	ld	$2, 80($29)\n						      \
     286  	ld	$3, 88($29)\n						      \
     287  "
     288  
     289  #define IFABIO32(X)
     290  #define IFNEWABI(X) X
     291  
     292  #endif
     293  
     294  #ifndef __mips16
     295  asm ("\n\
     296  	.text\n\
     297  	.align	2\n\
     298  	.set	nomips16\n\
     299  	.globl	_dl_runtime_resolve\n\
     300  	.type	_dl_runtime_resolve,@function\n\
     301  	.ent	_dl_runtime_resolve\n\
     302  _dl_runtime_resolve:\n\
     303  	.frame	$29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n\
     304  	.set noreorder\n\
     305  	# Save GP.\n\
     306  1:	move	$3, $28\n\
     307  	# Save arguments and sp value in stack.\n\
     308  	" STRINGXP(PTR_SUBIU) "  $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
     309  	# Modify t9 ($25) so as to point .cpload instruction.\n\
     310  	" IFABIO32(STRINGXP(PTR_ADDIU) "	$25, (2f-1b)\n") "\
     311  	# Compute GP.\n\
     312  2:	" STRINGXP(SETUP_GP) "\n\
     313  	" STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\
     314  	.set reorder\n\
     315  	# Save slot call pc.\n\
     316  	move	$2, $31\n\
     317  	" IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
     318  	" ELF_DL_SAVE_ARG_REGS "\
     319  	move	$4, $24\n\
     320  	move	$5, $15\n\
     321  	move	$6, $3\n\
     322  	move	$7, $2\n\
     323  	jal	__dl_runtime_resolve\n\
     324  	" ELF_DL_RESTORE_ARG_REGS "\
     325  	" STRINGXP(RESTORE_GP64) "\n\
     326  	" STRINGXP(PTR_ADDIU) "	$29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
     327  	move	$25, $2\n\
     328  	jr	$25\n\
     329  	.end	_dl_runtime_resolve\n\
     330  	.previous\n\
     331  ");
     332  
     333  /* Assembler veneer called from the PLT header code when using PLTs.
     334  
     335     Code in each PLT entry and the PLT header fills in the arguments to
     336     this function:
     337  
     338     - $15 (o32 t7, n32/n64 t3) - caller's return address
     339     - $24 (t8) - PLT entry index
     340     - $25 (t9) - address of _dl_runtime_pltresolve
     341     - o32 $28 (gp), n32/n64 $14 (t2) - address of .got.plt
     342  
     343     Different registers are used for .got.plt because the ABI was
     344     originally designed for o32, where gp was available (call
     345     clobbered).  On n32/n64 gp is call saved.
     346  
     347     _dl_fixup needs:
     348  
     349     - $4 (a0) - link map address
     350     - $5 (a1) - .rel.plt offset (== PLT entry index * 8)  */
     351  
     352  asm ("\n\
     353  	.text\n\
     354  	.align	2\n\
     355  	.set	nomips16\n\
     356  	.globl	_dl_runtime_pltresolve\n\
     357  	.type	_dl_runtime_pltresolve,@function\n\
     358  	.ent	_dl_runtime_pltresolve\n\
     359  _dl_runtime_pltresolve:\n\
     360  	.frame	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
     361  	.set noreorder\n\
     362  	# Save arguments and sp value in stack.\n\
     363  1:	" STRINGXP(PTR_SUBIU) "	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
     364  	" IFABIO32(STRINGXP(PTR_L) "	$13, " STRINGXP(PTRSIZE) "($28)") "\n\
     365  	" IFNEWABI(STRINGXP(PTR_L) "	$13, " STRINGXP(PTRSIZE) "($14)") "\n\
     366  	# Modify t9 ($25) so as to point .cpload instruction.\n\
     367  	" IFABIO32(STRINGXP(PTR_ADDIU) "	$25, (2f-1b)\n") "\
     368  	# Compute GP.\n\
     369  2:	" STRINGXP(SETUP_GP) "\n\
     370  	" STRINGXV(SETUP_GP64 (0, _dl_runtime_pltresolve)) "\n\
     371  	.set reorder\n\
     372  	" IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
     373  	" ELF_DL_PLT_SAVE_ARG_REGS "\
     374  	move	$4, $13\n\
     375  	sll	$5, $24, " STRINGXP(PTRLOG) " + 1\n\
     376  	jal	_dl_fixup\n\
     377  	move	$25, $2\n\
     378  	" ELF_DL_PLT_RESTORE_ARG_REGS "\
     379  	" STRINGXP(RESTORE_GP64) "\n\
     380  	" STRINGXP(PTR_ADDIU) "	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
     381  	jr	$25\n\
     382  	.end	_dl_runtime_pltresolve\n\
     383  	.previous\n\
     384  ");
     385  
     386  #elif _MIPS_SIM == _ABIO32 /* __mips16 */
     387  /* MIPS16 version, O32 only.  */
     388  asm ("\n\
     389  	.text\n\
     390  	.align	2\n\
     391  	.set	mips16\n\
     392  	.globl	_dl_runtime_resolve\n\
     393  	.type	_dl_runtime_resolve,@function\n\
     394  	.ent	_dl_runtime_resolve\n\
     395  _dl_runtime_resolve:\n\
     396  	.frame	$29, " STRINGXP (ELF_DL_FRAME_SIZE) ", $31\n\
     397  	# Save arguments and sp value in stack.\n\t"
     398  # if _MIPS_ISA >= _MIPS_ISA_MIPS32
     399  	"save	" STRINGXP (ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
     400  # else
     401  	"addiu	$sp, -" STRINGXP (ELF_DL_FRAME_SIZE) "\n\
     402  	sw	$7, 32($sp)\n\
     403  	sw	$6, 28($sp)\n\
     404  	sw	$5, 24($sp)\n\
     405  	sw	$4, 20($sp)\n\t"
     406  # endif
     407  	"# Preserve caller's $ra, for RESTORE instruction below.\n\
     408  	move	$5, $15\n\
     409  	sw	$5, 36($sp)\n\
     410  	# Compute GP into $2.\n\
     411  	li	$2, %hi(_gp_disp)\n\
     412  	addiu	$3, $pc, %lo(_gp_disp)\n\
     413  	sll	$2, 16\n\
     414  	addu	$2, $3\n\
     415  	lw	$3, %got(__dl_runtime_resolve)($2)\n\
     416  	move	$4, $24\n\
     417  	addiu	$3, %lo(__dl_runtime_resolve)\n\
     418  	move	$7, $ra\n\
     419  	move	$6, $28\n\
     420  	move	$25, $3\n\
     421  	jalr	$3\n\t"
     422  # if _MIPS_ISA >= _MIPS_ISA_MIPS32
     423  	"restore " STRINGXP(ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
     424  # else
     425  	"# Restore $ra, move placed further down to hide latency.\n\
     426  	lw	$4, 36($sp)\n\
     427  	lw	$5, 24($sp)\n\
     428  	lw	$6, 28($sp)\n\
     429  	lw	$7, 32($sp)\n\
     430  	move	$ra, $4\n\
     431  	lw	$4, 20($sp)\n\
     432  	addiu	$sp, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\t"
     433  # endif
     434  	"move	$25, $2\n\
     435  	jr	$2\n\
     436  	.end	_dl_runtime_resolve\n\
     437  	.previous\n\
     438  ");
     439  
     440  asm ("\n\
     441  	.text\n\
     442  	.align	2\n\
     443  	.set	mips16\n\
     444  	.globl	_dl_runtime_pltresolve\n\
     445  	.type	_dl_runtime_pltresolve,@function\n\
     446  	.ent	_dl_runtime_pltresolve\n\
     447  _dl_runtime_pltresolve:\n\
     448  	.frame	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
     449  	# Save arguments and sp value in stack.\n\t"
     450  # if _MIPS_ISA >= _MIPS_ISA_MIPS32
     451  	"save	" STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
     452  # else
     453  	"addiu	$sp, -" STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
     454  	sw	$7, 40($sp)\n\
     455  	sw	$6, 36($sp)\n\
     456  	sw	$5, 32($sp)\n\
     457  	sw	$4, 28($sp)\n\t"
     458  # endif
     459  	"# Preserve MIPS16 stub function arguments.\n\
     460  	sw	$3, 20($sp)\n\
     461  	sw	$2, 16($sp)\n\
     462  	# Preserve caller's $ra, for RESTORE instruction below.\n\
     463  	move	$3, $15\n\
     464  	sw	$3, 44($sp)\n\
     465  	# Compute GP into $2.\n\
     466  	li	$2, %hi(_gp_disp)\n\
     467  	addiu	$3, $pc, %lo(_gp_disp)\n\
     468  	sll	$2, 16\n\
     469  	addu	$2, $3\n\
     470  	# Save GP value in slot.\n\
     471  	sw	$2, 24($sp)\n\
     472  	# Load _dl_fixup address.\n\
     473  	lw	$6, %call16(_dl_fixup)($2)\n\
     474  	# Load link map address.\n\
     475  	move	$3, $28\n\
     476  	lw	$4, " STRINGXP (PTRSIZE) "($3)\n\
     477  	move	$5, $24\n\
     478  	sll	$5, " STRINGXP (PTRLOG) " + 1\n\
     479  	# Call _dl_fixup.\n\
     480  	move	$25, $6\n\
     481  	jalr	$6\n\
     482  	move	$25, $2\n\
     483  	# Reload GP value into $28.\n\
     484  	lw	$3, 24($sp)\n\
     485  	move	$28, $3\n\
     486  	lw	$3, 16($sp)\n\
     487  	move	$15, $3\n\
     488  	lw	$3, 20($sp)\n\t"
     489  # if _MIPS_ISA >= _MIPS_ISA_MIPS32
     490  	"restore " STRINGXP (ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
     491  # else
     492  	"# Restore $ra, move placed further down to hide latency.\n\
     493  	lw	$4, 44($sp)\n\
     494  	lw	$5, 32($sp)\n\
     495  	lw	$6, 36($sp)\n\
     496  	lw	$7, 40($sp)\n\
     497  	move	$ra, $4\n\
     498  	lw	$4, 28($sp)\n\
     499  	addiu	$sp, " STRINGXP (ELF_DL_PLT_FRAME_SIZE) "\n\t"
     500  # endif
     501  	".set	noreorder\n\
     502  	jr	$2\n\
     503  	 move	$2, $15\n\
     504  	.set	reorder\n\
     505  	.end	_dl_runtime_pltresolve\n\
     506  	.previous\n\
     507  ");
     508  
     509  #else /* __mips16 && _MIPS_SIM != _ABIO32 */
     510  # error "MIPS16 support for N32/N64 not implemented"
     511  
     512  #endif /* __mips16 */