Blame sysdeps/mips/dl-trampoline.c

Packit 6c4009
/* PLT trampoline.  MIPS version.
Packit 6c4009
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Kazumoto Kojima <kkojima@info.kanagawa-u.ac.jp>.
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
/*  FIXME: Profiling of shared libraries is not implemented yet.  */
Packit 6c4009
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
#include <link.h>
Packit 6c4009
#include <elf.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <dl-machine.h>
Packit 6c4009
#include <sysdep-cancel.h>
Packit 6c4009
Packit 6c4009
/* Get link map for callers object containing STUB_PC.  */
Packit 6c4009
static inline struct link_map *
Packit 6c4009
elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
Packit 6c4009
{
Packit 6c4009
  extern int _dl_mips_gnu_objects;
Packit 6c4009
Packit 6c4009
  /* got[1] is reserved to keep its link map address for the shared
Packit 6c4009
     object generated by the gnu linker.  If all are such objects, we
Packit 6c4009
     can find the link map from current GPREG simply.  If not so, get
Packit 6c4009
     the link map for caller's object containing STUB_PC.  */
Packit 6c4009
Packit 6c4009
  if (_dl_mips_gnu_objects)
Packit 6c4009
    {
Packit 6c4009
      ElfW(Addr) *got = elf_mips_got_from_gpreg (gpreg);
Packit 6c4009
      ElfW(Word) g1;
Packit 6c4009
Packit 6c4009
      g1 = ((ElfW(Word) *) got)[1];
Packit 6c4009
Packit 6c4009
      if ((g1 & ELF_MIPS_GNU_GOT1_MASK) != 0)
Packit 6c4009
	{
Packit 6c4009
	  struct link_map *l =
Packit 6c4009
	    (struct link_map *) (g1 & ~ELF_MIPS_GNU_GOT1_MASK);
Packit 6c4009
	  ElfW(Addr) base, limit;
Packit 6c4009
	  const ElfW(Phdr) *p = l->l_phdr;
Packit 6c4009
	  ElfW(Half) this, nent = l->l_phnum;
Packit 6c4009
Packit 6c4009
	  /* For the common case of a stub being called from the containing
Packit 6c4009
	     object, STUB_PC will point to somewhere within the object that
Packit 6c4009
	     is described by the link map fetched via got[1].  Otherwise we
Packit 6c4009
	     have to scan all maps.  */
Packit 6c4009
	  for (this = 0; this < nent; this++)
Packit 6c4009
	    {
Packit 6c4009
	      if (p[this].p_type == PT_LOAD)
Packit 6c4009
		{
Packit 6c4009
		  base = p[this].p_vaddr + l->l_addr;
Packit 6c4009
		  limit = base + p[this].p_memsz;
Packit 6c4009
		  if (stub_pc >= base && stub_pc < limit)
Packit 6c4009
		    return l;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
    struct link_map *l;
Packit 6c4009
    Lmid_t nsid;
Packit 6c4009
Packit 6c4009
    for (nsid = 0; nsid < DL_NNS; ++nsid)
Packit 6c4009
      for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
Packit 6c4009
	{
Packit 6c4009
	  ElfW(Addr) base, limit;
Packit 6c4009
	  const ElfW(Phdr) *p = l->l_phdr;
Packit 6c4009
	  ElfW(Half) this, nent = l->l_phnum;
Packit 6c4009
Packit 6c4009
	  for (this = 0; this < nent; ++this)
Packit 6c4009
	    {
Packit 6c4009
	      if (p[this].p_type == PT_LOAD)
Packit 6c4009
		{
Packit 6c4009
		  base = p[this].p_vaddr + l->l_addr;
Packit 6c4009
		  limit = base + p[this].p_memsz;
Packit 6c4009
		  if (stub_pc >= base && stub_pc < limit)
Packit 6c4009
		    return l;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
  _dl_signal_error (0, NULL, NULL, "cannot find runtime link map");
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Define mips specific runtime resolver. The function __dl_runtime_resolve
Packit 6c4009
   is called from assembler function _dl_runtime_resolve which converts
Packit 6c4009
   special argument registers t7 ($15) and t8 ($24):
Packit 6c4009
     t7  address to return to the caller of the function
Packit 6c4009
     t8  index for this function symbol in .dynsym
Packit 6c4009
   to usual c arguments.
Packit 6c4009
Packit 6c4009
   Other architectures call fixup from dl-runtime.c in
Packit 6c4009
   _dl_runtime_resolve.  MIPS instead calls __dl_runtime_resolve.  We
Packit 6c4009
   have to use our own version because of the way the got section is
Packit 6c4009
   treated on MIPS (we've also got ELF_MACHINE_PLT defined).  */
Packit 6c4009
Packit 6c4009
/* The flag _dl_mips_gnu_objects is set if all dynamic objects are
Packit 6c4009
   generated by the gnu linker. */
Packit 6c4009
int _dl_mips_gnu_objects = 1;
Packit 6c4009
Packit 6c4009
/* This is called from assembly stubs below which the compiler can't see.  */
Packit 6c4009
static ElfW(Addr)
Packit 6c4009
__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr))
Packit 6c4009
		  __attribute_used__;
Packit 6c4009
Packit 6c4009
static ElfW(Addr)
Packit 6c4009
__dl_runtime_resolve (ElfW(Word) sym_index,
Packit 6c4009
		      ElfW(Word) return_address,
Packit 6c4009
		      ElfW(Addr) old_gpreg,
Packit 6c4009
		      ElfW(Addr) stub_pc)
Packit 6c4009
{
Packit 6c4009
  struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc);
Packit 6c4009
  const ElfW(Sym) *const symtab
Packit 6c4009
    = (const ElfW(Sym) *) D_PTR (l, l_info[DT_SYMTAB]);
Packit 6c4009
  const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
Packit 6c4009
  ElfW(Addr) *got
Packit 6c4009
    = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
Packit 6c4009
  const ElfW(Word) local_gotno
Packit 6c4009
    = (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
Packit 6c4009
  const ElfW(Word) gotsym
Packit 6c4009
    = (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
Packit 6c4009
  const ElfW(Sym) *sym = &symtab[sym_index];
Packit 6c4009
  struct link_map *sym_map;
Packit 6c4009
  ElfW(Addr) value;
Packit 6c4009
Packit 6c4009
  /* FIXME: The symbol versioning stuff is not tested yet.  */
Packit 6c4009
  if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
Packit 6c4009
    {
Packit 6c4009
      switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL ? 1 : 0)
Packit 6c4009
	{
Packit 6c4009
	default:
Packit 6c4009
	  {
Packit 6c4009
	    const ElfW(Half) *vernum =
Packit 6c4009
	      (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
Packit 6c4009
	    ElfW(Half) ndx = vernum[sym_index] & 0x7fff;
Packit 6c4009
	    const struct r_found_version *version = &l->l_versions[ndx];
Packit 6c4009
Packit 6c4009
	    if (version->hash != 0)
Packit 6c4009
	      {
Packit 6c4009
                /* We need to keep the scope around so do some locking.  This is
Packit 6c4009
		   not necessary for objects which cannot be unloaded or when
Packit 6c4009
		   we are not using any threads (yet).  */
Packit 6c4009
		if (!RTLD_SINGLE_THREAD_P)
Packit 6c4009
		  THREAD_GSCOPE_SET_FLAG ();
Packit 6c4009
Packit 6c4009
		sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l,
Packit 6c4009
					       &sym, l->l_scope, version,
Packit 6c4009
					       ELF_RTYPE_CLASS_PLT, 0, 0);
Packit 6c4009
Packit 6c4009
                /* We are done with the global scope.  */
Packit 6c4009
		if (!RTLD_SINGLE_THREAD_P)
Packit 6c4009
		  THREAD_GSCOPE_RESET_FLAG ();
Packit 6c4009
Packit 6c4009
		break;
Packit 6c4009
	      }
Packit 6c4009
	    /* Fall through.  */
Packit 6c4009
	  }
Packit 6c4009
	case 0:
Packit 6c4009
	  {
Packit 6c4009
          /* We need to keep the scope around so do some locking.  This is
Packit 6c4009
	     not necessary for objects which cannot be unloaded or when
Packit 6c4009
	     we are not using any threads (yet).  */
Packit 6c4009
	  int flags = DL_LOOKUP_ADD_DEPENDENCY;
Packit 6c4009
	  if (!RTLD_SINGLE_THREAD_P)
Packit 6c4009
	    {
Packit 6c4009
	      THREAD_GSCOPE_SET_FLAG ();
Packit 6c4009
	      flags |= DL_LOOKUP_GSCOPE_LOCK;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
Packit 6c4009
					 l->l_scope, 0, ELF_RTYPE_CLASS_PLT,
Packit 6c4009
					 flags, 0);
Packit 6c4009
Packit 6c4009
          /* We are done with the global scope.  */
Packit 6c4009
	  if (!RTLD_SINGLE_THREAD_P)
Packit 6c4009
	    THREAD_GSCOPE_RESET_FLAG ();
Packit 6c4009
	  }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Currently value contains the base load address of the object
Packit 6c4009
	 that defines sym.  Now add in the symbol offset.  */
Packit 6c4009
      value = SYMBOL_ADDRESS (sym_map, sym, true);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    /* We already found the symbol.  The module (and therefore its load
Packit 6c4009
       address) is also known.  */
Packit 6c4009
    value = SYMBOL_ADDRESS (l, sym, true);
Packit 6c4009
Packit 6c4009
  /* Apply the relocation with that value.  */
Packit 6c4009
  *(got + local_gotno + sym_index - gotsym) = value;
Packit 6c4009
Packit 6c4009
  return value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#if _MIPS_SIM == _ABIO32
Packit 6c4009
#define ELF_DL_FRAME_SIZE 40
Packit 6c4009
Packit 6c4009
#define ELF_DL_SAVE_ARG_REGS "\
Packit 6c4009
	sw	$15, 36($29)\n						      \
Packit 6c4009
	sw	$4, 16($29)\n						      \
Packit 6c4009
	sw	$5, 20($29)\n						      \
Packit 6c4009
	sw	$6, 24($29)\n						      \
Packit 6c4009
	sw	$7, 28($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
#define ELF_DL_RESTORE_ARG_REGS "\
Packit 6c4009
	lw	$31, 36($29)\n						      \
Packit 6c4009
	lw	$4, 16($29)\n						      \
Packit 6c4009
	lw	$5, 20($29)\n						      \
Packit 6c4009
	lw	$6, 24($29)\n						      \
Packit 6c4009
	lw	$7, 28($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
/* The PLT resolver should also save and restore $2 and $3, which are used
Packit 6c4009
   as arguments to MIPS16 stub functions.  */
Packit 6c4009
#define ELF_DL_PLT_FRAME_SIZE 48
Packit 6c4009
Packit 6c4009
#define ELF_DL_PLT_SAVE_ARG_REGS \
Packit 6c4009
	ELF_DL_SAVE_ARG_REGS "\
Packit 6c4009
	sw	$2, 40($29)\n						      \
Packit 6c4009
	sw	$3, 44($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
#define ELF_DL_PLT_RESTORE_ARG_REGS \
Packit 6c4009
	ELF_DL_RESTORE_ARG_REGS "\
Packit 6c4009
	lw	$2, 40($29)\n						      \
Packit 6c4009
	lw	$3, 44($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
#define IFABIO32(X) X
Packit 6c4009
#define IFNEWABI(X)
Packit 6c4009
Packit 6c4009
#else /* _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 */
Packit 6c4009
Packit 6c4009
#define ELF_DL_FRAME_SIZE 80
Packit 6c4009
Packit 6c4009
#define ELF_DL_SAVE_ARG_REGS "\
Packit 6c4009
	sd	$15, 72($29)\n						      \
Packit 6c4009
	sd	$4, 8($29)\n						      \
Packit 6c4009
	sd	$5, 16($29)\n						      \
Packit 6c4009
	sd	$6, 24($29)\n						      \
Packit 6c4009
	sd	$7, 32($29)\n						      \
Packit 6c4009
	sd	$8, 40($29)\n						      \
Packit 6c4009
	sd	$9, 48($29)\n						      \
Packit 6c4009
	sd	$10, 56($29)\n						      \
Packit 6c4009
	sd	$11, 64($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
#define ELF_DL_RESTORE_ARG_REGS "\
Packit 6c4009
	ld	$31, 72($29)\n						      \
Packit 6c4009
	ld	$4, 8($29)\n						      \
Packit 6c4009
	ld	$5, 16($29)\n						      \
Packit 6c4009
	ld	$6, 24($29)\n						      \
Packit 6c4009
	ld	$7, 32($29)\n						      \
Packit 6c4009
	ld	$8, 40($29)\n						      \
Packit 6c4009
	ld	$9, 48($29)\n						      \
Packit 6c4009
	ld	$10, 56($29)\n						      \
Packit 6c4009
	ld	$11, 64($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
/* The PLT resolver should also save and restore $2 and $3, which are used
Packit 6c4009
   as arguments to MIPS16 stub functions.  */
Packit 6c4009
#define ELF_DL_PLT_FRAME_SIZE 96
Packit 6c4009
Packit 6c4009
#define ELF_DL_PLT_SAVE_ARG_REGS \
Packit 6c4009
	ELF_DL_SAVE_ARG_REGS "\
Packit 6c4009
	sd	$2, 80($29)\n						      \
Packit 6c4009
	sd	$3, 88($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
#define ELF_DL_PLT_RESTORE_ARG_REGS \
Packit 6c4009
	ELF_DL_RESTORE_ARG_REGS "\
Packit 6c4009
	ld	$2, 80($29)\n						      \
Packit 6c4009
	ld	$3, 88($29)\n						      \
Packit 6c4009
"
Packit 6c4009
Packit 6c4009
#define IFABIO32(X)
Packit 6c4009
#define IFNEWABI(X) X
Packit 6c4009
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifndef __mips16
Packit 6c4009
asm ("\n\
Packit 6c4009
	.text\n\
Packit 6c4009
	.align	2\n\
Packit 6c4009
	.set	nomips16\n\
Packit 6c4009
	.globl	_dl_runtime_resolve\n\
Packit 6c4009
	.type	_dl_runtime_resolve,@function\n\
Packit 6c4009
	.ent	_dl_runtime_resolve\n\
Packit 6c4009
_dl_runtime_resolve:\n\
Packit 6c4009
	.frame	$29, " STRINGXP(ELF_DL_FRAME_SIZE) ", $31\n\
Packit 6c4009
	.set noreorder\n\
Packit 6c4009
	# Save GP.\n\
Packit 6c4009
1:	move	$3, $28\n\
Packit 6c4009
	# Save arguments and sp value in stack.\n\
Packit 6c4009
	" STRINGXP(PTR_SUBIU) "  $29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
Packit 6c4009
	# Modify t9 ($25) so as to point .cpload instruction.\n\
Packit 6c4009
	" IFABIO32(STRINGXP(PTR_ADDIU) "	$25, (2f-1b)\n") "\
Packit 6c4009
	# Compute GP.\n\
Packit 6c4009
2:	" STRINGXP(SETUP_GP) "\n\
Packit 6c4009
	" STRINGXV(SETUP_GP64 (0, _dl_runtime_resolve)) "\n\
Packit 6c4009
	.set reorder\n\
Packit 6c4009
	# Save slot call pc.\n\
Packit 6c4009
	move	$2, $31\n\
Packit 6c4009
	" IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
Packit 6c4009
	" ELF_DL_SAVE_ARG_REGS "\
Packit 6c4009
	move	$4, $24\n\
Packit 6c4009
	move	$5, $15\n\
Packit 6c4009
	move	$6, $3\n\
Packit 6c4009
	move	$7, $2\n\
Packit 6c4009
	jal	__dl_runtime_resolve\n\
Packit 6c4009
	" ELF_DL_RESTORE_ARG_REGS "\
Packit 6c4009
	" STRINGXP(RESTORE_GP64) "\n\
Packit 6c4009
	" STRINGXP(PTR_ADDIU) "	$29, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\
Packit 6c4009
	move	$25, $2\n\
Packit 6c4009
	jr	$25\n\
Packit 6c4009
	.end	_dl_runtime_resolve\n\
Packit 6c4009
	.previous\n\
Packit 6c4009
");
Packit 6c4009
Packit 6c4009
/* Assembler veneer called from the PLT header code when using PLTs.
Packit 6c4009
Packit 6c4009
   Code in each PLT entry and the PLT header fills in the arguments to
Packit 6c4009
   this function:
Packit 6c4009
Packit 6c4009
   - $15 (o32 t7, n32/n64 t3) - caller's return address
Packit 6c4009
   - $24 (t8) - PLT entry index
Packit 6c4009
   - $25 (t9) - address of _dl_runtime_pltresolve
Packit 6c4009
   - o32 $28 (gp), n32/n64 $14 (t2) - address of .got.plt
Packit 6c4009
Packit 6c4009
   Different registers are used for .got.plt because the ABI was
Packit 6c4009
   originally designed for o32, where gp was available (call
Packit 6c4009
   clobbered).  On n32/n64 gp is call saved.
Packit 6c4009
Packit 6c4009
   _dl_fixup needs:
Packit 6c4009
Packit 6c4009
   - $4 (a0) - link map address
Packit 6c4009
   - $5 (a1) - .rel.plt offset (== PLT entry index * 8)  */
Packit 6c4009
Packit 6c4009
asm ("\n\
Packit 6c4009
	.text\n\
Packit 6c4009
	.align	2\n\
Packit 6c4009
	.set	nomips16\n\
Packit 6c4009
	.globl	_dl_runtime_pltresolve\n\
Packit 6c4009
	.type	_dl_runtime_pltresolve,@function\n\
Packit 6c4009
	.ent	_dl_runtime_pltresolve\n\
Packit 6c4009
_dl_runtime_pltresolve:\n\
Packit 6c4009
	.frame	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
Packit 6c4009
	.set noreorder\n\
Packit 6c4009
	# Save arguments and sp value in stack.\n\
Packit 6c4009
1:	" STRINGXP(PTR_SUBIU) "	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
Packit 6c4009
	" IFABIO32(STRINGXP(PTR_L) "	$13, " STRINGXP(PTRSIZE) "($28)") "\n\
Packit 6c4009
	" IFNEWABI(STRINGXP(PTR_L) "	$13, " STRINGXP(PTRSIZE) "($14)") "\n\
Packit 6c4009
	# Modify t9 ($25) so as to point .cpload instruction.\n\
Packit 6c4009
	" IFABIO32(STRINGXP(PTR_ADDIU) "	$25, (2f-1b)\n") "\
Packit 6c4009
	# Compute GP.\n\
Packit 6c4009
2:	" STRINGXP(SETUP_GP) "\n\
Packit 6c4009
	" STRINGXV(SETUP_GP64 (0, _dl_runtime_pltresolve)) "\n\
Packit 6c4009
	.set reorder\n\
Packit 6c4009
	" IFABIO32(STRINGXP(CPRESTORE(32))) "\n\
Packit 6c4009
	" ELF_DL_PLT_SAVE_ARG_REGS "\
Packit 6c4009
	move	$4, $13\n\
Packit 6c4009
	sll	$5, $24, " STRINGXP(PTRLOG) " + 1\n\
Packit 6c4009
	jal	_dl_fixup\n\
Packit 6c4009
	move	$25, $2\n\
Packit 6c4009
	" ELF_DL_PLT_RESTORE_ARG_REGS "\
Packit 6c4009
	" STRINGXP(RESTORE_GP64) "\n\
Packit 6c4009
	" STRINGXP(PTR_ADDIU) "	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
Packit 6c4009
	jr	$25\n\
Packit 6c4009
	.end	_dl_runtime_pltresolve\n\
Packit 6c4009
	.previous\n\
Packit 6c4009
");
Packit 6c4009
Packit 6c4009
#elif _MIPS_SIM == _ABIO32 /* __mips16 */
Packit 6c4009
/* MIPS16 version, O32 only.  */
Packit 6c4009
asm ("\n\
Packit 6c4009
	.text\n\
Packit 6c4009
	.align	2\n\
Packit 6c4009
	.set	mips16\n\
Packit 6c4009
	.globl	_dl_runtime_resolve\n\
Packit 6c4009
	.type	_dl_runtime_resolve,@function\n\
Packit 6c4009
	.ent	_dl_runtime_resolve\n\
Packit 6c4009
_dl_runtime_resolve:\n\
Packit 6c4009
	.frame	$29, " STRINGXP (ELF_DL_FRAME_SIZE) ", $31\n\
Packit 6c4009
	# Save arguments and sp value in stack.\n\t"
Packit 6c4009
# if _MIPS_ISA >= _MIPS_ISA_MIPS32
Packit 6c4009
	"save	" STRINGXP (ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
Packit 6c4009
# else
Packit 6c4009
	"addiu	$sp, -" STRINGXP (ELF_DL_FRAME_SIZE) "\n\
Packit 6c4009
	sw	$7, 32($sp)\n\
Packit 6c4009
	sw	$6, 28($sp)\n\
Packit 6c4009
	sw	$5, 24($sp)\n\
Packit 6c4009
	sw	$4, 20($sp)\n\t"
Packit 6c4009
# endif
Packit 6c4009
	"# Preserve caller's $ra, for RESTORE instruction below.\n\
Packit 6c4009
	move	$5, $15\n\
Packit 6c4009
	sw	$5, 36($sp)\n\
Packit 6c4009
	# Compute GP into $2.\n\
Packit 6c4009
	li	$2, %hi(_gp_disp)\n\
Packit 6c4009
	addiu	$3, $pc, %lo(_gp_disp)\n\
Packit 6c4009
	sll	$2, 16\n\
Packit 6c4009
	addu	$2, $3\n\
Packit 6c4009
	lw	$3, %got(__dl_runtime_resolve)($2)\n\
Packit 6c4009
	move	$4, $24\n\
Packit 6c4009
	addiu	$3, %lo(__dl_runtime_resolve)\n\
Packit 6c4009
	move	$7, $ra\n\
Packit 6c4009
	move	$6, $28\n\
Packit 6c4009
	move	$25, $3\n\
Packit 6c4009
	jalr	$3\n\t"
Packit 6c4009
# if _MIPS_ISA >= _MIPS_ISA_MIPS32
Packit 6c4009
	"restore " STRINGXP(ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
Packit 6c4009
# else
Packit 6c4009
	"# Restore $ra, move placed further down to hide latency.\n\
Packit 6c4009
	lw	$4, 36($sp)\n\
Packit 6c4009
	lw	$5, 24($sp)\n\
Packit 6c4009
	lw	$6, 28($sp)\n\
Packit 6c4009
	lw	$7, 32($sp)\n\
Packit 6c4009
	move	$ra, $4\n\
Packit 6c4009
	lw	$4, 20($sp)\n\
Packit 6c4009
	addiu	$sp, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\t"
Packit 6c4009
# endif
Packit 6c4009
	"move	$25, $2\n\
Packit 6c4009
	jr	$2\n\
Packit 6c4009
	.end	_dl_runtime_resolve\n\
Packit 6c4009
	.previous\n\
Packit 6c4009
");
Packit 6c4009
Packit 6c4009
asm ("\n\
Packit 6c4009
	.text\n\
Packit 6c4009
	.align	2\n\
Packit 6c4009
	.set	mips16\n\
Packit 6c4009
	.globl	_dl_runtime_pltresolve\n\
Packit 6c4009
	.type	_dl_runtime_pltresolve,@function\n\
Packit 6c4009
	.ent	_dl_runtime_pltresolve\n\
Packit 6c4009
_dl_runtime_pltresolve:\n\
Packit 6c4009
	.frame	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
Packit 6c4009
	# Save arguments and sp value in stack.\n\t"
Packit 6c4009
# if _MIPS_ISA >= _MIPS_ISA_MIPS32
Packit 6c4009
	"save	" STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
Packit 6c4009
# else
Packit 6c4009
	"addiu	$sp, -" STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
Packit 6c4009
	sw	$7, 40($sp)\n\
Packit 6c4009
	sw	$6, 36($sp)\n\
Packit 6c4009
	sw	$5, 32($sp)\n\
Packit 6c4009
	sw	$4, 28($sp)\n\t"
Packit 6c4009
# endif
Packit 6c4009
	"# Preserve MIPS16 stub function arguments.\n\
Packit 6c4009
	sw	$3, 20($sp)\n\
Packit 6c4009
	sw	$2, 16($sp)\n\
Packit 6c4009
	# Preserve caller's $ra, for RESTORE instruction below.\n\
Packit 6c4009
	move	$3, $15\n\
Packit 6c4009
	sw	$3, 44($sp)\n\
Packit 6c4009
	# Compute GP into $2.\n\
Packit 6c4009
	li	$2, %hi(_gp_disp)\n\
Packit 6c4009
	addiu	$3, $pc, %lo(_gp_disp)\n\
Packit 6c4009
	sll	$2, 16\n\
Packit 6c4009
	addu	$2, $3\n\
Packit 6c4009
	# Save GP value in slot.\n\
Packit 6c4009
	sw	$2, 24($sp)\n\
Packit 6c4009
	# Load _dl_fixup address.\n\
Packit 6c4009
	lw	$6, %call16(_dl_fixup)($2)\n\
Packit 6c4009
	# Load link map address.\n\
Packit 6c4009
	move	$3, $28\n\
Packit 6c4009
	lw	$4, " STRINGXP (PTRSIZE) "($3)\n\
Packit 6c4009
	move	$5, $24\n\
Packit 6c4009
	sll	$5, " STRINGXP (PTRLOG) " + 1\n\
Packit 6c4009
	# Call _dl_fixup.\n\
Packit 6c4009
	move	$25, $6\n\
Packit 6c4009
	jalr	$6\n\
Packit 6c4009
	move	$25, $2\n\
Packit 6c4009
	# Reload GP value into $28.\n\
Packit 6c4009
	lw	$3, 24($sp)\n\
Packit 6c4009
	move	$28, $3\n\
Packit 6c4009
	lw	$3, 16($sp)\n\
Packit 6c4009
	move	$15, $3\n\
Packit 6c4009
	lw	$3, 20($sp)\n\t"
Packit 6c4009
# if _MIPS_ISA >= _MIPS_ISA_MIPS32
Packit 6c4009
	"restore " STRINGXP (ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
Packit 6c4009
# else
Packit 6c4009
	"# Restore $ra, move placed further down to hide latency.\n\
Packit 6c4009
	lw	$4, 44($sp)\n\
Packit 6c4009
	lw	$5, 32($sp)\n\
Packit 6c4009
	lw	$6, 36($sp)\n\
Packit 6c4009
	lw	$7, 40($sp)\n\
Packit 6c4009
	move	$ra, $4\n\
Packit 6c4009
	lw	$4, 28($sp)\n\
Packit 6c4009
	addiu	$sp, " STRINGXP (ELF_DL_PLT_FRAME_SIZE) "\n\t"
Packit 6c4009
# endif
Packit 6c4009
	".set	noreorder\n\
Packit 6c4009
	jr	$2\n\
Packit 6c4009
	 move	$2, $15\n\
Packit 6c4009
	.set	reorder\n\
Packit 6c4009
	.end	_dl_runtime_pltresolve\n\
Packit 6c4009
	.previous\n\
Packit 6c4009
");
Packit 6c4009
Packit 6c4009
#else /* __mips16 && _MIPS_SIM != _ABIO32 */
Packit 6c4009
# error "MIPS16 support for N32/N64 not implemented"
Packit 6c4009
Packit 6c4009
#endif /* __mips16 */