1  /* Machine-dependent ELF dynamic relocation inline functions.  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  #ifndef dl_machine_h
      21  #define dl_machine_h
      22  
      23  #define ELF_MACHINE_NAME "MIPS"
      24  
      25  #include <entry.h>
      26  
      27  #ifndef ENTRY_POINT
      28  #error ENTRY_POINT needs to be defined for MIPS.
      29  #endif
      30  
      31  #include <sgidefs.h>
      32  #include <sysdep.h>
      33  #include <sys/asm.h>
      34  #include <dl-tls.h>
      35  #include <dl-static-tls.h>
      36  #include <dl-machine-rel.h>
      37  
      38  /* The offset of gp from GOT might be system-dependent.  It's set by
      39     ld.  The same value is also */
      40  #define OFFSET_GP_GOT 0x7ff0
      41  
      42  #ifndef _RTLD_PROLOGUE
      43  # define _RTLD_PROLOGUE(entry)						\
      44  	".globl\t" __STRING(entry) "\n\t"				\
      45  	".ent\t" __STRING(entry) "\n\t"					\
      46  	".type\t" __STRING(entry) ", @function\n"			\
      47  	__STRING(entry) ":\n\t"
      48  #endif
      49  
      50  #ifndef _RTLD_EPILOGUE
      51  # define _RTLD_EPILOGUE(entry)						\
      52  	".end\t" __STRING(entry) "\n\t"					\
      53  	".size\t" __STRING(entry) ", . - " __STRING(entry) "\n\t"
      54  #endif
      55  
      56  /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.
      57     This only makes sense on MIPS when using PLTs, so choose the
      58     PLT relocation (not encountered when not using PLTs).  */
      59  #define ELF_MACHINE_JMP_SLOT			R_MIPS_JUMP_SLOT
      60  #define elf_machine_type_class(type) \
      61    ((((type) == ELF_MACHINE_JMP_SLOT) * ELF_RTYPE_CLASS_PLT)	\
      62     | (((type) == R_MIPS_COPY) * ELF_RTYPE_CLASS_COPY))
      63  
      64  #if ((defined __mips_nan2008 && !defined HAVE_MIPS_NAN2008) \
      65       || (!defined __mips_nan2008 && defined HAVE_MIPS_NAN2008))
      66  # error "Configuration inconsistency: __mips_nan2008 != HAVE_MIPS_NAN2008, overridden CFLAGS?"
      67  #endif
      68  #ifdef __mips_nan2008
      69  # define ELF_MACHINE_NAN2008 EF_MIPS_NAN2008
      70  #else
      71  # define ELF_MACHINE_NAN2008 0
      72  #endif
      73  
      74  /* Return nonzero iff ELF header is compatible with the running host.  */
      75  static inline int __attribute_used__
      76  elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
      77  {
      78  #if _MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIN32
      79    /* Don't link o32 and n32 together.  */
      80    if (((ehdr->e_flags & EF_MIPS_ABI2) != 0) != (_MIPS_SIM == _ABIN32))
      81      return 0;
      82  #endif
      83  
      84    /* Don't link 2008-NaN and legacy-NaN objects together.  */
      85    if ((ehdr->e_flags & EF_MIPS_NAN2008) != ELF_MACHINE_NAN2008)
      86      return 0;
      87  
      88    /* Ensure that the old O32 FP64 ABI is never loaded, it is not supported
      89       on linux.  */
      90    if (ehdr->e_flags & EF_MIPS_FP64)
      91      return 0;
      92  
      93    switch (ehdr->e_machine)
      94      {
      95      case EM_MIPS:
      96      case EM_MIPS_RS3_LE:
      97        return 1;
      98      default:
      99        return 0;
     100      }
     101  }
     102  
     103  static inline ElfW(Addr) *
     104  elf_mips_got_from_gpreg (ElfW(Addr) gpreg)
     105  {
     106    /* FIXME: the offset of gp from GOT may be system-dependent. */
     107    return (ElfW(Addr) *) (gpreg - OFFSET_GP_GOT);
     108  }
     109  
     110  /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
     111     first element of the GOT.  This must be inlined in a function which
     112     uses global data.  We assume its $gp points to the primary GOT.  */
     113  static inline ElfW(Addr)
     114  elf_machine_dynamic (void)
     115  {
     116    register ElfW(Addr) gp __asm__ ("$28");
     117    return *elf_mips_got_from_gpreg (gp);
     118  }
     119  
     120  #define STRINGXP(X) __STRING(X)
     121  #define STRINGXV(X) STRINGV_(X)
     122  #define STRINGV_(...) # __VA_ARGS__
     123  
     124  /* Return the run-time load address of the shared object.  */
     125  static inline ElfW(Addr)
     126  elf_machine_load_address (void)
     127  {
     128    ElfW(Addr) addr;
     129  #ifndef __mips16
     130    asm ("	.set noreorder\n"
     131         "	" STRINGXP (PTR_LA) " %0, 0f\n"
     132  # if !defined __mips_isa_rev || __mips_isa_rev < 6
     133         "	bltzal $0, 0f\n"
     134         "	nop\n"
     135         "0:	" STRINGXP (PTR_SUBU) " %0, $31, %0\n"
     136  # else
     137         "0:	addiupc $31, 0\n"
     138         "	" STRINGXP (PTR_SUBU) " %0, $31, %0\n"
     139  # endif
     140         "	.set reorder\n"
     141         :	"=r" (addr)
     142         :	/* No inputs */
     143         :	"$31");
     144  #else
     145    ElfW(Addr) tmp;
     146    asm ("	.set noreorder\n"
     147         "	move %1,$gp\n"
     148         "	lw %1,%%got(0f)(%1)\n"
     149         "0:	.fill 0\n"		/* Clear the ISA bit on 0:.  */
     150         "	la %0,0b\n"
     151         "	addiu %1,%%lo(0b)\n"
     152         "	subu %0,%1\n"
     153         "	.set reorder\n"
     154         :	"=d" (addr), "=d" (tmp)
     155         :	/* No inputs */);
     156  #endif
     157    return addr;
     158  }
     159  
     160  /* The MSB of got[1] of a gnu object is set to identify gnu objects.  */
     161  #if _MIPS_SIM == _ABI64
     162  # define ELF_MIPS_GNU_GOT1_MASK	0x8000000000000000L
     163  #else
     164  # define ELF_MIPS_GNU_GOT1_MASK	0x80000000L
     165  #endif
     166  
     167  /* We can't rely on elf_machine_got_rel because _dl_object_relocation_scope
     168     fiddles with global data.  */
     169  #define ELF_MACHINE_BEFORE_RTLD_RELOC(bootstrap_map, dynamic_info)	\
     170  do {									\
     171    struct link_map *map = bootstrap_map;					\
     172    ElfW(Sym) *sym;							\
     173    ElfW(Addr) *got;							\
     174    int i, n;								\
     175  									\
     176    got = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);			\
     177  									\
     178    if (__builtin_expect (map->l_addr == 0, 1))				\
     179      break;								\
     180  									\
     181    /* got[0] is reserved. got[1] is also reserved for the dynamic object	\
     182       generated by gnu ld. Skip these reserved entries from		\
     183       relocation.  */							\
     184    i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1;				\
     185    n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;			\
     186  									\
     187    /* Add the run-time displacement to all local got entries. */		\
     188    while (i < n)								\
     189      got[i++] += map->l_addr;						\
     190  									\
     191    /* Handle global got entries. */					\
     192    got += n;								\
     193    sym = (ElfW(Sym) *) D_PTR(map, l_info[DT_SYMTAB])			\
     194         + map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;			\
     195    i = (map->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val			\
     196         - map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val);			\
     197  									\
     198    while (i--)								\
     199      {									\
     200        if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON)	\
     201  	*got = SYMBOL_ADDRESS (map, sym, true);				\
     202        else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC			\
     203  	       && *got != sym->st_value)				\
     204  	*got += map->l_addr;						\
     205        else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)		\
     206  	{								\
     207  	  if (sym->st_other == 0)					\
     208  	    *got += map->l_addr;					\
     209  	}								\
     210        else								\
     211  	*got = SYMBOL_ADDRESS (map, sym, true);				\
     212  									\
     213        got++;								\
     214        sym++;								\
     215      }									\
     216  } while(0)
     217  
     218  
     219  /* Mask identifying addresses reserved for the user program,
     220     where the dynamic linker should not map anything.  */
     221  #define ELF_MACHINE_USER_ADDRESS_MASK	0x80000000UL
     222  
     223  
     224  /* Initial entry point code for the dynamic linker.
     225     The C function `_dl_start' is the real entry point;
     226     its return value is the user program's entry point.
     227     Note how we have to be careful about two things:
     228  
     229     1) That we allocate a minimal stack of 24 bytes for
     230        every function call, the MIPS ABI states that even
     231        if all arguments are passed in registers the procedure
     232        called can use the 16 byte area pointed to by $sp
     233        when it is called to store away the arguments passed
     234        to it.
     235  
     236     2) That under Unix the entry is named __start
     237        and not just plain _start.  */
     238  
     239  #ifndef __mips16
     240  # if !defined __mips_isa_rev || __mips_isa_rev < 6
     241  #  define LCOFF STRINGXP(.Lcof2)
     242  #  define LOAD_31 STRINGXP(bltzal $8) "," STRINGXP(.Lcof2)
     243  # else
     244  #  define LCOFF STRINGXP(.Lcof1)
     245  #  define LOAD_31 "addiupc $31, 0"
     246  # endif
     247  # define RTLD_START asm (\
     248  	".text\n\
     249  	" _RTLD_PROLOGUE(ENTRY_POINT) "\
     250  	" STRINGXV(SETUP_GPX($25)) "\n\
     251  	" STRINGXV(SETUP_GPX64($18,$25)) "\n\
     252  	# i386 ABI book says that the first entry of GOT holds\n\
     253  	# the address of the dynamic structure. Though MIPS ABI\n\
     254  	# doesn't say nothing about this, I emulate this here.\n\
     255  	" STRINGXP(PTR_LA) " $4, _DYNAMIC\n\
     256  	# Subtract OFFSET_GP_GOT\n\
     257  	" STRINGXP(PTR_S) " $4, -0x7ff0($28)\n\
     258  	move $4, $29\n\
     259  	" STRINGXP(PTR_SUBIU) " $29, 16\n\
     260  	\n\
     261  	" STRINGXP(PTR_LA) " $8, " LCOFF "\n\
     262  .Lcof1:	" LOAD_31 "\n\
     263  .Lcof2:	" STRINGXP(PTR_SUBU) " $8, $31, $8\n\
     264  	\n\
     265  	" STRINGXP(PTR_LA) " $25, _dl_start\n\
     266  	" STRINGXP(PTR_ADDU) " $25, $8\n\
     267  	jalr $25\n\
     268  	\n\
     269  	" STRINGXP(PTR_ADDIU) " $29, 16\n\
     270  	# Get the value of label '_dl_start_user' in t9 ($25).\n\
     271  	" STRINGXP(PTR_LA) " $25, _dl_start_user\n\
     272  	" _RTLD_EPILOGUE(ENTRY_POINT) "\
     273  	\n\
     274  	\n\
     275  	" _RTLD_PROLOGUE(_dl_start_user) "\
     276  	" STRINGXP(SETUP_GP) "\n\
     277  	" STRINGXV(SETUP_GP64($18,_dl_start_user)) "\n\
     278  	move $16, $28\n\
     279  	# Save the user entry point address in a saved register.\n\
     280  	move $17, $2\n\
     281  	# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
     282  	" STRINGXP(PTR_L) " $4, _rtld_local\n\
     283  	" STRINGXP(PTR_L) /* or lw???  fixme */ " $5, 0($29)\n\
     284  	" STRINGXP(PTR_LA) " $6, " STRINGXP (PTRSIZE) "($29)\n\
     285  	sll $7, $5, " STRINGXP (PTRLOG) "\n\
     286  	" STRINGXP(PTR_ADDU) " $7, $7, $6\n\
     287  	" STRINGXP(PTR_ADDU) " $7, $7, " STRINGXP (PTRSIZE) " \n\
     288  	# Make sure the stack pointer is aligned for _dl_init.\n\
     289  	and $2, $29, -2 * " STRINGXP(SZREG) "\n\
     290  	move $8, $29\n\
     291  	" STRINGXP(PTR_SUBIU) " $29, $2, 32\n\
     292  	" STRINGXP(PTR_S) " $8, (32 - " STRINGXP(SZREG) ")($29)\n\
     293  	" STRINGXP(SAVE_GP(16)) "\n\
     294  	# Call the function to run the initializers.\n\
     295  	jal _dl_init\n\
     296  	# Restore the stack pointer for _start.\n\
     297  	" STRINGXP(PTR_L)  " $29, (32 - " STRINGXP(SZREG) ")($29)\n\
     298  	# Pass our finalizer function to the user in $2 as per ELF ABI.\n\
     299  	" STRINGXP(PTR_LA) " $2, _dl_fini\n\
     300  	# Jump to the user entry point.\n\
     301  	move $25, $17\n\
     302  	jr $25\n\t"\
     303  	_RTLD_EPILOGUE(_dl_start_user)\
     304  	".previous"\
     305  );
     306  
     307  #else /* __mips16 */
     308  /* MIPS16 version.  We currently only support O32 under MIPS16; the proper
     309     assembly preprocessor abstractions will need to be added if other ABIs
     310     are to be supported.  */
     311  
     312  # define RTLD_START asm (\
     313  	".text\n\
     314  	.set mips16\n\
     315  	" _RTLD_PROLOGUE (ENTRY_POINT) "\
     316  	# Construct GP value in $3.\n\
     317  	li $3, %hi(_gp_disp)\n\
     318  	addiu $4, $pc, %lo(_gp_disp)\n\
     319  	sll $3, 16\n\
     320  	addu $3, $4\n\
     321  	move $28, $3\n\
     322  	lw $4, %got(_DYNAMIC)($3)\n\
     323  	sw $4, -0x7ff0($3)\n\
     324  	move $4, $sp\n\
     325  	addiu $sp, -16\n\
     326  	# _dl_start() is sufficiently near to use pc-relative\n\
     327  	# load address.\n\
     328  	la $3, _dl_start\n\
     329  	move $25, $3\n\
     330  	jalr $3\n\
     331  	addiu $sp, 16\n\
     332  	" _RTLD_EPILOGUE (ENTRY_POINT) "\
     333  	\n\
     334  	\n\
     335  	" _RTLD_PROLOGUE (_dl_start_user) "\
     336  	li $16, %hi(_gp_disp)\n\
     337  	addiu $4, $pc, %lo(_gp_disp)\n\
     338  	sll $16, 16\n\
     339  	addu $16, $4\n\
     340  	move $17, $2\n\
     341  	move $28, $16\n\
     342  	# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
     343  	lw $4, %got(_rtld_local)($16)\n\
     344  	lw $4, 0($4)\n\
     345  	lw $5, 0($sp)\n\
     346  	addiu $6, $sp, " STRINGXP (PTRSIZE) "\n\
     347  	sll $7, $5, " STRINGXP (PTRLOG) "\n\
     348  	addu $7, $6\n\
     349  	addu $7, " STRINGXP (PTRSIZE) "\n\
     350  	# Make sure the stack pointer is aligned for _dl_init.\n\
     351  	li $2, 2 * " STRINGXP (SZREG) "\n\
     352  	neg $2, $2\n\
     353  	move $3, $sp\n\
     354  	and $2, $3\n\
     355  	sw $3, -" STRINGXP (SZREG) "($2)\n\
     356  	addiu $2, -32\n\
     357  	move $sp, $2\n\
     358  	sw $16, 16($sp)\n\
     359  	# Call the function to run the initializers.\n\
     360  	lw $2, %call16(_dl_init)($16)\n\
     361  	move $25, $2\n\
     362  	jalr $2\n\
     363  	# Restore the stack pointer for _start.\n\
     364  	lw $2, 32-" STRINGXP (SZREG) "($sp)\n\
     365  	move $sp, $2\n\
     366  	move $28, $16\n\
     367  	# Pass our finalizer function to the user in $2 as per ELF ABI.\n\
     368  	lw $2, %call16(_dl_fini)($16)\n\
     369  	# Jump to the user entry point.\n\
     370  	move $25, $17\n\
     371  	jr $17\n\t"\
     372  	_RTLD_EPILOGUE (_dl_start_user)\
     373  	".previous"\
     374  );
     375  
     376  #endif /* __mips16 */
     377  
     378  /* Names of the architecture-specific auditing callback functions.  */
     379  # if _MIPS_SIM == _ABIO32
     380  #  define ARCH_LA_PLTENTER mips_o32_gnu_pltenter
     381  #  define ARCH_LA_PLTEXIT mips_o32_gnu_pltexit
     382  # elif _MIPS_SIM == _ABIN32
     383  #  define ARCH_LA_PLTENTER mips_n32_gnu_pltenter
     384  #  define ARCH_LA_PLTEXIT mips_n32_gnu_pltexit
     385  # else
     386  #  define ARCH_LA_PLTENTER mips_n64_gnu_pltenter
     387  #  define ARCH_LA_PLTEXIT mips_n64_gnu_pltexit
     388  # endif
     389  
     390  /* We define an initialization function.  This is called very early in
     391     _dl_sysdep_start.  */
     392  #define DL_PLATFORM_INIT dl_platform_init ()
     393  
     394  static inline void __attribute__ ((unused))
     395  dl_platform_init (void)
     396  {
     397    if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
     398      /* Avoid an empty string which would disturb us.  */
     399      GLRO(dl_platform) = NULL;
     400  }
     401  
     402  /* For a non-writable PLT, rewrite the .got.plt entry at RELOC_ADDR to
     403     point at the symbol with address VALUE.  For a writable PLT, rewrite
     404     the corresponding PLT entry instead.  */
     405  static inline ElfW(Addr)
     406  elf_machine_fixup_plt (struct link_map *map, lookup_t t,
     407  		       const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
     408  		       const ElfW(Rel) *reloc,
     409  		       ElfW(Addr) *reloc_addr, ElfW(Addr) value)
     410  {
     411    return *reloc_addr = value;
     412  }
     413  
     414  static inline ElfW(Addr)
     415  elf_machine_plt_value (struct link_map *map, const ElfW(Rel) *reloc,
     416  		       ElfW(Addr) value)
     417  {
     418    return value;
     419  }
     420  
     421  #endif /* !dl_machine_h */
     422  
     423  #ifdef RESOLVE_MAP
     424  
     425  /* Perform a relocation described by R_INFO at the location pointed to
     426     by RELOC_ADDR.  SYM is the relocation symbol specified by R_INFO and
     427     MAP is the object containing the reloc.  */
     428  
     429  static inline void
     430  __attribute__ ((always_inline))
     431  elf_machine_reloc (struct link_map *map, struct r_scope_elem *scope[],
     432  		   ElfW(Addr) r_info, const ElfW(Sym) *sym,
     433  		   const struct r_found_version *version, void *reloc_addr,
     434  		   ElfW(Addr) r_addend, int inplace_p)
     435  {
     436    const unsigned long int r_type = ELFW(R_TYPE) (r_info);
     437    ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
     438  
     439  #if !defined RTLD_BOOTSTRAP && !defined SHARED
     440    /* This is defined in rtld.c, but nowhere in the static libc.a;
     441       make the reference weak so static programs can still link.  This
     442       declaration cannot be done when compiling rtld.c (i.e.  #ifdef
     443       RTLD_BOOTSTRAP) because rtld.c contains the common defn for
     444       _dl_rtld_map, which is incompatible with a weak decl in the same
     445       file.  */
     446    weak_extern (GL(dl_rtld_map));
     447  #endif
     448  
     449    switch (r_type)
     450      {
     451  #if !defined (RTLD_BOOTSTRAP)
     452  # if _MIPS_SIM == _ABI64
     453      case R_MIPS_TLS_DTPMOD64:
     454      case R_MIPS_TLS_DTPREL64:
     455      case R_MIPS_TLS_TPREL64:
     456  # else
     457      case R_MIPS_TLS_DTPMOD32:
     458      case R_MIPS_TLS_DTPREL32:
     459      case R_MIPS_TLS_TPREL32:
     460  # endif
     461        {
     462  	struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
     463  						r_type);
     464  
     465  	switch (r_type)
     466  	  {
     467  	  case R_MIPS_TLS_DTPMOD64:
     468  	  case R_MIPS_TLS_DTPMOD32:
     469  	    if (sym_map)
     470  	      *addr_field = sym_map->l_tls_modid;
     471  	    break;
     472  
     473  	  case R_MIPS_TLS_DTPREL64:
     474  	  case R_MIPS_TLS_DTPREL32:
     475  	    if (sym)
     476  	      {
     477  		if (inplace_p)
     478  		  r_addend = *addr_field;
     479  		*addr_field = r_addend + TLS_DTPREL_VALUE (sym);
     480  	      }
     481  	    break;
     482  
     483  	  case R_MIPS_TLS_TPREL32:
     484  	  case R_MIPS_TLS_TPREL64:
     485  	    if (sym)
     486  	      {
     487  		CHECK_STATIC_TLS (map, sym_map);
     488  		if (inplace_p)
     489  		  r_addend = *addr_field;
     490  		*addr_field = r_addend + TLS_TPREL_VALUE (sym_map, sym);
     491  	      }
     492  	    break;
     493  	  }
     494  
     495  	break;
     496        }
     497  #endif
     498  
     499  #if _MIPS_SIM == _ABI64
     500      case (R_MIPS_64 << 8) | R_MIPS_REL32:
     501  #else
     502      case R_MIPS_REL32:
     503  #endif
     504        {
     505  	int symidx = ELFW(R_SYM) (r_info);
     506  	ElfW(Addr) reloc_value;
     507  
     508  	if (inplace_p)
     509  	  /* Support relocations on mis-aligned offsets.  */
     510  	  __builtin_memcpy (&reloc_value, reloc_addr, sizeof (reloc_value));
     511  	else
     512  	  reloc_value = r_addend;
     513  
     514  	if (symidx)
     515  	  {
     516  	    const ElfW(Word) gotsym
     517  	      = (const ElfW(Word)) map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
     518  
     519  	    if ((ElfW(Word))symidx < gotsym)
     520  	      {
     521  		/* This wouldn't work for a symbol imported from other
     522  		   libraries for which there's no GOT entry, but MIPS
     523  		   requires every symbol referenced in a dynamic
     524  		   relocation to have a GOT entry in the primary GOT,
     525  		   so we only get here for locally-defined symbols.
     526  		   For section symbols, we should *NOT* be adding
     527  		   sym->st_value (per the definition of the meaning of
     528  		   S in reloc expressions in the ELF64 MIPS ABI),
     529  		   since it should have already been added to
     530  		   reloc_value by the linker, but older versions of
     531  		   GNU ld didn't add it, and newer versions don't emit
     532  		   useless relocations to section symbols any more, so
     533  		   it is safe to keep on adding sym->st_value, even
     534  		   though it's not ABI compliant.  Some day we should
     535  		   bite the bullet and stop doing this.  */
     536  #ifndef RTLD_BOOTSTRAP
     537  		if (map != &GL(dl_rtld_map))
     538  #endif
     539  		  reloc_value += SYMBOL_ADDRESS (map, sym, true);
     540  	      }
     541  	    else
     542  	      {
     543  #ifndef RTLD_BOOTSTRAP
     544  		const ElfW(Addr) *got
     545  		  = (const ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
     546  		const ElfW(Word) local_gotno
     547  		  = (const ElfW(Word))
     548  		    map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
     549  
     550  		reloc_value += got[symidx + local_gotno - gotsym];
     551  #endif
     552  	      }
     553  	  }
     554  	else
     555  #ifndef RTLD_BOOTSTRAP
     556  	  if (map != &GL(dl_rtld_map))
     557  #endif
     558  	    reloc_value += map->l_addr;
     559  
     560  	__builtin_memcpy (reloc_addr, &reloc_value, sizeof (reloc_value));
     561        }
     562        break;
     563  #ifndef RTLD_BOOTSTRAP
     564  #if _MIPS_SIM == _ABI64
     565      case (R_MIPS_64 << 8) | R_MIPS_GLOB_DAT:
     566  #else
     567      case R_MIPS_GLOB_DAT:
     568  #endif
     569        {
     570  	int symidx = ELFW(R_SYM) (r_info);
     571  	const ElfW(Word) gotsym
     572  	  = (const ElfW(Word)) map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
     573  
     574  	if (__builtin_expect ((ElfW(Word)) symidx >= gotsym, 1))
     575  	  {
     576  	    const ElfW(Addr) *got
     577  	      = (const ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
     578  	    const ElfW(Word) local_gotno
     579  	      = ((const ElfW(Word))
     580  		 map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val);
     581  
     582  	    ElfW(Addr) reloc_value = got[symidx + local_gotno - gotsym];
     583  	    __builtin_memcpy (reloc_addr, &reloc_value, sizeof (reloc_value));
     584  	  }
     585        }
     586        break;
     587  #endif
     588      case R_MIPS_NONE:		/* Alright, Wilbur.  */
     589        break;
     590  
     591      case R_MIPS_JUMP_SLOT:
     592        {
     593  	struct link_map *sym_map;
     594  	ElfW(Addr) value;
     595  
     596  	/* The addend for a jump slot relocation must always be zero:
     597  	   calls via the PLT always branch to the symbol's address and
     598  	   not to the address plus a non-zero offset.  */
     599  	if (r_addend != 0)
     600  	  _dl_signal_error (0, map->l_name, NULL,
     601  			    "found jump slot relocation with non-zero addend");
     602  
     603  	sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
     604  	value = SYMBOL_ADDRESS (sym_map, sym, true);
     605  	*addr_field = value;
     606  
     607  	break;
     608        }
     609  
     610      case R_MIPS_COPY:
     611        {
     612  	const ElfW(Sym) *const refsym = sym;
     613  	struct link_map *sym_map;
     614  	ElfW(Addr) value;
     615  
     616  	/* Calculate the address of the symbol.  */
     617  	sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
     618  	value = SYMBOL_ADDRESS (sym_map, sym, true);
     619  
     620  	if (__builtin_expect (sym == NULL, 0))
     621  	  /* This can happen in trace mode if an object could not be
     622  	     found.  */
     623  	  break;
     624  	if (__builtin_expect (sym->st_size > refsym->st_size, 0)
     625  	    || (__builtin_expect (sym->st_size < refsym->st_size, 0)
     626  		&& GLRO(dl_verbose)))
     627  	  {
     628  	    const char *strtab;
     629  
     630  	    strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
     631  	    _dl_error_printf ("\
     632    %s: Symbol `%s' has different size in shared object, consider re-linking\n",
     633  			      RTLD_PROGNAME, strtab + refsym->st_name);
     634  	  }
     635  	memcpy (reloc_addr, (void *) value,
     636  		sym->st_size < refsym->st_size
     637  		? sym->st_size : refsym->st_size);
     638  	break;
     639        }
     640  
     641  #if _MIPS_SIM == _ABI64
     642      case R_MIPS_64:
     643        /* For full compliance with the ELF64 ABI, one must precede the
     644  	 _REL32/_64 pair of relocations with a _64 relocation, such
     645  	 that the in-place addend is read as a 64-bit value.  IRIX
     646  	 didn't pick up on this requirement, so we treat the
     647  	 _REL32/_64 relocation as a 64-bit relocation even if it's by
     648  	 itself.  For ABI compliance, we ignore such _64 dummy
     649  	 relocations.  For RELA, this may be simply removed, since
     650  	 it's totally unnecessary.  */
     651        if (ELFW(R_SYM) (r_info) == 0)
     652  	break;
     653  #endif
     654        /* Fall through.  */
     655      default:
     656        _dl_reloc_bad_type (map, r_type, 0);
     657        break;
     658      }
     659  }
     660  
     661  /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
     662     MAP is the object containing the reloc.  */
     663  
     664  static inline void
     665  __attribute__ ((always_inline))
     666  elf_machine_rel (struct link_map *map, struct r_scope_elem *scope[],
     667  		 const ElfW(Rel) *reloc, const ElfW(Sym) *sym,
     668  		 const struct r_found_version *version, void *const reloc_addr,
     669  		 int skip_ifunc)
     670  {
     671    elf_machine_reloc (map, scope, reloc->r_info, sym, version, reloc_addr, 0, 1);
     672  }
     673  
     674  static inline void
     675  __attribute__((always_inline))
     676  elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
     677  			  void *const reloc_addr)
     678  {
     679    /* XXX Nothing to do.  There is no relative relocation, right?  */
     680  }
     681  
     682  static inline void
     683  __attribute__((always_inline))
     684  elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
     685  		      ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
     686  		      int skip_ifunc)
     687  {
     688    ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
     689    const unsigned int r_type = ELFW(R_TYPE) (reloc->r_info);
     690    /* Check for unexpected PLT reloc type.  */
     691    if (__builtin_expect (r_type == R_MIPS_JUMP_SLOT, 1))
     692      {
     693        if (__builtin_expect (map->l_mach.plt, 0) == 0)
     694  	{
     695  	  /* Nothing is required here since we only support lazy
     696  	     relocation in executables.  */
     697  	}
     698        else
     699  	*reloc_addr = map->l_mach.plt;
     700      }
     701    else
     702      _dl_reloc_bad_type (map, r_type, 1);
     703  }
     704  
     705  static inline void
     706  __attribute__ ((always_inline))
     707  elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[], const ElfW(Rela) *reloc,
     708  		  const ElfW(Sym) *sym, const struct r_found_version *version,
     709  		  void *const reloc_addr, int skip_ifunc)
     710  {
     711    elf_machine_reloc (map, scope, reloc->r_info, sym, version, reloc_addr,
     712  		     reloc->r_addend, 0);
     713  }
     714  
     715  static inline void
     716  __attribute__((always_inline))
     717  elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
     718  			   void *const reloc_addr)
     719  {
     720  }
     721  
     722  #ifndef RTLD_BOOTSTRAP
     723  /* Relocate GOT. */
     724  static inline void
     725  __attribute__((always_inline))
     726  elf_machine_got_rel (struct link_map *map, struct r_scope_elem *scope[], int lazy)
     727  {
     728    ElfW(Addr) *got;
     729    ElfW(Sym) *sym;
     730    const ElfW(Half) *vernum;
     731    int i, n, symidx;
     732  
     733  #define RESOLVE_GOTSYM(sym,vernum,sym_index,reloc)			  \
     734      ({									  \
     735        const ElfW(Sym) *ref = sym;					  \
     736        const struct r_found_version *version __attribute__ ((unused))	  \
     737  	= vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL;	  \
     738        struct link_map *sym_map;						  \
     739        sym_map = RESOLVE_MAP (map, scope, &ref, version, reloc);		  \
     740        SYMBOL_ADDRESS (sym_map, ref, true);				  \
     741      })
     742  
     743    if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
     744      vernum = (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
     745    else
     746      vernum = NULL;
     747  
     748    got = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
     749  
     750    n = map->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;
     751    /* The dynamic linker's local got entries have already been relocated.  */
     752    if (map != &GL(dl_rtld_map))
     753      {
     754        /* got[0] is reserved. got[1] is also reserved for the dynamic object
     755  	 generated by gnu ld. Skip these reserved entries from relocation.  */
     756        i = (got[1] & ELF_MIPS_GNU_GOT1_MASK)? 2 : 1;
     757  
     758        /* Add the run-time displacement to all local got entries if
     759  	 needed.  */
     760        if (__builtin_expect (map->l_addr != 0, 0))
     761  	{
     762  	  while (i < n)
     763  	    got[i++] += map->l_addr;
     764  	}
     765      }
     766  
     767    /* Handle global got entries. */
     768    got += n;
     769    /* Keep track of the symbol index.  */
     770    symidx = map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;
     771    sym = (ElfW(Sym) *) D_PTR (map, l_info[DT_SYMTAB]) + symidx;
     772    i = (map->l_info[DT_MIPS (SYMTABNO)]->d_un.d_val
     773         - map->l_info[DT_MIPS (GOTSYM)]->d_un.d_val);
     774  
     775    /* This loop doesn't handle Quickstart.  */
     776    while (i--)
     777      {
     778        if (sym->st_shndx == SHN_UNDEF)
     779  	{
     780  	  if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC && sym->st_value
     781  	      && !(sym->st_other & STO_MIPS_PLT))
     782  	    {
     783  	      if (lazy)
     784  		*got = SYMBOL_ADDRESS (map, sym, true);
     785  	      else
     786  		/* This is a lazy-binding stub, so we don't need the
     787  		   canonical address.  */
     788  		*got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_JUMP_SLOT);
     789  	    }
     790  	  else
     791  	    *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
     792  	}
     793        else if (sym->st_shndx == SHN_COMMON)
     794  	*got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
     795        else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
     796  	       && *got != sym->st_value)
     797  	{
     798  	  if (lazy)
     799  	    *got += map->l_addr;
     800  	  else
     801  	    /* This is a lazy-binding stub, so we don't need the
     802  	       canonical address.  */
     803  	    *got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_JUMP_SLOT);
     804  	}
     805        else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)
     806  	{
     807  	  if (sym->st_other == 0)
     808  	    *got += map->l_addr;
     809  	}
     810        else
     811  	*got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
     812  
     813        ++got;
     814        ++sym;
     815        ++symidx;
     816      }
     817  
     818  #undef RESOLVE_GOTSYM
     819  }
     820  #endif
     821  
     822  /* Set up the loaded object described by L so its stub function
     823     will jump to the on-demand fixup code __dl_runtime_resolve.  */
     824  
     825  static inline int
     826  __attribute__((always_inline))
     827  elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
     828  			   int lazy, int profile)
     829  {
     830  # ifndef RTLD_BOOTSTRAP
     831    ElfW(Addr) *got;
     832    extern void _dl_runtime_resolve (ElfW(Word));
     833    extern void _dl_runtime_pltresolve (void);
     834    extern int _dl_mips_gnu_objects;
     835  
     836    if (lazy)
     837      {
     838        /* The GOT entries for functions have not yet been filled in.
     839  	 Their initial contents will arrange when called to put an
     840  	 offset into the .dynsym section in t8, the return address
     841  	 in t7 and then jump to _GLOBAL_OFFSET_TABLE[0].  */
     842        got = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
     843  
     844        /* This function will get called to fix up the GOT entry indicated by
     845  	 the register t8, and then jump to the resolved address.  */
     846        got[0] = (ElfW(Addr)) &_dl_runtime_resolve;
     847  
     848        /* Store l to _GLOBAL_OFFSET_TABLE[1] for gnu object. The MSB
     849  	 of got[1] of a gnu object is set to identify gnu objects.
     850  	 Where we can store l for non gnu objects? XXX  */
     851        if ((got[1] & ELF_MIPS_GNU_GOT1_MASK) != 0)
     852  	got[1] = ((ElfW(Addr)) l | ELF_MIPS_GNU_GOT1_MASK);
     853        else
     854  	_dl_mips_gnu_objects = 0;
     855      }
     856  
     857    /* Relocate global offset table.  */
     858    elf_machine_got_rel (l, scope, lazy);
     859  
     860    /* If using PLTs, fill in the first two entries of .got.plt.  */
     861    if (l->l_info[DT_JMPREL] && lazy)
     862      {
     863        ElfW(Addr) *gotplt;
     864        gotplt = (ElfW(Addr) *) D_PTR (l, l_info[DT_MIPS (PLTGOT)]);
     865        /* If a library is prelinked but we have to relocate anyway,
     866  	 we have to be able to undo the prelinking of .got.plt.
     867  	 The prelinker saved the address of .plt for us here.  */
     868        if (gotplt[1])
     869  	l->l_mach.plt = gotplt[1] + l->l_addr;
     870        gotplt[0] = (ElfW(Addr)) &_dl_runtime_pltresolve;
     871        gotplt[1] = (ElfW(Addr)) l;
     872      }
     873  
     874  # endif
     875    return lazy;
     876  }
     877  
     878  #endif /* RESOLVE_MAP */