This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RE: [RFC] Add IFUNC support for MIPS


Hi,

I have revised the patch with more suggestions from the previous discussions. 
Detailed description is in the attached text file. I think I have got a better handle on
symbol preemption with this version, but I would really appreciate some input on that
aspect.

Previous discussions for reference:
https://www.sourceware.org/ml/binutils/2013-08/msg00121.html
https://www.sourceware.org/ml/binutils/2013-11/msg00155.html
https://sourceware.org/ml/binutils/2013-12/msg00058.html

Tests have been split out in to a separate patch and attached for review. 
These are carried forward from the previous iteration. I am working on translating 
more of my informal tests to the correct format.

Regards,
Faraz Shahbazker


bfd/ChangeLog:

	* bfd-in2.h (BFD_RELOC_MIPS_IRELATIVE): New relocation.
	* elf32-mips.c
	(elf_mips_eh_howto table): Howto for BFD_RELOC_MIPS_IRELATIVE.
	(bfd_elf32_bfd_reloc_type_lookup): Case for R_MIPS_IRELATIVE.
	(bfd_elf32_bfd_reloc_name_lookup): Case for R_MIPS_IRELATIVE.
	(mips_elf32_rtype_to_howto): Case for R_MIPS_IRELATIVE
	* elfxx-mips.c
	(struct mips_got_info): New fields general_gotno and
	assigned_general_gotno.
	(struct mips_elf_link_hash_entry): New fields for offset in to
	IPLT/IGOT and flags to indicate symbol needs IPLT or IRELOC.
	(mips_elf_link_hash_table): New fields for size of IPLT stubs
	and hash-table for local IFUNC symbols.
	(MIPS16_P): New macro to check ASE flag.
	(mips16_exec_iplt_entry): Template for mips16 IPLT stub.
	(mips32_exec_iplt_entry): Template for mips32 IPLT stub.
	(mips32r6_exec_iplt_entry): Template for mips32 R6 IPLT stub.
	(micromips32_exec_iplt_entry): Template for micromips32 stub.
	(mips64_exec_iplt_entry): Template for mips64 IPLT stub.
	(mips64_48b_exec_iplt_entry): Template for mips64 IPLT stub.
	(mips_elf_link_hash_newfunc): Initialization of new
	mips_elf_link_hash_entry elements.
	(mips_elf_create_stub_symbol): Set ISA bit in address and ST_MIPS16
	type for mips16 IPLT stubs.
	(mips_elf_rel_dyn_section): Moved up to avoid forward declaration.
	(mips_get_irel_section): New function.
	(mips_elf_allocate_ireloc): Likewise.
	(mips_elf_allocate_iplt): Likewise.
	(mips_elf_check_local_symbols): Likewise.
	(mips_elf_check_symbols): Allocate an IPLT entry and/or dynamic
	relocation for IFUNC symbols.
	(mips_elf_count_got_entry): Count local IFUNCs under general
	GOT entries instead of local.
	(mips_elf_got16_entry): Add argument h and pass it to
	mips_elf_create_local_got_entry instead of NULL.
	(mips_elf_create_local_got_entry): Add check for sufficient general
	GOT entries. Assign local IFUNCs from general GOT entries pool.
	(mips_elf_count_general_got_symbols): New function.
	(mips_elf_deallocate_irelocs): Likewise.
	(mips_elf_multi_got): Traverse primary GOT entries and deallocate
	previously allocated IRELOCs for symbols marked RELOC_ONLY.
	(mips_elf_create_ifunc_sections): New function.
	(get_local_sym_hash): Likewise.
        (mips_elf_calculate_relocation): Create hash-table for local IFUNC
	symbols and condition them to be accessed through GOT.
	Point IFUNC symbol value to IPLT stub for symbols that need IPLT.
	(mips_elf_create_dynamic_relocation): Emit IRELATIVE relocation
	instead of REL32 for local IFUNC reference.
        (_bfd_mips_elf_section_processing): Size .igot section.
	(_bfd_mips_elf_add_symbol_hook): Mark output as has_gnu_symbols.
	(_bfd_mips_elf_check_relocs): Check need to create IFUNC sections.
	If symbol is an IFUNC, don't convert it to an STT_FUNC. Relax error
	checking to allow local IFUNCs to be accessed via call16 reloc.
	(_bfd_mips_elf_adjust_dynamic_symbol): Add STT_GNU_IFUNC to sanity
	check.
	(_bfd_mips_elf_always_size_sections): Allocate relocs for local IFUNCs.
	(mips_elf_lay_out_got): Count general GOT entries. Offset local entries
	to follow general entries.
	(_bfd_mips_elf_size_dynamic_sections): Exclude IPLT and IGOT.
	Create dynamic tag for DT_MIPS_GENERAL_GOTNO if needed.
	(_bfd_mips_elf_relocate_section): Relax error checking to allow
	local IFUNCs to be accessed via standalone got16 reloc.
	(mips_elf_create_iplt): New function.
	(misp_elf_check_local_got_index): Likewise.
	(mips_elf_create_ireloc): Likewise.
	(_bfd_mips_elf_finish_dynamic_symbol): Create IPLT stub/IREL
	relocation for IFUNC symbols as necessary. Set IFUNC symbol value
	to the IPLT entry address for executable objects.
	(_bfd_mips_elf_finish_local_dynamic_symbol): New function.
	(_bfd_mips_elf_finish_dynamic_sections): Call
	_bfd_mips_elf_finish_local_dynamic_symbol for all local IFUNCs.
	Set values of dynamic tag - DT_MIPS_GENERAL_GOTNO.
	(local_htab_hash): New function, hash table for local IFUNCs.
	(loc_htab_eq): New comparison function for local IFUNC hash table.
	(_bfd_mips_elf_link_hash_table_free): New function.
	(_bfd_mips_elf_link_hash_table_create): Allocate a hash table for
	local IFUNCs.
	( _bfd_mips_elf_get_target_dtag): Add cases for dynamic tag -
	DT_MIPS_GENERAL_GOTNO.
	* libbfd.h
	(bfd_reloc_code_real_names): Entry for BFD_RELOC_MIPS_IRELATIVE.
	* reloc.c
	(ENUMDOC): BFD_RELOC_MIPS_IRELATIVE entry.

binutils/ChangeLog:

	* readelf.c:
	(get_mips_dynamic_type): Add case for DT_MIPS_GENERAL_GOTNO.
	(dynamic_section_mips_val): Add case for DT_MIPS_GENERAL_GOTNO.

elfcpp/ChangeLog:

	* elfcpp.h
	(enum DT): Add cases for dynamic tag DT_MIPS_GENERAL_GOTNO.

include/elf/ChangeLog:

	* mips.h
	(START_RELOC_NUMBERS): Entry for R_MIPS_IRELATIVE.
	(DT_MIPS_GENERAL_GOTNO): New dynamic tag.
---
 bfd/bfd-in2.h      |    3 +
 bfd/elf32-mips.c   |   22 ++
 bfd/elfxx-mips.c   |  991 ++++++++++++++++++++++++++++++++++++++++++++++++----
 bfd/libbfd.h       |    1 +
 bfd/reloc.c        |    5 +
 binutils/readelf.c |    3 +
 elfcpp/elfcpp.h    |    2 +
 include/elf/mips.h |    7 +
 8 files changed, 964 insertions(+), 70 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index c27db8d..7eec1b5 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -3038,6 +3038,9 @@ to compensate for the borrow when the low bits are added.  */
   BFD_RELOC_MIPS_JUMP_SLOT,
 
 
+/* MIPS support for STT_GNU_IFUNC.  */
+  BFD_RELOC_MIPS_IRELATIVE,
+
 /* Moxie ELF relocations.  */
   BFD_RELOC_MOXIE_10_PCREL,
 
diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
index fff08e5..7f9c9b4 100644
--- a/bfd/elf32-mips.c
+++ b/bfd/elf32-mips.c
@@ -1646,6 +1646,22 @@ static reloc_howto_type elf_mips_eh_howto =
 	 0xffffffff,	        /* dst_mask */
 	 FALSE);		/* pcrel_offset */
 
+/* STT_GNU_IFUNC support: */
+static reloc_howto_type elf_mips_irelative_howto =
+  HOWTO (R_MIPS_IRELATIVE,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield,/* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MIPS_IRELATIVE",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE);		/* pcrel_offset */
+
 /* Set the GP value for OUTPUT_BFD.  Returns FALSE if this is a
    dangerous relocation.  */
 
@@ -2126,6 +2142,8 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
       return &elf_mips_jump_slot_howto;
     case BFD_RELOC_MIPS_EH:
       return &elf_mips_eh_howto;
+    case BFD_RELOC_MIPS_IRELATIVE:
+      return &elf_mips_irelative_howto;
     }
 }
 
@@ -2173,6 +2191,8 @@ bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     return &elf_mips_jump_slot_howto;
   if (strcasecmp (elf_mips_eh_howto.name, r_name) == 0)
     return &elf_mips_eh_howto;
+  if (strcasecmp (elf_mips_irelative_howto.name, r_name) == 0)
+    return &elf_mips_irelative_howto;
 
   return NULL;
 }
@@ -2199,6 +2219,8 @@ mips_elf32_rtype_to_howto (unsigned int r_type,
       return &elf_mips_jump_slot_howto;
     case R_MIPS_EH:
       return &elf_mips_eh_howto;
+    case R_MIPS_IRELATIVE:
+      return &elf_mips_irelative_howto;
     default:
       if (r_type >= R_MICROMIPS_min && r_type < R_MICROMIPS_max)
 	return &elf_micromips_howto_table_rel[r_type - R_MICROMIPS_min];
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 9932453..521fcd6 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -45,6 +45,7 @@
 #include "coff/mips.h"
 
 #include "hashtab.h"
+#include "objalloc.h"
 
 /* Types of TLS GOT entry.  */
 enum mips_got_tls_type {
@@ -165,10 +166,14 @@ struct mips_got_info
   unsigned int tls_assigned_gotno;
   /* The number of local .got entries, eventually including page entries.  */
   unsigned int local_gotno;
+  /* The number of explicitly relocated .got entries.  */
+  unsigned int general_gotno;
   /* The maximum number of page entries needed.  */
   unsigned int page_gotno;
   /* The number of relocations needed for the GOT entries.  */
   unsigned int relocs;
+  /* The first unused general .got entry.  */
+  unsigned int assigned_general_gotno;
   /* The first unused local .got entry.  */
   unsigned int assigned_low_gotno;
   /* The last unused local .got entry.  */
@@ -375,6 +380,12 @@ struct mips_elf_link_hash_entry
      being called returns a floating point value.  */
   asection *call_fp_stub;
 
+  /* Offset into the IPLT table.  */
+  bfd_vma iplt_offset;
+
+  /* Offset into the IGOT table.  */
+  bfd_vma igot_offset;
+
   /* The highest GGA_* value that satisfies all references to this symbol.  */
   unsigned int global_got_area : 2;
 
@@ -413,6 +424,12 @@ struct mips_elf_link_hash_entry
 
   /* Does this symbol resolve to a PLT entry?  */
   unsigned int use_plt_entry : 1;
+
+  /* Does this symbol need an IPLT stub?  */
+  unsigned int needs_iplt : 1;
+
+  /* Does this ifunc symbol need an IGOT entry?  */
+  unsigned int needs_ireloc : 1;
 };
 
 /* MIPS ELF linker hash table.  */
@@ -485,6 +502,9 @@ struct mips_elf_link_hash_table
   /* The index of the next .got.plt entry to create.  */
   bfd_vma plt_got_index;
 
+  /* The size of an IPLT entry in bytes.  */
+  bfd_vma iplt_entry_size;
+
   /* The number of functions that need a lazy-binding stub.  */
   bfd_vma lazy_stub_count;
 
@@ -516,6 +536,10 @@ struct mips_elf_link_hash_table
 
   /* Is the PLT header compressed?  */
   unsigned int plt_header_is_comp : 1;
+
+  /* Used by local STT_GNU_IFUNC symbols.  */
+  htab_t loc_hash_table;
+  void *loc_hash_memory;
 };
 
 /* Get the MIPS ELF linker hash table from a link_info structure.  */
@@ -800,6 +824,10 @@ static bfd *reldyn_sorting_bfd;
 #define MICROMIPS_P(abfd) \
   ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0)
 
+/* Nonzero if ABFD has mips16 code.  */
+#define MIPS16_P(abfd) \
+  ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16) != 0)
+
 /* Nonzero if ABFD is MIPS R6.  */
 #define MIPSR6_P(abfd) \
   ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R6 \
@@ -1191,6 +1219,69 @@ static const bfd_vma mips_vxworks_shared_plt_entry[] =
   0x10000000,	/* b .PLT_resolver	*/
   0x24180000	/* li t8, <pltindex>	*/
 };
+
+/* The format of MIPS16 o32 IPLT entries.  We use v0 ($2)
+   and v1 ($3) as temporaries because t8 ($24) and t9 ($25) are not
+   directly addressable.  */
+static const bfd_vma mips16_exec_iplt_entry[] =
+{
+  0xb202,		/* lw 	 $2, 8($pc)       	*/
+  0x9a60,		/* lw 	 $3, 0($2)		*/
+  0xeb00,		/* jr 	 $3			*/
+  0x653b,		/* move  $25, $3		*/
+  0x0000, 0x0000	/* .word (.igot address)	*/
+};
+
+/* The format of 32 bit IPLT entries.  */
+static const bfd_vma mips32_exec_iplt_entry[] =
+{
+  0x3c0f0000,   /* lui $15, %hi(.igot address)		*/
+  0x8df90000,   /* lw  $25, %lo(.igot address)($15)	*/
+  0x03200008,   /* jr  $25				*/
+  0x00000000    /* nop					*/
+};
+
+/* Format of 32 bit IPLT entries for R6. JR encoding differs.  */
+static const bfd_vma mips32r6_exec_iplt_entry[] =
+{
+  0x3c0f0000,   /* lui $15, %hi(.igot address)		*/
+  0x8df90000,   /* lw  $25, %lo(.igot address)($15)	*/
+  0x03200009,   /* jr  $25				*/
+  0x00000000    /* nop					*/
+};
+
+/* The format of 32-bit micromips IPLT entries.  */
+static const bfd_vma micromips32_exec_iplt_entry[] =
+{
+  0x41a30000, 	/* lui $2, %hi(.igot address)		*/
+  0xff230000,	/* ld  $2, %lo(.igot address)($2) 	*/
+  0x4599,	/* jr  $25				*/
+  0x0c00,	/* nop					*/
+};
+
+/* The format of 64-bit IPLT entries.  */
+static const bfd_vma mips64_exec_iplt_entry[] =
+{
+  0x3c0f0000,	/* lui $15, %highest(.got.iplt entry)        */
+  0x3c0e0000,	/* lui $14, %hi(.got.iplt entry)             */
+  0x25ef0000,	/* addiu $15, $15, %higher(.got.iplt entry)  */
+  0x000f783c,	/* dsll32 $15, $15, 0x0                      */
+  0x01ee782d,	/* daddu $15, $15, $14                       */
+  0xddf90000,	/* ld $25, %lo(.got.iplt entry)($15)         */
+  0x03200008,	/* jr $25                                    */
+  0x00000000,	/* nop                                       */
+};
+
+static const bfd_vma mips64_48b_exec_iplt_entry[] =
+{
+  0x3c0f0000,	/* lui $15, %higher(.got.iplt entry)         */
+  0x25ef0000,	/* addiu $15, $15, %high(.got.iplt entry)    */
+  0x000f7c38,	/* dsll $15, $15, 16  			     */
+  0xddf90000,	/* ld $25, %lo(.got.iplt entry)($15)         */
+  0x03200008,	/* jr $25                                    */
+  0x00000000,	/* nop                                       */
+};
+
 ?
 /* microMIPS 32-bit opcode helper installer.  */
 
@@ -1289,6 +1380,10 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->has_nonpic_branches = FALSE;
       ret->needs_lazy_stub = FALSE;
       ret->use_plt_entry = FALSE;
+      ret->needs_iplt = FALSE;
+      ret->needs_ireloc = FALSE;
+      ret->iplt_offset = -1;
+      ret->igot_offset = 0;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -1588,8 +1683,9 @@ mips_elf_create_stub_symbol (struct bfd_link_info *info,
   struct elf_link_hash_entry *elfh;
   const char *name;
 
-  if (ELF_ST_IS_MICROMIPS (h->root.other))
-    value |= 1;
+  if (ELF_ST_IS_MICROMIPS (h->root.other)
+      || (ELF_ST_IS_MIPS16 (h->root.other) && h->root.type == STT_GNU_IFUNC))
+      value |= 1;
 
   /* Create a new symbol.  */
   name = ACONCAT ((prefix, h->root.root.root.string, NULL));
@@ -1604,6 +1700,10 @@ mips_elf_create_stub_symbol (struct bfd_link_info *info,
   elfh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
   elfh->size = size;
   elfh->forced_local = 1;
+
+  if (ELF_ST_IS_MIPS16 (h->root.other) && h->needs_iplt)
+    elfh->other = STO_MIPS16;
+
   return TRUE;
 }
 
@@ -1966,6 +2066,137 @@ mips_elf_add_la25_stub (struct bfd_link_info *info,
 	  : mips_elf_add_la25_intro (stub, info));
 }
 
+/* Return the dynamic relocation section.  If it doesn't exist, try to
+   create a new it if CREATE_P, otherwise return NULL.  Also return NULL
+   if creation fails.  */
+
+static asection *
+mips_elf_rel_dyn_section (struct bfd_link_info *info, bfd_boolean create_p)
+{
+  const char *dname;
+  asection *sreloc;
+  bfd *dynobj;
+
+  dname = MIPS_ELF_REL_DYN_NAME (info);
+  dynobj = elf_hash_table (info)->dynobj;
+  sreloc = bfd_get_linker_section (dynobj, dname);
+  if (sreloc == NULL && create_p)
+    {
+      sreloc = bfd_make_section_anyway_with_flags (dynobj, dname,
+						   (SEC_ALLOC
+						    | SEC_LOAD
+						    | SEC_HAS_CONTENTS
+						    | SEC_IN_MEMORY
+						    | SEC_LINKER_CREATED
+						    | SEC_READONLY));
+      if (sreloc == NULL
+	  || !bfd_set_section_alignment (dynobj, sreloc,
+					 MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
+	return NULL;
+    }
+  return sreloc;
+}
+
+/* Return section for R_MIPS_IRELATIVE relocations.  If the link is
+   dynamic, the relocations should go in .dynrel, otherwise they should
+   go in the special .rel.iplt section.  */
+
+static asection *
+mips_get_irel_section (struct bfd_link_info *info,
+		       struct mips_elf_link_hash_table *htab)
+{
+  asection *srel = (elf_hash_table (info)->dynamic_sections_created)
+		    ? mips_elf_rel_dyn_section (info, FALSE)
+		    : htab->root.irelplt;
+  BFD_ASSERT (srel != NULL);
+  return srel;
+}
+
+/* Reserve space in the rel.iplt section for IRELATIVE relocation.  */
+
+static bfd_boolean
+mips_elf_allocate_ireloc (struct bfd_link_info *info,
+			  struct mips_elf_link_hash_table *mhtab,
+			  struct mips_elf_link_hash_entry *mh)
+{
+  asection *srel;
+  bfd *dynobj;
+
+  if (mh->needs_iplt || mh->global_got_area == GGA_NORMAL
+      || mh->root.forced_local)
+    {
+      srel = mips_get_irel_section (info, mhtab);
+      dynobj = elf_hash_table (info)->dynobj;
+      if (srel != mhtab->root.irelplt && srel->size == 0)
+	{
+	  /* Make room for a null element.  */
+	  srel->size += MIPS_ELF_REL_SIZE (dynobj);
+	  ++srel->reloc_count;
+	}
+      srel->size += MIPS_ELF_REL_SIZE (dynobj);
+      mh->needs_ireloc = TRUE;
+    }
+
+  return TRUE;
+}
+
+/* Reserve space in the iplt and igot tables for an ifunc entry
+and allocate space for an IRELATIVE relocation.  */
+
+static bfd_boolean
+mips_elf_allocate_iplt (struct bfd_link_info *info,
+			struct mips_elf_link_hash_table *mhtab,
+			struct mips_elf_link_hash_entry *mh)
+{
+  asection *s;
+  bfd *abfd = info->output_bfd;
+
+  BFD_ASSERT (!mh->needs_iplt);
+  BFD_ASSERT (mhtab->root.iplt != NULL);
+
+  s = mhtab->root.iplt;
+
+  mh->iplt_offset = s->size;
+  mips_elf_create_stub_symbol (info, mh, ".iplt.", mhtab->root.iplt,
+			       s->size, mhtab->iplt_entry_size);
+  s->size += mhtab->iplt_entry_size;
+
+  BFD_ASSERT (mhtab->root.igotplt != NULL);
+  mh->igot_offset = mhtab->root.igotplt->size;
+  mhtab->root.igotplt->size += MIPS_ELF_GOT_SIZE (abfd);
+  mh->needs_iplt = TRUE;
+
+  return TRUE;
+}
+
+/* hash_traverse callback that is called before sizing sections.
+   DATA points to a mips_htab_traverse_info structure.  */
+
+static bfd_boolean
+mips_elf_check_local_symbols (void **slot, void *data)
+{
+  struct mips_htab_traverse_info *hti =
+    (struct mips_htab_traverse_info *) data;
+  struct mips_elf_link_hash_entry *h =
+    (struct mips_elf_link_hash_entry *) *slot;
+
+  /* If the referenced symbol is ifunc, allocate an iplt for it.  */
+  if (h && !h->needs_iplt && h->root.type == STT_GNU_IFUNC)
+    {
+      struct bfd_link_info *info = hti->info;
+      /* .iplt entry is needed only for executable objects.  */
+      if (!info->shared &&
+	  !mips_elf_allocate_iplt (info, mips_elf_hash_table (info), h))
+	return FALSE;
+
+      /* IRELATIVE fixup will be needed for each local IFUNC.  */
+      if (!mips_elf_allocate_ireloc (info, mips_elf_hash_table (info), h))
+	return FALSE;
+    }
+
+  return TRUE;
+}
+
 /* A mips_elf_link_hash_traverse callback that is called before sizing
    sections.  DATA points to a mips_htab_traverse_info structure.  */
 
@@ -1978,6 +2209,19 @@ mips_elf_check_symbols (struct mips_elf_link_hash_entry *h, void *data)
   if (!hti->info->relocatable)
     mips_elf_check_mips16_stubs (hti->info, h);
 
+  if (h && !h->needs_iplt &&
+      h->root.type == STT_GNU_IFUNC)
+    {
+      struct bfd_link_info *info = hti->info;
+      /* .iplt entry is needed only for executable objects.  */
+      if (!info->shared
+	  && !mips_elf_allocate_iplt (info, mips_elf_hash_table (info), h))
+	return FALSE;
+      /* Allocate an extra relocation for this IFUNC, if needed.  */
+      if (!mips_elf_allocate_ireloc (info, mips_elf_hash_table (info), h))
+	return FALSE;
+    }
+
   if (mips_elf_local_pic_function_p (h))
     {
       /* PR 12845: If H is in a section that has been garbage
@@ -3145,37 +3389,6 @@ mips_elf_replace_bfd_got (bfd *abfd, struct mips_got_info *g)
   tdata->got = g;
 }
 
-/* Return the dynamic relocation section.  If it doesn't exist, try to
-   create a new it if CREATE_P, otherwise return NULL.  Also return NULL
-   if creation fails.  */
-
-static asection *
-mips_elf_rel_dyn_section (struct bfd_link_info *info, bfd_boolean create_p)
-{
-  const char *dname;
-  asection *sreloc;
-  bfd *dynobj;
-
-  dname = MIPS_ELF_REL_DYN_NAME (info);
-  dynobj = elf_hash_table (info)->dynobj;
-  sreloc = bfd_get_linker_section (dynobj, dname);
-  if (sreloc == NULL && create_p)
-    {
-      sreloc = bfd_make_section_anyway_with_flags (dynobj, dname,
-						   (SEC_ALLOC
-						    | SEC_LOAD
-						    | SEC_HAS_CONTENTS
-						    | SEC_IN_MEMORY
-						    | SEC_LINKER_CREATED
-						    | SEC_READONLY));
-      if (sreloc == NULL
-	  || ! bfd_set_section_alignment (dynobj, sreloc,
-					  MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
-	return NULL;
-    }
-  return sreloc;
-}
-
 /* Return the GOT_TLS_* type required by relocation type R_TYPE.  */
 
 static int
@@ -3270,7 +3483,14 @@ mips_elf_count_got_entry (struct bfd_link_info *info,
 					? &entry->d.h->root : NULL);
     }
   else if (entry->symndx >= 0 || entry->d.h->global_got_area == GGA_NONE)
-    g->local_gotno += 1;
+    {
+      /* Count IFUNCs as general GOT entries since they will need
+	 explicit IRELATIVE relocations.  */
+      if (entry->symndx < 0 && entry->d.h->root.type == STT_GNU_IFUNC)
+	g->general_gotno += 1;
+      else
+	g->local_gotno += 1;
+    }
   else
     g->global_gotno += 1;
 }
@@ -3600,7 +3820,8 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
 
 static bfd_vma
 mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
-		      bfd_vma value, bfd_boolean external)
+		      bfd_vma value, bfd_boolean external,
+		      struct mips_elf_link_hash_entry *h)
 {
   struct mips_got_entry *entry;
 
@@ -3615,7 +3836,7 @@ mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
      R_MIPS16_GOT16, R_MIPS_CALL16, etc.  The format of the entry is the
      same in all cases.  */
   entry = mips_elf_create_local_got_entry (abfd, info, ibfd, value, 0,
-					   NULL, R_MIPS_GOT16);
+					   h, R_MIPS_GOT16);
   if (entry)
     return entry->gotidx;
   else
@@ -3715,7 +3936,8 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
   if (entry)
     return entry;
 
-  if (g->assigned_low_gotno > g->assigned_high_gotno)
+  if (g->assigned_low_gotno > g->assigned_high_gotno ||
+      g->assigned_general_gotno > g->assigned_low_gotno)
     {
       /* We didn't allocate enough space in the GOT.  */
       (*_bfd_error_handler)
@@ -3728,7 +3950,11 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
   if (!entry)
     return NULL;
 
-  if (got16_reloc_p (r_type)
+  if (h && h->root.type == STT_GNU_IFUNC)
+    /* Allocate IFUNC slots in the general GOT region since they
+       will need explicit IRELATIVE relocations.  */
+    lookup.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_general_gotno++;
+  else if (got16_reloc_p (r_type)
       || call16_reloc_p (r_type)
       || got_page_reloc_p (r_type)
       || got_disp_reloc_p (r_type))
@@ -4460,6 +4686,29 @@ mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
     }
   return 1;
 }
+
+/* A elf_link_hash_traverse callback for which INF points to the
+   link_info structure. Count the number of GOT entries that need
+   explicit relocations by iterating over the local hash table.  */
+
+static int
+mips_elf_count_general_got_symbols (void **slot, void *inf)
+{
+  struct mips_elf_link_hash_entry *h =
+    (struct mips_elf_link_hash_entry *) *slot;
+  struct bfd_link_info *info;
+  struct mips_elf_link_hash_table *htab;
+  struct mips_got_info *g;
+
+  info = (struct bfd_link_info *) inf;
+  htab = mips_elf_hash_table (info);
+
+  g = htab->got_info;
+  if (h != NULL && h->root.type == STT_GNU_IFUNC &&
+      (h->root.forced_local || h->global_got_area == GGA_NONE))
+    g->general_gotno++;
+  return 1;
+}
 ?
 /* A htab_traverse callback for GOT entries.  Add each one to the GOT
    given in mips_elf_traverse_got_arg DATA.  Clear DATA->G on error.  */
@@ -4698,6 +4947,31 @@ mips_elf_set_global_got_area (void **entryp, void *data)
   return 1;
 }
 
+/* A htab_traverse callback for GOT entries, where DATA points to a
+   mips_elf_traverse_got_arg. Deallocate dedicated REL32 relocations
+   for global IFUNCs that are marked RELOC_ONLY.  */
+
+static int
+mips_elf_deallocate_irelocs (void **entryp, void *data)
+{
+  struct mips_got_entry *entry;
+
+  entry = (struct mips_got_entry *) *entryp;
+  if (entry->abfd != NULL
+      && entry->symndx == -1
+      && entry->d.h->global_got_area == GGA_RELOC_ONLY
+      && entry->d.h->root.type == STT_GNU_IFUNC
+      && entry->d.h->needs_ireloc)
+    {
+      struct mips_elf_traverse_got_arg *arg
+	= (struct mips_elf_traverse_got_arg *) data;
+      asection *srel = mips_elf_rel_dyn_section (arg->info, FALSE);
+      srel->size -= MIPS_ELF_REL_SIZE (elf_hash_table (arg->info)->dynobj);
+      entry->d.h->needs_ireloc = FALSE;
+    }
+  return 1;
+}
+
 /* A htab_traverse callback for secondary GOT entries, where DATA points
    to a mips_elf_traverse_got_arg.  Assign GOT indices to global entries
    and record the number of relocations they require.  DATA->value is
@@ -4852,6 +5126,11 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
   tga.value = GGA_NORMAL;
   htab_traverse (g->got_entries, mips_elf_set_global_got_area, &tga);
 
+  /* Deallocate extra relocation intended for IFUNCs that have been marked
+     as RELOC_ONLY. These entries will get explicit relocs due to multi-got
+     which will work just as well for IFUNC resolution.  */
+  htab_traverse (gg->got_entries, mips_elf_deallocate_irelocs, &tga);
+
   /* Now go through the GOTs assigning them offset ranges.
      [assigned_low_gotno, local_gotno[ will be set to the range of local
      entries in each GOT.  We can then compute the end of a GOT by
@@ -5087,6 +5366,64 @@ mips_elf_create_compact_rel_section
   return TRUE;
 }
 
+/* Create the .iplt, .rel(a).iplt and .igot sections.  */
+
+static bfd_boolean
+mips_elf_create_ifunc_sections (struct bfd_link_info *info)
+{
+  struct mips_elf_link_hash_table * volatile htab;
+  const struct elf_backend_data *bed;
+  bfd *dynobj;
+  asection *s;
+  flagword flags;
+
+  htab = mips_elf_hash_table (info);
+  dynobj = htab->root.dynobj;
+  bed = get_elf_backend_data (dynobj);
+  flags = bed->dynamic_sec_flags;
+
+  s = bfd_make_section_anyway_with_flags (dynobj, ".iplt",
+					  flags | SEC_READONLY | SEC_CODE);
+  if (s == NULL || !bfd_set_section_alignment (dynobj, s, bed->plt_alignment))
+    return FALSE;
+
+  htab->root.iplt = s;
+
+  if (!info->shared)
+    {
+      if (ABI_64_P (dynobj))
+	htab->iplt_entry_size = 4 * ARRAY_SIZE (mips64_exec_iplt_entry);
+      else if (MIPS16_P (dynobj))
+	    htab->iplt_entry_size = 2 * ARRAY_SIZE (mips16_exec_iplt_entry);
+      else if (MICROMIPS_P (dynobj))
+	  /* Multiply by compression ratio for micromips.  */
+	    htab->iplt_entry_size = 4
+	      * (ARRAY_SIZE (micromips32_exec_iplt_entry) * 3 / 4);
+      else
+	htab->iplt_entry_size = 4 * (ARRAY_SIZE (mips32_exec_iplt_entry)
+				     + (LOAD_INTERLOCKS_P (dynobj)? 0 : 1));
+    }
+
+  BFD_ASSERT (htab->root.igotplt == NULL);
+
+  s = bfd_make_section_anyway_with_flags (dynobj, ".igot", flags);
+  if (s == NULL
+      || !bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
+    return FALSE;
+  htab->root.igotplt = s;
+  mips_elf_section_data (s)->elf.this_hdr.sh_flags |= SHF_ALLOC | SHF_WRITE;
+
+  BFD_ASSERT (htab->root.irelplt == NULL);
+
+  s = bfd_make_section_with_flags (dynobj, ".rel.iplt", flags | SEC_READONLY);
+  if (s == NULL
+      || !bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
+    return FALSE;
+
+  htab->root.irelplt = s;
+  return TRUE;
+}
+
 /* Create the .got section to hold the global offset table.  */
 
 static bfd_boolean
@@ -5203,6 +5540,74 @@ mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type,
       return FALSE;
     }
 }
+
+/* Find and/or create a hash entry for local symbol.  */
+
+static struct mips_elf_link_hash_entry *
+get_local_sym_hash (struct mips_elf_link_hash_table *htab,
+		    bfd *abfd, const Elf_Internal_Rela *rel)
+{
+  struct mips_elf_link_hash_entry e, *ret;
+  asection *sec;
+  hashval_t h;
+  void **slot;
+  Elf_Internal_Sym *isym;
+  Elf_Internal_Shdr *symtab_hdr;
+  char *namep;
+
+  sec = bfd_get_section_by_name (abfd, ".text");
+  h = ELF_LOCAL_SYMBOL_HASH (sec->id, ELF_R_SYM (abfd, rel->r_info));
+  isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd,
+				ELF_R_SYM (abfd, rel->r_info));
+  symtab_hdr =  &elf_tdata (abfd)->symtab_hdr;
+  namep = bfd_elf_string_from_elf_section (abfd, symtab_hdr->sh_link,
+					   isym->st_name);
+
+  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+    elf_tdata (abfd)->has_gnu_symbols = TRUE;
+
+  e.root.indx = sec->id;
+  e.root.dynstr_index = ELF_R_SYM (abfd, rel->r_info);
+  e.root.root.root.string = namep;
+
+  slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h, INSERT);
+  if (!slot)
+    return NULL;
+
+  /* Found match */
+  if (*slot)
+    {
+      ret = (struct mips_elf_link_hash_entry *) *slot;
+      return ret;
+    }
+
+  /* Allocate new slot */
+  ret = (struct mips_elf_link_hash_entry *)
+    objalloc_alloc ((struct objalloc *) htab->loc_hash_memory,
+		    sizeof (struct mips_elf_link_hash_entry));
+
+  if (ret)
+    {
+      memset (ret, 0, sizeof (*ret));
+      ret->root.indx = sec->id;
+      ret->root.dynstr_index = ELF_R_SYM (abfd, rel->r_info);
+      ret->root.dynindx = -1;
+      ret->root.root.root.string = namep;
+      ret->root.root.u.def.section = sec;
+      ret->root.root.u.def.value = isym->st_value;
+      ret->root.got.offset = (bfd_vma) -1;
+      ret->global_got_area = GGA_NONE;
+      ret->root.type = STT_GNU_IFUNC;
+      ret->root.def_regular = 1;
+      ret->root.ref_regular = 1;
+      ret->root.forced_local = 1;
+      ret->root.root.type = bfd_link_hash_defined;
+
+      *slot = ret;
+    }
+
+  return ret;
+}
 ?
 /* Calculate the value produced by the RELOCATION (which comes from
    the INPUT_BFD).  The ADDEND is the addend to use for this
@@ -5253,6 +5658,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
   /* TRUE if the symbol referred to by this relocation is a local
      symbol.  */
   bfd_boolean local_p, was_local_p;
+  /* TRUE if the symbol referred to by this relocation is a local IFUNC */
+  bfd_boolean local_gnu_ifunc_p = FALSE;
   /* TRUE if the symbol referred to by this relocation is "_gp_disp".  */
   bfd_boolean gp_disp_p = FALSE;
   /* TRUE if the symbol referred to by this relocation is
@@ -5333,6 +5740,13 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
 
       target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (sym->st_other);
       target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (sym->st_other);
+
+      if (sym->st_info == STT_GNU_IFUNC)
+	{
+	  h = get_local_sym_hash (mips_elf_hash_table (info), input_bfd,
+				  relocation);
+	  local_gnu_ifunc_p = TRUE;
+	}
     }
   else
     {
@@ -5557,6 +5971,17 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       target_is_16_bit_code_p = !micromips_p;
       target_is_micromips_code_p = micromips_p;
     }
+  /* If this symbol is an ifunc, point to the iplt stub for it.  */
+  else if (h && h->needs_iplt)
+    {
+      BFD_ASSERT (htab->root.iplt != NULL);
+      symbol = (htab->root.iplt->output_section->vma
+		+ htab->root.iplt->output_offset
+		+ h->iplt_offset);
+      /* Set ISA bit in address for compressed code.  */
+      if (ELF_ST_IS_COMPRESSED (h->root.other))
+	symbol |= 1;
+    }
 
   /* Make sure MIPS16 and microMIPS are not used together.  */
   if ((r_type == R_MIPS16_26 && target_is_micromips_code_p)
@@ -5941,10 +6366,13 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MICROMIPS_CALL16:
       /* VxWorks does not have separate local and global semantics for
 	 R_MIPS*_GOT16; every relocation evaluates to "G".  */
+      /* Local IFUNC symbols must be accessed through GOT, similar to global
+	 symbols, to allow for indirection */
       if (!htab->is_vxworks && local_p)
 	{
 	  value = mips_elf_got16_entry (abfd, input_bfd, info,
-					symbol + addend, !was_local_p);
+					symbol + addend,
+					!was_local_p || local_gnu_ifunc_p, h);
 	  if (value == MINUS_ONE)
 	    return bfd_reloc_outofrange;
 	  value
@@ -6448,31 +6876,41 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
   if (defined_p && r_type != R_MIPS_REL32)
     *addendp += symbol;
 
-  if (htab->is_vxworks)
-    /* VxWorks uses non-relative relocations for this.  */
-    outrel[0].r_info = ELF32_R_INFO (indx, R_MIPS_32);
+  if (h && !indx && h->root.type == STT_GNU_IFUNC)
+    {
+      outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
+				     R_MIPS_IRELATIVE);
+      outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
+				     R_MIPS_NONE);
+    }
   else
-    /* The relocation is always an REL32 relocation because we don't
-       know where the shared library will wind up at load-time.  */
-    outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
-				   R_MIPS_REL32);
-
-  /* For strict adherence to the ABI specification, we should
-     generate a R_MIPS_64 relocation record by itself before the
-     _REL32/_64 record as well, such that the addend is read in as
-     a 64-bit value (REL32 is a 32-bit relocation, after all).
-     However, since none of the existing ELF64 MIPS dynamic
-     loaders seems to care, we don't waste space with these
-     artificial relocations.  If this turns out to not be true,
-     mips_elf_allocate_dynamic_relocation() should be tweaked so
-     as to make room for a pair of dynamic relocations per
-     invocation if ABI_64_P, and here we should generate an
-     additional relocation record with R_MIPS_64 by itself for a
-     NULL symbol before this relocation record.  */
-  outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
-				 ABI_64_P (output_bfd)
-				 ? R_MIPS_64
-				 : R_MIPS_NONE);
+    {
+      if (htab->is_vxworks)
+	/* VxWorks uses non-relative relocations for this.  */
+	outrel[0].r_info = ELF32_R_INFO (indx, R_MIPS_32);
+      else
+	/* The relocation is always an REL32 relocation because we don't
+	   know where the shared library will wind up at load-time.  */
+	outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
+				       R_MIPS_REL32);
+
+      /* For strict adherence to the ABI specification, we should
+	 generate a R_MIPS_64 relocation record by itself before the
+	 _REL32/_64 record as well, such that the addend is read in as
+	 a 64-bit value (REL32 is a 32-bit relocation, after all).
+	 However, since none of the existing ELF64 MIPS dynamic
+	 loaders seems to care, we don't waste space with these
+	 artificial relocations.  If this turns out to not be true,
+	 mips_elf_allocate_dynamic_relocation() should be tweaked so
+	 as to make room for a pair of dynamic relocations per
+	 invocation if ABI_64_P, and here we should generate an
+	 additional relocation record with R_MIPS_64 by itself for a
+	 NULL symbol before this relocation record.  */
+      outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
+				     ABI_64_P (output_bfd)
+				     ? R_MIPS_64
+				     : R_MIPS_NONE);
+    }
   outrel[2].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_NONE);
 
   /* Adjust the output offset of the relocation to reference the
@@ -7019,6 +7457,8 @@ _bfd_mips_elf_section_processing (bfd *abfd, Elf_Internal_Shdr *hdr)
 		hdr->sh_size += hdr->sh_addralign - adjust;
 	    }
 	}
+      else if (strcmp (name, ".igot") == 0)
+	hdr->sh_entsize =  MIPS_ELF_GOT_SIZE (abfd);
     }
 
   return TRUE;
@@ -7381,6 +7821,9 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
 			       flagword *flagsp ATTRIBUTE_UNUSED,
 			       asection **secp, bfd_vma *valp)
 {
+  if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+    elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+
   if (SGI_COMPAT (abfd)
       && (abfd->flags & DYNAMIC) != 0
       && strcmp (*namep, "_rld_new_interface") == 0)
@@ -7932,6 +8375,13 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   bed = get_elf_backend_data (abfd);
   rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
 
+  /* This needs to happen early. If the sections aren't needed
+     they will not get generated.  */
+  if (htab->root.dynobj == NULL)
+    htab->root.dynobj = abfd;
+  if (!htab->root.iplt && !mips_elf_create_ifunc_sections (info))
+    return FALSE;
+
   /* Check for the mips16 stub sections.  */
 
   name = bfd_get_section_name (abfd, sec);
@@ -8193,12 +8643,30 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       bfd_boolean can_make_dynamic_p;
       bfd_boolean call_reloc_p;
       bfd_boolean constrain_symbol_p;
+      bfd_boolean local_gnu_ifunc_p = FALSE;
 
       r_symndx = ELF_R_SYM (abfd, rel->r_info);
       r_type = ELF_R_TYPE (abfd, rel->r_info);
 
       if (r_symndx < extsymoff)
-	h = NULL;
+	{
+	  Elf_Internal_Sym *isym;
+	  isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx);
+
+	  if (isym == NULL)
+	    return FALSE;
+
+	  /* Relocation is for local STT_GNU_IFUNC symbol.  */
+	  if (isym->st_info == STT_GNU_IFUNC)
+	    {
+	      /* Ensure that we have a hash entry for this symbol.  */
+	      if (get_local_sym_hash (htab, abfd, rel) == NULL)
+		return FALSE;
+	      local_gnu_ifunc_p = TRUE;
+	    }
+
+	  h = NULL;
+       }
       else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
 	{
 	  (*_bfd_error_handler)
@@ -8410,7 +8878,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_MIPS_CALL16:
 	case R_MIPS16_CALL16:
 	case R_MICROMIPS_CALL16:
-	  if (h == NULL)
+	  /* Exclude local IFUNCs from check */
+	  if (h == NULL && !local_gnu_ifunc_p)
 	    {
 	      (*_bfd_error_handler)
 		(_("%B: CALL16 reloc at 0x%lx not against global symbol"),
@@ -8435,9 +8904,11 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	      /* We need a stub, not a plt entry for the undefined
 		 function.  But we record it as if it needs plt.  See
-		 _bfd_elf_adjust_dynamic_symbol.  */
+		 _bfd_elf_adjust_dynamic_symbol.  If it is an ifunc
+		 symbol it will go into an iplt section and not plt.  */
 	      h->needs_plt = 1;
-	      h->type = STT_FUNC;
+	      if (h->type != STT_GNU_IFUNC)
+		h->type = STT_FUNC;
 	    }
 	  break;
 
@@ -8976,6 +9447,7 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* Make sure we know what is going on here.  */
   BFD_ASSERT (dynobj != NULL
 	      && (h->needs_plt
+		  || h->type == STT_GNU_IFUNC
 		  || h->u.weakdef != NULL
 		  || (h->def_dynamic
 		      && h->ref_regular
@@ -9254,6 +9726,11 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
   hti.error = FALSE;
   mips_elf_link_hash_traverse (mips_elf_hash_table (info),
 			       mips_elf_check_symbols, &hti);
+
+  /* Allocate relocs for local IFUNC symbols.  */
+  htab_traverse (htab->loc_hash_table,
+		 mips_elf_check_local_symbols, &hti);
+
   if (hti.error)
     return FALSE;
 
@@ -9298,9 +9775,17 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
      count the number of reloc-only GOT symbols.  */
   mips_elf_link_hash_traverse (htab, mips_elf_count_got_symbols, info);
 
+  /* Count local IFUNC symbols. These need general GOT entries that
+     are fixed-up by explicit IRELATIVE relocations.  */
+  htab_traverse (htab->loc_hash_table, mips_elf_count_general_got_symbols, info);
+
   if (!mips_elf_resolve_final_got_entries (info, g))
     return FALSE;
 
+  g->assigned_general_gotno = htab->reserved_gotno;
+  g->local_gotno += g->general_gotno;
+  g->assigned_low_gotno += g->general_gotno;
+
   /* Calculate the total loadable size of the output.  That
      will give us the maximum number of GOT_PAGE entries
      required.  */
@@ -9742,6 +10227,8 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
       else if (! CONST_STRNEQ (name, ".init")
 	       && s != htab->sgot
 	       && s != htab->sgotplt
+	       && s != htab->root.iplt
+	       && s != htab->root.igotplt
 	       && s != htab->sstubs
 	       && s != htab->sdynbss)
 	{
@@ -9887,6 +10374,13 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
 	  if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_PLTGOT, 0))
 	    return FALSE;
 	}
+
+      if (htab->got_info->general_gotno > 0)
+	{
+	  if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GENERAL_GOTNO, 0))
+	    return FALSE;
+	}
+
       if (htab->is_vxworks
 	  && !elf_vxworks_add_dynamic_entries (output_bfd, info))
 	return FALSE;
@@ -10018,6 +10512,7 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       Elf_Internal_Shdr *symtab_hdr;
       struct elf_link_hash_entry *h;
       bfd_boolean rel_reloc;
+      bfd_boolean local_gnu_ifunc_p = FALSE;
 
       rel_reloc = (NEWABI_P (input_bfd)
 		   && mips_elf_rel_relocation_p (input_bfd, input_section,
@@ -10031,6 +10526,23 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	{
 	  sec = local_sections[r_symndx];
 	  h = NULL;
+
+	  Elf_Internal_Sym *isym;
+	  struct mips_elf_link_hash_table *htab;
+	  htab = mips_elf_hash_table (info);
+	  isym = bfd_sym_from_r_symndx (&htab->sym_cache, input_bfd, r_symndx);
+
+	  if (isym == NULL)
+	    return FALSE;
+
+	  /* Relocation is for local STT_GNU_IFUNC symbol.  */
+	  if (isym->st_info == STT_GNU_IFUNC)
+	    {
+	      /* Ensure that we have a hash entry for this symbol.  */
+	      if (get_local_sym_hash (htab, input_bfd, rel) == NULL)
+		return FALSE;
+	      local_gnu_ifunc_p = TRUE;
+	    }
 	}
       else
 	{
@@ -10091,8 +10603,9 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 		      && mips_elf_local_relocation_p (input_bfd, rel,
 						      local_sections)))
 		{
-		  if (!mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
-						     contents, &addend))
+		  if (!local_gnu_ifunc_p
+		      && !mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
+							contents, &addend))
 		    {
 		      if (h)
 			name = h->root.root.string;
@@ -10479,6 +10992,237 @@ mips_elf_irix6_finish_dynamic_symbol (bfd *abfd ATTRIBUTE_UNUSED,
 	}
 }
 
+/* Create the contents of the iplt entry for an IFUNC symbol.  */
+
+static bfd_boolean
+mips_elf_create_iplt (bfd *output_bfd,
+		      struct mips_elf_link_hash_table *htab,
+		      struct mips_elf_link_hash_entry *hmips,
+		      bfd_vma igotplt_address)
+{
+  bfd_byte *loc;
+  const bfd_vma *iplt_entry;
+  bfd_vma high = mips_elf_high (igotplt_address);
+  bfd_vma low = igotplt_address & 0xffff;
+
+  /* Find out where the .iplt entry should go.  */
+  if (!htab->root.iplt->contents)
+    {
+      htab->root.iplt->contents = bfd_zalloc (output_bfd,
+					      htab->root.iplt->size);
+      if (!htab->root.iplt->contents)
+	return FALSE;
+    }
+  loc = htab->root.iplt->contents + hmips->iplt_offset;
+
+  /* Fill in the IPLT entry itself.  */
+  if (ABI_64_P (output_bfd))
+    {
+      bfd_vma highest = mips_elf_highest (igotplt_address);
+      bfd_vma higher = mips_elf_higher (igotplt_address);
+      iplt_entry = mips64_exec_iplt_entry;
+
+      if (highest)
+	{
+	  /* Full 64-bit address space */
+	  bfd_put_32 (output_bfd, iplt_entry[0] | highest, loc);
+	  bfd_put_32 (output_bfd, iplt_entry[1] | high, loc + 4);
+	  bfd_put_32 (output_bfd, iplt_entry[2] | higher, loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[3], loc + 12);
+	  bfd_put_32 (output_bfd, iplt_entry[4], loc + 16);
+	  bfd_put_32 (output_bfd, iplt_entry[5] | low, loc + 20);
+	  bfd_put_32 (output_bfd, iplt_entry[6], loc + 24);
+	  bfd_put_32 (output_bfd, iplt_entry[7], loc + 28);
+	}
+      else if (higher)
+	{
+	  /* 48-bit address space */
+	  iplt_entry = mips64_48b_exec_iplt_entry;
+	  bfd_put_32 (output_bfd, iplt_entry[0] | higher, loc);
+	  bfd_put_32 (output_bfd, iplt_entry[1] | high, loc + 4);
+	  bfd_put_32 (output_bfd, iplt_entry[2], loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[3] | low, loc + 12);
+	  bfd_put_32 (output_bfd, iplt_entry[4], loc + 16);
+	  bfd_put_32 (output_bfd, iplt_entry[5], loc + 20);
+	}
+      else
+	{
+	  /* 32-bit address space */
+	  bfd_put_32 (output_bfd, iplt_entry[0] | high, loc);
+	  bfd_put_32 (output_bfd, iplt_entry[5] | low, loc + 4);
+	  bfd_put_32 (output_bfd, iplt_entry[6], loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[7], loc + 12);
+	}
+    }
+  else if (ELF_ST_IS_MIPS16 (hmips->root.other))
+    {
+      iplt_entry = mips16_exec_iplt_entry;
+      bfd_put_16 (output_bfd, iplt_entry[0], loc);
+      bfd_put_16 (output_bfd, iplt_entry[1], loc + 2);
+      bfd_put_16 (output_bfd, iplt_entry[2], loc + 4);
+      bfd_put_16 (output_bfd, iplt_entry[3], loc + 6);
+      bfd_put_32 (output_bfd, igotplt_address, loc + 8);
+    }
+  else if (ELF_ST_IS_MICROMIPS (hmips->root.other))
+    {
+      iplt_entry = micromips32_exec_iplt_entry;
+      bfd_put_micromips_32 (output_bfd, iplt_entry[0] | high, loc);
+      bfd_put_micromips_32 (output_bfd, iplt_entry[1] | low , loc + 4);
+
+      bfd_put_16 (output_bfd, iplt_entry[2], loc + 8);
+      bfd_put_16 (output_bfd, iplt_entry[3], loc + 10);
+    }
+  else
+    {
+      if (MIPSR6_P(output_bfd))
+	iplt_entry = mips32r6_exec_iplt_entry;
+      else
+	iplt_entry = mips32_exec_iplt_entry;
+      bfd_put_32 (output_bfd, iplt_entry[0] | high, loc);
+      bfd_put_32 (output_bfd, iplt_entry[1] | low, loc + 4);
+      if (LOAD_INTERLOCKS_P (output_bfd))
+	{
+	  bfd_put_32 (output_bfd, iplt_entry[2], loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[3], loc + 12);
+	}
+      else
+	{
+	  bfd_put_32 (output_bfd, iplt_entry[3], loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[2], loc + 12);
+	  bfd_put_32 (output_bfd, iplt_entry[3], loc + 16);
+	}
+    }
+
+  return TRUE;
+}
+
+/* Find local GOT index for VALUE. Return -1 if no GOT slot is found.  */
+
+static bfd_vma
+mips_elf_check_local_got_index (bfd *abfd, struct bfd_link_info *info,
+				 bfd_vma value)
+{
+  struct mips_got_entry lookup, *entry;
+  void **loc;
+  struct mips_got_info *g;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
+  g = mips_elf_bfd_got (abfd, FALSE);
+  BFD_ASSERT (g != NULL);
+
+  lookup.abfd = NULL;
+  lookup.symndx = -1;
+  lookup.d.address = value;
+  lookup.tls_type = GOT_TLS_NONE;
+  loc = htab_find_slot (g->got_entries, &lookup, NO_INSERT);
+  if (!loc)
+    return -1;
+
+  entry = (struct mips_got_entry *) *loc;
+  if (entry)
+    return entry->gotidx;
+  else
+    return -1;
+}
+
+/* Create the IRELATIVE relocation for an IFUNC symbol.  */
+
+static bfd_boolean
+mips_elf_create_ireloc (bfd *output_bfd,
+		      bfd *dynobj,
+		      struct mips_elf_link_hash_table *htab,
+		      struct mips_elf_link_hash_entry *hmips,
+		      Elf_Internal_Sym *sym,
+		      struct bfd_link_info *info)
+{
+  bfd_vma igotplt_address = 0;
+  int igot_offset = -1;
+  asection *gotsect, *relsect;
+
+  if (!hmips->needs_iplt)
+    {
+      gotsect = htab->sgot;
+      if (hmips->global_got_area == GGA_NORMAL)
+	  igot_offset = mips_elf_primary_global_got_index (output_bfd,
+							   info, &hmips->root);
+      else if (hmips->global_got_area == GGA_NONE)
+	  igot_offset = mips_elf_check_local_got_index (output_bfd, info,
+							sym->st_value);
+    }
+  else
+    {
+      bfd_byte *loc;
+      bfd_vma igot_index;
+      gotsect = htab->root.igotplt;
+      igot_offset = hmips->igot_offset;
+
+      /* Calculate the address of the IGOT entry.  */
+      igot_index = igot_offset / MIPS_ELF_GOT_SIZE (dynobj);
+
+      if (!gotsect->contents)
+	{
+	  gotsect->contents = bfd_zalloc (output_bfd, gotsect->size);
+	  if (!gotsect->contents)
+	    return FALSE;
+	}
+
+      /* Initially point the .igot entry at the IFUNC resolver routine.  */
+      loc = (bfd_byte *) gotsect->contents
+	+ igot_index * MIPS_ELF_GOT_SIZE (dynobj);
+
+      if (ABI_64_P (output_bfd))
+	bfd_put_64 (output_bfd, sym->st_value, loc);
+      else
+	bfd_put_32 (output_bfd, sym->st_value, loc);
+    }
+
+  igotplt_address = (gotsect->output_section->vma + gotsect->output_offset
+		     + igot_offset);
+
+  relsect = mips_get_irel_section (info, htab);
+
+  if (igot_offset >= 0)
+    {
+      if (hmips->needs_iplt && relsect->contents == NULL)
+	{
+	  /* Allocate memory for the relocation section contents.  */
+	  relsect->contents = bfd_zalloc (dynobj, relsect->size);
+	  if (relsect->contents == NULL)
+	    return FALSE;
+	}
+
+      if (hmips->needs_iplt || hmips->global_got_area == GGA_NONE)
+	/* Emit an R_MIPS_IRELATIVE relocation against the [I]GOT entry.  */
+	mips_elf_output_dynamic_relocation (output_bfd, relsect,
+					    relsect->reloc_count++, 0,
+					    R_MIPS_IRELATIVE, igotplt_address);
+      else if (hmips->global_got_area == GGA_NORMAL)
+	{
+	  /* Emit an R_MIPS_REL32 relocation against global GOT entry for
+	     a preemptible symbol.  */
+	  asection *sec = hmips->root.root.u.def.section;
+	  Elf_Internal_Rela rel[3];
+
+	  memset (rel, 0, sizeof (rel));
+	  rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_REL32);
+	  rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = igot_offset;
+
+	  mips_elf_create_dynamic_relocation (output_bfd, info, rel, hmips,
+					      sec, sym->st_value, NULL,
+					      gotsect);
+	}
+    }
+  /* If necessary, generate the corresponding .iplt entry.  */
+  if (hmips->needs_iplt
+      && !mips_elf_create_iplt (output_bfd, htab, hmips, igotplt_address))
+    return FALSE;
+
+  return TRUE;
+}
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -10807,6 +11551,19 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
       sym->st_other = other;
     }
 
+  if (hmips->root.type == STT_GNU_IFUNC)
+    {
+      if (hmips->needs_ireloc
+	  && !mips_elf_create_ireloc (output_bfd, dynobj, htab,
+				      hmips, sym, info))
+	return FALSE;
+
+      if (!elf_hash_table (info)->dynamic_sections_created)
+	return TRUE;
+      if (h->dynindx == -1  && !h->forced_local)
+	return TRUE;
+    }
+
   /* If we have a MIPS16 function with a stub, the dynamic symbol must
      refer to the stub, since only the stub uses the standard calling
      conventions.  */
@@ -10970,9 +11727,37 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
       sym->st_other -= STO_MICROMIPS;
     }
 
+  if (hmips->needs_iplt)
+    {
+      /* Point at the iplt stub for this ifunc symbol.  */
+      sym->st_value = htab->root.iplt->output_section->vma
+	+ htab->root.iplt->output_offset + hmips->iplt_offset;
+      sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_FUNC);
+      if (ELF_ST_IS_COMPRESSED (hmips->root.other))
+	sym->st_value |= 1;
+    }
+
   return TRUE;
 }
 
+/* Finish up local dynamic symbol handling.  We set the contents of
+   various dynamic sections here.  */
+
+static bfd_boolean
+_bfd_mips_elf_finish_local_dynamic_symbol (void **slot, void *inf)
+{
+  struct mips_elf_link_hash_entry *h = (struct mips_elf_link_hash_entry *) *slot;
+  struct bfd_link_info *info  = (struct bfd_link_info *)inf;
+  Elf_Internal_Sym isym;
+
+  isym.st_value = h->root.root.u.def.section->output_section->vma
+    + h->root.root.u.def.section->output_offset + h->root.root.u.def.value;
+  isym.st_other = h->root.other;
+
+  return _bfd_mips_elf_finish_dynamic_symbol (info->output_bfd, info,
+					      &h->root, &isym);
+}
+
 /* Likewise, for VxWorks.  */
 
 bfd_boolean
@@ -11358,6 +12143,13 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
   sgot = htab->sgot;
   gg = htab->got_info;
 
+  if (htab_elements (htab->loc_hash_table) > 0)
+  {
+    /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
+    htab_traverse (htab->loc_hash_table,
+		   _bfd_mips_elf_finish_local_dynamic_symbol, info);
+  }
+
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       bfd_byte *b;
@@ -11528,6 +12320,10 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
 	      dyn.d_un.d_ptr = s->vma;
 	      break;
 
+	    case DT_MIPS_GENERAL_GOTNO:
+	      dyn.d_un.d_val = htab->reserved_gotno + gg->general_gotno;
+	      break;
+
 	    case DT_RELASZ:
 	      BFD_ASSERT (htab->is_vxworks);
 	      /* The count does not include the JUMP_SLOT relocations.  */
@@ -13862,6 +14658,48 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
   return FALSE;
 }
 ?
+/* Compute a hash of a local hash entry.  We use elf_link_hash_entry
+  for local symbol so that we can handle local STT_GNU_IFUNC symbols
+  as global symbol.  We reuse indx and dynstr_index for local symbol
+  hash since they aren't used by global symbols in this backend.  */
+
+static hashval_t
+local_htab_hash (const void *ptr)
+{
+  struct  mips_elf_link_hash_entry *h =
+    (struct  mips_elf_link_hash_entry *) ptr;
+  return ELF_LOCAL_SYMBOL_HASH (h->root.indx, h->root.dynstr_index);
+}
+
+/* Compare local hash entries.  */
+
+static int
+local_htab_eq (const void *ptr1, const void *ptr2)
+{
+  struct mips_elf_link_hash_entry *h1 =
+    (struct mips_elf_link_hash_entry *) ptr1;
+  struct  mips_elf_link_hash_entry *h2 =
+    (struct  mips_elf_link_hash_entry *) ptr2;
+
+  return h1->root.indx == h2->root.indx &&
+    h1->root.dynstr_index == h2->root.dynstr_index;
+}
+
+/* Destroy a MIPS ELF linker hash table.  */
+
+static void
+_bfd_mips_elf_link_hash_table_free (bfd *obfd)
+{
+  struct mips_elf_link_hash_table *htab;
+
+  htab = (struct mips_elf_link_hash_table *) obfd->link.hash;
+  if (htab->loc_hash_table)
+    htab_delete (htab->loc_hash_table);
+  if (htab->loc_hash_memory)
+    objalloc_free ((struct objalloc *) htab->loc_hash_memory);
+  _bfd_elf_link_hash_table_free (obfd);
+}
+
 /* Create a MIPS ELF linker hash table.  */
 
 struct bfd_link_hash_table *
@@ -13885,6 +14723,17 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd)
   ret->root.init_plt_refcount.plist = NULL;
   ret->root.init_plt_offset.plist = NULL;
 
+  /* Create hash table for local IFUNC symbols.  */
+  ret->loc_hash_table = htab_try_create (1024,
+					 local_htab_hash,
+					 local_htab_eq,
+					 NULL);
+  ret->loc_hash_memory = objalloc_create ();
+  if (!ret->loc_hash_table || !ret->loc_hash_memory)
+    {
+      _bfd_mips_elf_link_hash_table_free (abfd);
+      return NULL;
+    }
   return &ret->root.root;
 }
 
@@ -15509,7 +16358,9 @@ _bfd_mips_elf_get_target_dtag (bfd_vma dtag)
       return "DT_MIPS_PLTGOT";
     case DT_MIPS_RWPLT:
       return "DT_MIPS_RWPLT";
-    }
+    case DT_MIPS_GENERAL_GOTNO:
+      return "DT_MIPS_GENERAL_GOTNO";
+   }
 }
 
 /* Return the meaning of Tag_GNU_MIPS_ABI_FP value FP, or null if
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 18e3c40..2d0742d 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -1199,6 +1199,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_MIPS_COPY",
   "BFD_RELOC_MIPS_JUMP_SLOT",
 
+  "BFD_RELOC_MIPS_IRELATIVE",
   "BFD_RELOC_MOXIE_10_PCREL",
 
   "BFD_RELOC_FT32_10",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 3478006..18ed890 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2450,6 +2450,11 @@ ENUMDOC
 COMMENT
 
 ENUM
+  BFD_RELOC_MIPS_IRELATIVE
+ENUMDOC
+  MIPS support for STT_GNU_IFUNC.
+
+ENUM
   BFD_RELOC_MOXIE_10_PCREL
 ENUMDOC
   Moxie ELF relocations.
diff --git a/binutils/readelf.c b/binutils/readelf.c
index c313db4..7f97758 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -1724,6 +1724,8 @@ get_mips_dynamic_type (unsigned long type)
     case DT_MIPS_AUX_DYNAMIC: return "MIPS_AUX_DYNAMIC";
     case DT_MIPS_PLTGOT: return "MIPS_PLTGOT";
     case DT_MIPS_RWPLT: return "MIPS_RWPLT";
+    case DT_MIPS_GENERAL_GOTNO: return "MIPS_GENERAL_GOTNO";
+
     default:
       return NULL;
     }
@@ -8514,6 +8516,7 @@ dynamic_section_mips_val (Elf_Internal_Dyn * entry)
     case DT_MIPS_DELTA_SYM_NO:
     case DT_MIPS_DELTA_CLASSSYM_NO:
     case DT_MIPS_COMPACT_SIZE:
+    case DT_MIPS_GENERAL_GOTNO:
       print_vma (entry->d_un.d_ptr, DEC);
       break;
 
diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h
index 722984e..f919fb8 100644
--- a/elfcpp/elfcpp.h
+++ b/elfcpp/elfcpp.h
@@ -866,6 +866,8 @@ enum DT
   DT_MIPS_PLTGOT = 0x70000032,
   // Points to the base of a writable PLT.
   DT_MIPS_RWPLT = 0x70000034,
+  // The GOT index of the first implicitly relocated GOT entry.
+  DT_MIPS_GENERAL_GOTNO = 0x70000036,
 
   DT_AUXILIARY = 0x7ffffffd,
   DT_USED = 0x7ffffffe,
diff --git a/include/elf/mips.h b/include/elf/mips.h
index 57de3bc..4005047 100644
--- a/include/elf/mips.h
+++ b/include/elf/mips.h
@@ -115,6 +115,9 @@ START_RELOC_NUMBERS (elf_mips_reloc_type)
   RELOC_NUMBER (R_MIPS_COPY, 126)
   RELOC_NUMBER (R_MIPS_JUMP_SLOT, 127)
 
+  /* STT_GNU_IFUNC support */
+  RELOC_NUMBER (R_MIPS_IRELATIVE, 128)
+
   /* These relocations are specific to microMIPS.  */
   FAKE_RELOC (R_MICROMIPS_min, 130)
   RELOC_NUMBER (R_MICROMIPS_26_S1, 133)
@@ -751,6 +754,10 @@ extern void bfd_mips_elf32_swap_reginfo_out
 
 /* Relative offset of run time loader map, used for debugging.  */
 #define DT_MIPS_RLD_MAP_REL    0x70000035
+
+/* The GOT index of the first implicitly relocated GOT entry.  */
+#define DT_MIPS_GENERAL_GOTNO  0x70000036
+
 ?
 /* Flags which may appear in a DT_MIPS_FLAGS entry.  */
 
-- 
1.7.9.5

Attachment: Spec.txt
Description: Spec.txt

From a2c0aab6fb7bb35134a7d404add3301cc99569d0 Mon Sep 17 00:00:00 2001
From: Faraz Shahbazker <faraz.shahbazker@imgtec.com>
Date: Tue, 21 Jul 2015 04:15:25 -0700
Subject: [PATCH 2/2] [RFC] Add IFUNC tests for MIPS

ld/testsuite/ChangeLog:

	* ld-mips-elf/mips-ifunc.exp: Ifunc test script
	* ifunc-3-n32.r, ifunc-3-n32.sym, ifunc-3-n32.t, ifunc-3-n64.r,
	ifunc-3-n64.sym, ifunc-3-n64.t, ifunc-3-o32.r, ifunc-3-o32.sym,
	ifunc-3-o32.t, ifunc-4-n32.r, ifunc-4-n32.sym, ifunc-4-n32.t,
	ifunc-4-n64.r, ifunc-4-n64.sym, ifunc-4-n64.t, ifunc-4-o32.r,
	ifunc-4-o32.sym, ifunc-4-o32.t, ifunc-5-n32.g, ifunc-5-n32.r,
	ifunc-5-n32.sym, ifunc-5-n64.g, ifunc-5-n64.r, ifunc-5-n64.sym,
	ifunc-5-o32.g, ifunc-5-o32.r, ifunc-5-o32.sym, ifunc-6-n32.r,
	ifunc-6-n32.sym, ifunc-6-n64.r, ifunc-6-n64.sym, ifunc-6-o32.r,
	ifunc-6-o32.sym, ifunc-dyn-def.s, ifunc-dyn-main.s,
	ifunc-dyn-ref.s, ifunc-dyn.ld, ifunc-iplt.ld,
	ifunc-iplt-0x400000.t, ifunc-iplt-0x400000000.t,
	ifunc-iplt-0x4000000000000.t, ifunc-static-def.s,
	ifunc-static-main.s, ifunc-static-ref.s, ifunc-static.ld,
	libifunc-1-n32.sym, libifunc-1-n64.sym, libifunc-1-o32.sym,
	libifunc-2-n32.sym, libifunc-2-n64.sym, libifunc-2-o32.sym:
	New tests.
---
 ld/testsuite/ld-mips-elf/ifunc-3-n32.r             |    4 +
 ld/testsuite/ld-mips-elf/ifunc-3-n32.sym           |    6 +
 ld/testsuite/ld-mips-elf/ifunc-3-n32.t             |   10 ++
 ld/testsuite/ld-mips-elf/ifunc-3-n64.r             |    8 +
 ld/testsuite/ld-mips-elf/ifunc-3-n64.sym           |    6 +
 ld/testsuite/ld-mips-elf/ifunc-3-n64.t             |   11 ++
 ld/testsuite/ld-mips-elf/ifunc-3-o32.r             |    4 +
 ld/testsuite/ld-mips-elf/ifunc-3-o32.sym           |    6 +
 ld/testsuite/ld-mips-elf/ifunc-3-o32.t             |   11 ++
 ld/testsuite/ld-mips-elf/ifunc-4-n32.r             |    3 +
 ld/testsuite/ld-mips-elf/ifunc-4-n32.sym           |   29 ++++
 ld/testsuite/ld-mips-elf/ifunc-4-n32.t             |   10 ++
 ld/testsuite/ld-mips-elf/ifunc-4-n64.r             |    5 +
 ld/testsuite/ld-mips-elf/ifunc-4-n64.sym           |   29 ++++
 ld/testsuite/ld-mips-elf/ifunc-4-n64.t             |   11 ++
 ld/testsuite/ld-mips-elf/ifunc-4-o32.r             |    3 +
 ld/testsuite/ld-mips-elf/ifunc-4-o32.sym           |   24 +++
 ld/testsuite/ld-mips-elf/ifunc-4-o32.t             |   11 ++
 ld/testsuite/ld-mips-elf/ifunc-5-n32.r             |    4 +
 ld/testsuite/ld-mips-elf/ifunc-5-n32.sym           |    5 +
 ld/testsuite/ld-mips-elf/ifunc-5-n64.r             |    8 +
 ld/testsuite/ld-mips-elf/ifunc-5-n64.sym           |    5 +
 ld/testsuite/ld-mips-elf/ifunc-5-o32.r             |    4 +
 ld/testsuite/ld-mips-elf/ifunc-5-o32.sym           |    6 +
 ld/testsuite/ld-mips-elf/ifunc-6-n32.r             |    4 +
 ld/testsuite/ld-mips-elf/ifunc-6-n32.sym           |    6 +
 ld/testsuite/ld-mips-elf/ifunc-6-n64.r             |    8 +
 ld/testsuite/ld-mips-elf/ifunc-6-n64.sym           |    6 +
 ld/testsuite/ld-mips-elf/ifunc-6-o32.r             |    4 +
 ld/testsuite/ld-mips-elf/ifunc-6-o32.sym           |    6 +
 ld/testsuite/ld-mips-elf/ifunc-dyn-def.s           |   67 +++++++++
 ld/testsuite/ld-mips-elf/ifunc-dyn-main.s          |   39 +++++
 ld/testsuite/ld-mips-elf/ifunc-dyn-ref.s           |   43 ++++++
 ld/testsuite/ld-mips-elf/ifunc-dyn.ld              |   25 ++++
 ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000.t     |   11 ++
 ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000000.t  |   13 ++
 .../ld-mips-elf/ifunc-iplt-0x4000000000000.t       |   14 ++
 ld/testsuite/ld-mips-elf/ifunc-iplt.ld             |   26 ++++
 ld/testsuite/ld-mips-elf/ifunc-static-def.s        |  155 ++++++++++++++++++++
 ld/testsuite/ld-mips-elf/ifunc-static-main.s       |   39 +++++
 ld/testsuite/ld-mips-elf/ifunc-static-ref.s        |   39 +++++
 ld/testsuite/ld-mips-elf/ifunc-static.ld           |   27 ++++
 ld/testsuite/ld-mips-elf/libifunc-1-n32.sym        |    4 +
 ld/testsuite/ld-mips-elf/libifunc-1-n64.sym        |    4 +
 ld/testsuite/ld-mips-elf/libifunc-1-o32.sym        |    4 +
 ld/testsuite/ld-mips-elf/libifunc-2-n32.sym        |    4 +
 ld/testsuite/ld-mips-elf/libifunc-2-n64.sym        |    4 +
 ld/testsuite/ld-mips-elf/libifunc-2-o32.sym        |    5 +
 ld/testsuite/ld-mips-elf/mips-ifunc.exp            |  149 +++++++++++++++++++
 49 files changed, 929 insertions(+)
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-n32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-n32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-n32.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-n64.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-n64.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-n64.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-o32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-o32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-3-o32.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-n32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-n32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-n32.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-n64.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-n64.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-n64.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-o32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-o32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-4-o32.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-5-n32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-5-n32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-5-n64.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-5-n64.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-5-o32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-5-o32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-6-n32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-6-n32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-6-n64.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-6-n64.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-6-o32.r
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-6-o32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-dyn-def.s
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-dyn-main.s
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-dyn-ref.s
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-dyn.ld
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000000.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-iplt-0x4000000000000.t
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-iplt.ld
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-static-def.s
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-static-main.s
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-static-ref.s
 create mode 100644 ld/testsuite/ld-mips-elf/ifunc-static.ld
 create mode 100644 ld/testsuite/ld-mips-elf/libifunc-1-n32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/libifunc-1-n64.sym
 create mode 100644 ld/testsuite/ld-mips-elf/libifunc-1-o32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/libifunc-2-n32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/libifunc-2-n64.sym
 create mode 100644 ld/testsuite/ld-mips-elf/libifunc-2-o32.sym
 create mode 100644 ld/testsuite/ld-mips-elf/mips-ifunc.exp

diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-n32.r b/ld/testsuite/ld-mips-elf/ifunc-3-n32.r
new file mode 100644
index 0000000..b14c140
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00000000  00000000 R_MIPS_NONE      
+00000800  00000080 R_MIPS_IRELATIVE 
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-n32.sym b/ld/testsuite/ld-mips-elf/ifunc-3-n32.sym
new file mode 100644
index 0000000..6068a87
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n32.sym
@@ -0,0 +1,6 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    4   0: 00000000     0 FUNC    GLOBAL DEFAULT UND ref1
+    2   0: 00000400    28 FUNC    GLOBAL DEFAULT   1 func1
+    1   1: 00000001     0 SECTION GLOBAL DEFAULT ABS _DYNAMIC_LINKING
+    3   2: 0000100c     0 OBJECT  GLOBAL DEFAULT  14 __RLD_MAP
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-n32.t b/ld/testsuite/ld-mips-elf/ifunc-3-n32.t
new file mode 100644
index 0000000..edf2787
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n32.t
@@ -0,0 +1,10 @@
+tmpdir/ifunc-3-n32:     file format elf32-ntradbigmips
+
+
+Disassembly of section .iplt:
+
+00000400 <.iplt.func1>:
+ 400:	3c0f0000 	lui	t3,0x0
+ 404:	8df90800 	lw	t9,2048\(t3\)
+ 408:	03200008 	jr	t9
+ 40c:	00000000 	nop
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-n64.r b/ld/testsuite/ld-mips-elf/ifunc-3-n64.r
new file mode 100644
index 0000000..53b6455
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n64.r
@@ -0,0 +1,8 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 2 entries:
+  Offset          Info           Type           Sym. Value    Sym. Name
+000000000000  000000000000 R_MIPS_NONE      
+                    Type2: R_MIPS_NONE      
+                    Type3: R_MIPS_NONE      
+000000000800  000000000080 R_MIPS_IRELATIVE 
+                    Type2: R_MIPS_NONE      
+                    Type3: R_MIPS_NONE      
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-n64.sym b/ld/testsuite/ld-mips-elf/ifunc-3-n64.sym
new file mode 100644
index 0000000..bc9c09f
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n64.sym
@@ -0,0 +1,6 @@
+Symbol table for image:
+  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name
+    4   0: 0000000000000000     0 FUNC    GLOBAL DEFAULT UND ref1
+    2   0: 0000000000000400    28 FUNC    GLOBAL DEFAULT   1 func1
+    1   1: 0000000000000001     0 SECTION GLOBAL DEFAULT ABS _DYNAMIC_LINKING
+    3   2: 0000000000001018     0 OBJECT  GLOBAL DEFAULT  14 __RLD_MAP
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-n64.t b/ld/testsuite/ld-mips-elf/ifunc-3-n64.t
new file mode 100644
index 0000000..0df1db2
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n64.t
@@ -0,0 +1,11 @@
+tmpdir/ifunc-3-n64:     file format elf64-tradbigmips
+
+
+Disassembly of section .iplt:
+
+0000000000000400 <.iplt.func1>:
+ 400:	3c0f0000 	lui	t3,0x0
+ 404:	ddf90800 	ld	t9,2048\(t3\)
+ 408:	03200008 	jr	t9
+ 40c:	00000000 	nop
+	...
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-o32.r b/ld/testsuite/ld-mips-elf/ifunc-3-o32.r
new file mode 100644
index 0000000..b14c140
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-o32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00000000  00000000 R_MIPS_NONE      
+00000800  00000080 R_MIPS_IRELATIVE 
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-o32.sym b/ld/testsuite/ld-mips-elf/ifunc-3-o32.sym
new file mode 100644
index 0000000..a6c92d1
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-o32.sym
@@ -0,0 +1,6 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    4   0: 00000000     0 FUNC    GLOBAL DEFAULT UND ref1
+    2   0: 00000400    40 FUNC    GLOBAL DEFAULT   1 func1
+    1   1: 00000001     0 SECTION GLOBAL DEFAULT ABS _DYNAMIC_LINKING
+    3   2: 0000100c     0 OBJECT  GLOBAL DEFAULT  14 __RLD_MAP
diff --git a/ld/testsuite/ld-mips-elf/ifunc-3-o32.t b/ld/testsuite/ld-mips-elf/ifunc-3-o32.t
new file mode 100644
index 0000000..eba438f
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-o32.t
@@ -0,0 +1,11 @@
+tmpdir/ifunc-3-o32:     file format elf32-tradbigmips
+
+
+Disassembly of section .iplt:
+
+00000400 <.iplt.func1>:
+ 400:	3c0f0000 	lui	t7,0x0
+ 404:	8df90800 	lw	t9,2048\(t7\)
+ 408:	00000000 	nop
+ 40c:	03200008 	jr	t9
+ 410:	00000000 	nop
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-n32.r b/ld/testsuite/ld-mips-elf/ifunc-4-n32.r
new file mode 100644
index 0000000..e899ffc
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n32.r
@@ -0,0 +1,3 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 1 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00080800  00000080 R_MIPS_IRELATIVE 
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-n32.sym b/ld/testsuite/ld-mips-elf/ifunc-4-n32.sym
new file mode 100644
index 0000000..4761ae5
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n32.sym
@@ -0,0 +1,29 @@
+Symbol table '.symtab' contains 27 entries:
+   Num:    Value  Size Type    Bind   Vis      Ndx Name
+     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
+     1: 00080000     0 SECTION LOCAL  DEFAULT    1 
+     2: 00080400     0 SECTION LOCAL  DEFAULT    2 
+     3: 00080800     0 SECTION LOCAL  DEFAULT    3 
+     4: 00081000     0 SECTION LOCAL  DEFAULT    4 
+     5: 00081008     0 SECTION LOCAL  DEFAULT    5 
+     6: 00081020     0 SECTION LOCAL  DEFAULT    6 
+     7: 00000000     0 SECTION LOCAL  DEFAULT    7 
+     8: 00000000     0 SECTION LOCAL  DEFAULT    8 
+     9: 00000000     0 SECTION LOCAL  DEFAULT    9 
+    10: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref_main_1.c
+    11: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc.c
+    12: 00080430    36 FUNC    LOCAL  DEFAULT    2 f1_a
+    13: 00080454    36 FUNC    LOCAL  DEFAULT    2 f1_b
+    14: 00080478    36 FUNC    LOCAL  DEFAULT    2 f1_c
+    15: 000804d0     0 NOTYPE  LOCAL  DEFAULT    2 \$L8
+    16: 000804d8     0 NOTYPE  LOCAL  DEFAULT    2 \$L9
+    17: 000804f8     0 NOTYPE  LOCAL  DEFAULT    2 \$L10
+    18: 00080520     0 NOTYPE  LOCAL  DEFAULT    2 \$L13
+    19: 00080518     0 NOTYPE  LOCAL  DEFAULT    2 \$L12
+    20: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref.c
+    21: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
+    22: 00080000    16 FUNC    LOCAL  DEFAULT    1 .iplt.func1
+    23: 0008049c   156 IFUNC   GLOBAL DEFAULT    2 func1
+    24: 00080400    48 FUNC    GLOBAL DEFAULT    2 main
+    25: 00080540    48 FUNC    GLOBAL DEFAULT    2 ref1
+    26: 0008049c   156 FUNC    GLOBAL DEFAULT    2 func1_ifunc
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-n32.t b/ld/testsuite/ld-mips-elf/ifunc-4-n32.t
new file mode 100644
index 0000000..a681706
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n32.t
@@ -0,0 +1,10 @@
+tmpdir/ifunc-4-n32:     file format elf32-ntradbigmips
+
+
+Disassembly of section .iplt:
+
+00080000 <.iplt.func1>:
+   80000:	3c0f0008 	lui	t3,0x8
+   80004:	8df90800 	lw	t9,2048\(t3\)
+   80008:	03200008 	jr	t9
+   8000c:	00000000 	nop
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-n64.r b/ld/testsuite/ld-mips-elf/ifunc-4-n64.r
new file mode 100644
index 0000000..695742b
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n64.r
@@ -0,0 +1,5 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 1 entries:
+  Offset          Info           Type           Sym. Value    Sym. Name
+000000080800  000000000080 R_MIPS_IRELATIVE 
+                    Type2: R_MIPS_NONE      
+                    Type3: R_MIPS_NONE      
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-n64.sym b/ld/testsuite/ld-mips-elf/ifunc-4-n64.sym
new file mode 100644
index 0000000..0d044f5
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n64.sym
@@ -0,0 +1,29 @@
+Symbol table '.symtab' contains 27 entries:
+   Num:    Value          Size Type    Bind   Vis      Ndx Name
+     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
+     1: 0000000000080000     0 SECTION LOCAL  DEFAULT    1 
+     2: 0000000000080400     0 SECTION LOCAL  DEFAULT    2 
+     3: 0000000000080800     0 SECTION LOCAL  DEFAULT    3 
+     4: 0000000000081000     0 SECTION LOCAL  DEFAULT    4 
+     5: 0000000000081010     0 SECTION LOCAL  DEFAULT    5 
+     6: 0000000000081088     0 SECTION LOCAL  DEFAULT    6 
+     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
+     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
+     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
+    10: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref_main_1.c
+    11: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS ifunc.c
+    12: 0000000000080430    36 FUNC    LOCAL  DEFAULT    2 f1_a
+    13: 0000000000080454    36 FUNC    LOCAL  DEFAULT    2 f1_b
+    14: 0000000000080478    36 FUNC    LOCAL  DEFAULT    2 f1_c
+    15: 00000000000804d0     0 NOTYPE  LOCAL  DEFAULT    2 \$L8
+    16: 00000000000804d8     0 NOTYPE  LOCAL  DEFAULT    2 \$L9
+    17: 00000000000804f8     0 NOTYPE  LOCAL  DEFAULT    2 \$L10
+    18: 0000000000080520     0 NOTYPE  LOCAL  DEFAULT    2 \$L13
+    19: 0000000000080518     0 NOTYPE  LOCAL  DEFAULT    2 \$L12
+    20: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref.c
+    21: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
+    22: 0000000000080000    32 FUNC    LOCAL  DEFAULT    1 .iplt.func1
+    23: 000000000008049c   156 IFUNC   GLOBAL DEFAULT    2 func1
+    24: 0000000000080400    48 FUNC    GLOBAL DEFAULT    2 main
+    25: 0000000000080540    48 FUNC    GLOBAL DEFAULT    2 ref1
+    26: 000000000008049c   156 FUNC    GLOBAL DEFAULT    2 func1_ifunc
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-n64.t b/ld/testsuite/ld-mips-elf/ifunc-4-n64.t
new file mode 100644
index 0000000..bf5a2d2
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n64.t
@@ -0,0 +1,11 @@
+tmpdir/ifunc-4-n64:     file format elf64-tradbigmips
+
+
+Disassembly of section .iplt:
+
+0000000000080000 <.iplt.func1>:
+   80000:	3c0f0008 	lui	t3,0x8
+   80004:	ddf90800 	ld	t9,2048\(t3\)
+   80008:	03200008 	jr	t9
+   8000c:	00000000 	nop
+	...
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-o32.r b/ld/testsuite/ld-mips-elf/ifunc-4-o32.r
new file mode 100644
index 0000000..e899ffc
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-o32.r
@@ -0,0 +1,3 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 1 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00080800  00000080 R_MIPS_IRELATIVE 
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-o32.sym b/ld/testsuite/ld-mips-elf/ifunc-4-o32.sym
new file mode 100644
index 0000000..4f6e13e
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-o32.sym
@@ -0,0 +1,24 @@
+Symbol table '.symtab' contains 22 entries:
+   Num:    Value  Size Type    Bind   Vis      Ndx Name
+     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
+     1: 00080000     0 SECTION LOCAL  DEFAULT    1 
+     2: 00080400     0 SECTION LOCAL  DEFAULT    2 
+     3: 00080800     0 SECTION LOCAL  DEFAULT    3 
+     4: 00081000     0 SECTION LOCAL  DEFAULT    4 
+     5: 00081008     0 SECTION LOCAL  DEFAULT    5 
+     6: 00081020     0 SECTION LOCAL  DEFAULT    6 
+     7: 00000000     0 SECTION LOCAL  DEFAULT    7 
+     8: 00000000     0 SECTION LOCAL  DEFAULT    8 
+     9: 00000000     0 SECTION LOCAL  DEFAULT    9 
+    10: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref_main_1.c
+    11: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc.c
+    12: 00080430    36 FUNC    LOCAL  DEFAULT    2 f1_a
+    13: 00080454    36 FUNC    LOCAL  DEFAULT    2 f1_b
+    14: 00080478    36 FUNC    LOCAL  DEFAULT    2 f1_c
+    15: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref.c
+    16: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
+    17: 00080000    20 FUNC    LOCAL  DEFAULT    1 .iplt.func1
+    18: 0008049c   156 IFUNC   GLOBAL DEFAULT    2 func1
+    19: 00080400    48 FUNC    GLOBAL DEFAULT    2 main
+    20: 00080540    48 FUNC    GLOBAL DEFAULT    2 ref1
+    21: 0008049c   156 FUNC    GLOBAL DEFAULT    2 func1_ifunc
diff --git a/ld/testsuite/ld-mips-elf/ifunc-4-o32.t b/ld/testsuite/ld-mips-elf/ifunc-4-o32.t
new file mode 100644
index 0000000..bc71cb0
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-o32.t
@@ -0,0 +1,11 @@
+tmpdir/ifunc-4-o32:     file format elf32-tradbigmips
+
+
+Disassembly of section .iplt:
+
+00080000 <.iplt.func1>:
+   80000:	3c0f0008 	lui	t7,0x8
+   80004:	8df90800 	lw	t9,2048\(t7\)
+   80008:	00000000 	nop
+   8000c:	03200008 	jr	t9
+   80010:	00000000 	nop
diff --git a/ld/testsuite/ld-mips-elf/ifunc-5-n32.r b/ld/testsuite/ld-mips-elf/ifunc-5-n32.r
new file mode 100644
index 0000000..fb0ef4a
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-n32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00000000  00000000 R_MIPS_NONE      
+00000410  00000403 R_MIPS_REL32      func1\(\)    func1
diff --git a/ld/testsuite/ld-mips-elf/ifunc-5-n32.sym b/ld/testsuite/ld-mips-elf/ifunc-5-n32.sym
new file mode 100644
index 0000000..3756726
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-n32.sym
@@ -0,0 +1,5 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    2   0: 00000030    60 FUNC    GLOBAL DEFAULT   1 ref1
+    4   0: 0000000c    28 IFUNC   GLOBAL DEFAULT   1 func1
+    3   1: 0000000c    28 FUNC    GLOBAL DEFAULT   1 func1_ifunc
diff --git a/ld/testsuite/ld-mips-elf/ifunc-5-n64.r b/ld/testsuite/ld-mips-elf/ifunc-5-n64.r
new file mode 100644
index 0000000..718d84a
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-n64.r
@@ -0,0 +1,8 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 2 entries:
+  Offset          Info           Type           Sym. Value    Sym. Name
+000000000000  000000000000 R_MIPS_NONE      
+                    Type2: R_MIPS_NONE      
+                    Type3: R_MIPS_NONE      
+000000000420  000400001203 R_MIPS_REL32      func1\(\)          func1
+                    Type2: R_MIPS_64        
+                    Type3: R_MIPS_NONE      
diff --git a/ld/testsuite/ld-mips-elf/ifunc-5-n64.sym b/ld/testsuite/ld-mips-elf/ifunc-5-n64.sym
new file mode 100644
index 0000000..c28e09a
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-n64.sym
@@ -0,0 +1,5 @@
+Symbol table for image:
+  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name
+    2   0: 0000000000000030    60 FUNC    GLOBAL DEFAULT   1 ref1
+    4   0: 000000000000000c    28 IFUNC   GLOBAL DEFAULT   1 func1
+    3   1: 000000000000000c    28 FUNC    GLOBAL DEFAULT   1 func1_ifunc
diff --git a/ld/testsuite/ld-mips-elf/ifunc-5-o32.r b/ld/testsuite/ld-mips-elf/ifunc-5-o32.r
new file mode 100644
index 0000000..dbb3325
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-o32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00000000  00000000 R_MIPS_NONE      
+00000410  00000503 R_MIPS_REL32      func1\(\)    func1
diff --git a/ld/testsuite/ld-mips-elf/ifunc-5-o32.sym b/ld/testsuite/ld-mips-elf/ifunc-5-o32.sym
new file mode 100644
index 0000000..66a60f7
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-o32.sym
@@ -0,0 +1,6 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    3   0: 00000040    76 FUNC    GLOBAL DEFAULT   1 ref1
+    5   0: 0000000c    40 IFUNC   GLOBAL DEFAULT   1 func1
+    2   0: 00000000     0 SECTION GLOBAL DEFAULT ABS _gp_disp
+    4   1: 0000000c    40 FUNC    GLOBAL DEFAULT   1 func1_ifunc
diff --git a/ld/testsuite/ld-mips-elf/ifunc-6-n32.r b/ld/testsuite/ld-mips-elf/ifunc-6-n32.r
new file mode 100644
index 0000000..b14c140
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-n32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00000000  00000000 R_MIPS_NONE      
+00000800  00000080 R_MIPS_IRELATIVE 
diff --git a/ld/testsuite/ld-mips-elf/ifunc-6-n32.sym b/ld/testsuite/ld-mips-elf/ifunc-6-n32.sym
new file mode 100644
index 0000000..2c822d9
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-n32.sym
@@ -0,0 +1,6 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    4   0: 00000070    60 FUNC    GLOBAL DEFAULT   1 ref1
+    2   0: 00000400    28 FUNC    GLOBAL DEFAULT   1 func1
+    1   1: 00000001     0 SECTION GLOBAL DEFAULT ABS _DYNAMIC_LINKING
+    3   2: 00001000     0 OBJECT  GLOBAL DEFAULT  12 __RLD_MAP
diff --git a/ld/testsuite/ld-mips-elf/ifunc-6-n64.r b/ld/testsuite/ld-mips-elf/ifunc-6-n64.r
new file mode 100644
index 0000000..53b6455
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-n64.r
@@ -0,0 +1,8 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 2 entries:
+  Offset          Info           Type           Sym. Value    Sym. Name
+000000000000  000000000000 R_MIPS_NONE      
+                    Type2: R_MIPS_NONE      
+                    Type3: R_MIPS_NONE      
+000000000800  000000000080 R_MIPS_IRELATIVE 
+                    Type2: R_MIPS_NONE      
+                    Type3: R_MIPS_NONE      
diff --git a/ld/testsuite/ld-mips-elf/ifunc-6-n64.sym b/ld/testsuite/ld-mips-elf/ifunc-6-n64.sym
new file mode 100644
index 0000000..500aada
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-n64.sym
@@ -0,0 +1,6 @@
+Symbol table for image:
+  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name
+    4   0: 0000000000000070    60 FUNC    GLOBAL DEFAULT   1 ref1
+    2   0: 0000000000000400    28 FUNC    GLOBAL DEFAULT   1 func1
+    1   1: 0000000000000001     0 SECTION GLOBAL DEFAULT ABS _DYNAMIC_LINKING
+    3   2: 0000000000001000     0 OBJECT  GLOBAL DEFAULT  12 __RLD_MAP
diff --git a/ld/testsuite/ld-mips-elf/ifunc-6-o32.r b/ld/testsuite/ld-mips-elf/ifunc-6-o32.r
new file mode 100644
index 0000000..b14c140
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-o32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00000000  00000000 R_MIPS_NONE      
+00000800  00000080 R_MIPS_IRELATIVE 
diff --git a/ld/testsuite/ld-mips-elf/ifunc-6-o32.sym b/ld/testsuite/ld-mips-elf/ifunc-6-o32.sym
new file mode 100644
index 0000000..a39cb03
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-o32.sym
@@ -0,0 +1,6 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    4   0: 00000080    76 FUNC    GLOBAL DEFAULT   1 ref1
+    2   0: 00000400    40 FUNC    GLOBAL DEFAULT   1 func1
+    1   1: 00000001     0 SECTION GLOBAL DEFAULT ABS _DYNAMIC_LINKING
+    3   2: 00001000     0 OBJECT  GLOBAL DEFAULT  12 __RLD_MAP
diff --git a/ld/testsuite/ld-mips-elf/ifunc-dyn-def.s b/ld/testsuite/ld-mips-elf/ifunc-dyn-def.s
new file mode 100644
index 0000000..9b5d2a8
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-dyn-def.s
@@ -0,0 +1,67 @@
+	.file	1 "ifunc.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	legacy
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1_a
+	.type	f1_a, @function
+f1_a:
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	f1_a
+	.size	f1_a, .-f1_a
+	.align	2
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1_b
+	.type	f1_b, @function
+f1_b:
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	f1_b
+	.size	f1_b, .-f1_b
+	.align	2
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1_c
+	.type	f1_c, @function
+f1_c:
+	.frame	$fp,8,$31		# vars= 0, regs= 1/0, args= 0, gp= 0
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	f1_c
+	.size	f1_c, .-f1_c
+	.align	2
+	.globl	func1_ifunc
+	.set	nomips16
+	.set	nomicromips
+	.ent	func1_ifunc
+	.type	func1_ifunc, @function
+func1_ifunc:
+	lw	$2,%got(f1_a)($28)
+	addiu	$2,$2,%lo(f1_a)
+	lw	$2,%got(f1_b)($28)
+	addiu	$2,$2,%lo(f1_b)
+	lw	$2,%got(f1_c)($28)
+	addiu	$2,$2,%lo(f1_c)
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	func1_ifunc
+	.size	func1_ifunc, .-func1_ifunc
+	.globl	func1
+	.type	func1, @gnu_indirect_function
+	func1 = func1_ifunc
+	.ident	"GCC: (GNU) 4.9.0 20130917 (experimental)"
diff --git a/ld/testsuite/ld-mips-elf/ifunc-dyn-main.s b/ld/testsuite/ld-mips-elf/ifunc-dyn-main.s
new file mode 100644
index 0000000..8c12176
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-dyn-main.s
@@ -0,0 +1,39 @@
+	.file	1 "ifunc_ref_main_2.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	legacy
+	.gnu_attribute 4, 1
+	.abicalls
+	.option	pic0
+	.text
+	.align	2
+	.globl	main
+	.set	nomips16
+	.set	nomicromips
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$fp,32,$31		# vars= 0, regs= 2/0, args= 16, gp= 8
+	.mask	0xc0000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+	sw	$fp,24($sp)
+	move	$fp,$sp
+	jal	ref1
+	nop
+
+	move	$sp,$fp
+	lw	$31,28($sp)
+	lw	$fp,24($sp)
+	addiu	$sp,$sp,32
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.ident	"GCC: (GNU) 4.9.0 20130930 (experimental)"
diff --git a/ld/testsuite/ld-mips-elf/ifunc-dyn-ref.s b/ld/testsuite/ld-mips-elf/ifunc-dyn-ref.s
new file mode 100644
index 0000000..d562096
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-dyn-ref.s
@@ -0,0 +1,43 @@
+	.file	1 "ifunc_ref.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	legacy
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	ref1
+	.set	nomips16
+	.set	nomicromips
+	.ent	ref1
+	.type	ref1, @function
+ref1:
+	.frame	$fp,32,$31		# vars= 0, regs= 2/0, args= 16, gp= 8
+	.mask	0xc0000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload	$25
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+	sw	$fp,24($sp)
+	move	$fp,$sp
+	.cprestore	16
+	lw	$2,%call16(func1)($28)
+	move	$25,$2
+	jalr	$25
+	nop
+
+	lw	$28,16($fp)
+	move	$sp,$fp
+	lw	$31,28($sp)
+	lw	$fp,24($sp)
+	addiu	$sp,$sp,32
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	ref1
+	.size	ref1, .-ref1
+	.ident	"GCC: (GNU) 4.9.0 20130930 (experimental)"
diff --git a/ld/testsuite/ld-mips-elf/ifunc-dyn.ld b/ld/testsuite/ld-mips-elf/ifunc-dyn.ld
new file mode 100644
index 0000000..c82c8ce
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-dyn.ld
@@ -0,0 +1,25 @@
+ENTRY(_start)
+SECTIONS
+{
+  . = ALIGN (0x400);
+  .text : { *(.text) }
+
+  . = ALIGN (0x400);
+  .iplt : { *(.iplt) }
+
+  . = ALIGN (0x400);
+  .igot : { *(.igot) }
+
+  . = ALIGN (0x400);
+  .got : { *(.got) }
+
+  . = ALIGN (0x400);
+  .data : { *(.data) }
+
+  . = ALIGN (0x1000);
+  .rel.dyn        :
+    {
+      *(.rel.*)
+    }
+
+}
diff --git a/ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000.t b/ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000.t
new file mode 100644
index 0000000..c46a85d
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000.t
@@ -0,0 +1,11 @@
+tmpdir/ifunc-iplt-0x400000:     file format elf64-tradbigmips
+
+
+Disassembly of section .iplt:
+
+0000000000400188 <.iplt.func1>:
+  400188:	3c0f0041 	lui	t3,0x41
+  40018c:	ddf90320 	ld	t9,800\(t3\)
+  400190:	03200008 	jr	t9
+  400194:	00000000 	nop
+	...
diff --git a/ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000000.t b/ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000000.t
new file mode 100644
index 0000000..3562b23
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-iplt-0x400000000.t
@@ -0,0 +1,13 @@
+tmpdir/ifunc-iplt-0x400000000:     file format elf64-tradbigmips
+
+
+Disassembly of section .iplt:
+
+0000000400000188 <.iplt.func1>:
+   400000188:	3c0f0004 	lui	t3,0x4
+   40000018c:	25ef0001 	addiu	t3,t3,1
+   400000190:	000f7c38 	dsll	t3,t3,0x10
+   400000194:	ddf90320 	ld	t9,800\(t3\)
+   400000198:	03200008 	jr	t9
+   40000019c:	00000000 	nop
+	...
diff --git a/ld/testsuite/ld-mips-elf/ifunc-iplt-0x4000000000000.t b/ld/testsuite/ld-mips-elf/ifunc-iplt-0x4000000000000.t
new file mode 100644
index 0000000..90e8306
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-iplt-0x4000000000000.t
@@ -0,0 +1,14 @@
+tmpdir/ifunc-iplt-0x4000000000000:     file format elf64-tradbigmips
+
+
+Disassembly of section .iplt:
+
+0004000000000188 <.iplt.func1>:
+   4000000000188:	3c0f0004 	lui	t3,0x4
+   400000000018c:	3c0e0001 	lui	t2,0x1
+   4000000000190:	25ef0000 	addiu	t3,t3,0
+   4000000000194:	000f783c 	dsll32	t3,t3,0x0
+   4000000000198:	01ee782d 	daddu	t3,t3,t2
+   400000000019c:	ddf90320 	ld	t9,800\(t3\)
+   40000000001a0:	03200008 	jr	t9
+   40000000001a4:	00000000 	nop
diff --git a/ld/testsuite/ld-mips-elf/ifunc-iplt.ld b/ld/testsuite/ld-mips-elf/ifunc-iplt.ld
new file mode 100644
index 0000000..a26b803
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-iplt.ld
@@ -0,0 +1,26 @@
+ENTRY(_start)
+SECTIONS
+{
+  . = ALIGN (0x400);
+  .iplt : { *(.iplt) }
+
+  . = ALIGN (0x400);
+  .text : { *(.text) }
+
+  . = ALIGN (0x400);
+  .igot : { *(.igot) }
+
+  . = ALIGN (0x400);
+  .got : { *(.got) }
+
+  . = ALIGN (0x400);
+  .data : { *(.data) }
+
+  . = ALIGN (0x1000);
+  .rel.dyn        :
+    {
+      *(.rel.*)
+    }
+/*  /DISCARD/ : { *(*) } */
+
+}
diff --git a/ld/testsuite/ld-mips-elf/ifunc-static-def.s b/ld/testsuite/ld-mips-elf/ifunc-static-def.s
new file mode 100644
index 0000000..cf84696
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-static-def.s
@@ -0,0 +1,155 @@
+	.file	1 "ifunc.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	legacy
+	.gnu_attribute 4, 1
+	.abicalls
+	.option	pic0
+	.text
+	.align	2
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1_a
+	.type	f1_a, @function
+f1_a:
+	.frame	$fp,8,$31		# vars= 0, regs= 1/0, args= 0, gp= 0
+	.mask	0x40000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-8
+	sw	$fp,4($sp)
+	move	$fp,$sp
+	li	$2,1			# 0x1
+	move	$sp,$fp
+	lw	$fp,4($sp)
+	addiu	$sp,$sp,8
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	f1_a
+	.size	f1_a, .-f1_a
+	.align	2
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1_b
+	.type	f1_b, @function
+f1_b:
+	.frame	$fp,8,$31		# vars= 0, regs= 1/0, args= 0, gp= 0
+	.mask	0x40000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-8
+	sw	$fp,4($sp)
+	move	$fp,$sp
+	li	$2,2			# 0x2
+	move	$sp,$fp
+	lw	$fp,4($sp)
+	addiu	$sp,$sp,8
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	f1_b
+	.size	f1_b, .-f1_b
+	.align	2
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1_c
+	.type	f1_c, @function
+f1_c:
+	.frame	$fp,8,$31		# vars= 0, regs= 1/0, args= 0, gp= 0
+	.mask	0x40000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-8
+	sw	$fp,4($sp)
+	move	$fp,$sp
+	li	$2,3			# 0x3
+	move	$sp,$fp
+	lw	$fp,4($sp)
+	addiu	$sp,$sp,8
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	f1_c
+	.size	f1_c, .-f1_c
+	.align	2
+	.globl	func1_ifunc
+	.set	nomips16
+	.set	nomicromips
+	.ent	func1_ifunc
+	.type	func1_ifunc, @function
+func1_ifunc:
+	.frame	$fp,432,$31		# vars= 400, regs= 2/0, args= 16, gp= 8
+	.mask	0xc0000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-432
+	sw	$31,428($sp)
+	sw	$fp,424($sp)
+	move	$fp,$sp
+	addiu	$2,$fp,28
+	move	$4,$2
+	nop
+
+	beq	$2,$0,$L8
+	nop
+
+	li	$2,48			# 0x30
+	sw	$2,24($fp)
+	j	$L9
+	nop
+
+$L8:
+	li	$2,3			# 0x3
+	sw	$2,24($fp)
+$L9:
+	lw	$2,24($fp)
+	andi	$2,$2,0xf0
+	beq	$2,$0,$L10
+	nop
+
+	lui	$2,%hi(f1_a)
+	addiu	$2,$2,%lo(f1_a)
+	j	$L13
+	nop
+
+$L10:
+	lw	$2,24($fp)
+	andi	$2,$2,0xf
+	beq	$2,$0,$L12
+	nop
+
+	lui	$2,%hi(f1_b)
+	addiu	$2,$2,%lo(f1_b)
+	j	$L13
+	nop
+
+$L12:
+	lui	$2,%hi(f1_c)
+	addiu	$2,$2,%lo(f1_c)
+$L13:
+	move	$sp,$fp
+	lw	$31,428($sp)
+	lw	$fp,424($sp)
+	addiu	$sp,$sp,432
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	func1_ifunc
+	.size	func1_ifunc, .-func1_ifunc
+	.globl	func1
+	.type	func1, @gnu_indirect_function
+	func1 = func1_ifunc
+	.ident	"GCC: (GNU) 4.9.0 20130930 (experimental)"
diff --git a/ld/testsuite/ld-mips-elf/ifunc-static-main.s b/ld/testsuite/ld-mips-elf/ifunc-static-main.s
new file mode 100644
index 0000000..2093a42
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-static-main.s
@@ -0,0 +1,39 @@
+	.file	1 "ifunc_ref_main_1.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	legacy
+	.gnu_attribute 4, 1
+	.abicalls
+	.option	pic0
+	.text
+	.align	2
+	.globl	main
+	.set	nomips16
+	.set	nomicromips
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$fp,32,$31		# vars= 0, regs= 2/0, args= 16, gp= 8
+	.mask	0xc0000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+	sw	$fp,24($sp)
+	move	$fp,$sp
+	jal	func1
+	nop
+
+	move	$sp,$fp
+	lw	$31,28($sp)
+	lw	$fp,24($sp)
+	addiu	$sp,$sp,32
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.ident	"GCC: (GNU) 4.9.0 20130930 (experimental)"
diff --git a/ld/testsuite/ld-mips-elf/ifunc-static-ref.s b/ld/testsuite/ld-mips-elf/ifunc-static-ref.s
new file mode 100644
index 0000000..e17db14
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-static-ref.s
@@ -0,0 +1,39 @@
+	.file	1 "ifunc_ref.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	legacy
+	.gnu_attribute 4, 1
+	.abicalls
+	.option	pic0
+	.text
+	.align	2
+	.globl	ref1
+	.set	nomips16
+	.set	nomicromips
+	.ent	ref1
+	.type	ref1, @function
+ref1:
+	.frame	$fp,32,$31		# vars= 0, regs= 2/0, args= 16, gp= 8
+	.mask	0xc0000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+	sw	$fp,24($sp)
+	move	$fp,$sp
+	jal	func1
+	nop
+
+	move	$sp,$fp
+	lw	$31,28($sp)
+	lw	$fp,24($sp)
+	addiu	$sp,$sp,32
+	j	$31
+	nop
+
+	.set	macro
+	.set	reorder
+	.end	ref1
+	.size	ref1, .-ref1
+	.ident	"GCC: (GNU) 4.9.0 20130930 (experimental)"
diff --git a/ld/testsuite/ld-mips-elf/ifunc-static.ld b/ld/testsuite/ld-mips-elf/ifunc-static.ld
new file mode 100644
index 0000000..ef07ec6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-static.ld
@@ -0,0 +1,27 @@
+ENTRY(_start)
+SECTIONS
+{
+  . = 0x80000;
+  . = ALIGN (0x400);
+  .iplt : { *(.iplt) }
+
+  . = ALIGN (0x400);
+  .text : { *(.text) }
+
+  . = ALIGN (0x400);
+  .igot : { *(.igot) }
+
+  . = ALIGN (0x400);
+  .got : { *(.got) }
+
+  . = ALIGN (0x400);
+  .data : { *(.data) }
+
+  . = ALIGN (0x1000);
+  .rel.dyn        :
+    {
+      *(.rel.*)
+    }
+/*  /DISCARD/ : { *(*) } */
+
+}
diff --git a/ld/testsuite/ld-mips-elf/libifunc-1-n32.sym b/ld/testsuite/ld-mips-elf/libifunc-1-n32.sym
new file mode 100644
index 0000000..88d7e5d
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/libifunc-1-n32.sym
@@ -0,0 +1,4 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    3   0: 0000000c    28 FUNC    GLOBAL DEFAULT   1 func1_ifunc
+    2   0: 0000000c    28 IFUNC   GLOBAL DEFAULT   1 func1
diff --git a/ld/testsuite/ld-mips-elf/libifunc-1-n64.sym b/ld/testsuite/ld-mips-elf/libifunc-1-n64.sym
new file mode 100644
index 0000000..7236950
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/libifunc-1-n64.sym
@@ -0,0 +1,4 @@
+Symbol table for image:
+  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name
+    3   0: 000000000000000c    28 FUNC    GLOBAL DEFAULT   1 func1_ifunc
+    2   0: 000000000000000c    28 IFUNC   GLOBAL DEFAULT   1 func1
diff --git a/ld/testsuite/ld-mips-elf/libifunc-1-o32.sym b/ld/testsuite/ld-mips-elf/libifunc-1-o32.sym
new file mode 100644
index 0000000..b5fff26
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/libifunc-1-o32.sym
@@ -0,0 +1,4 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    3   0: 0000000c    40 FUNC    GLOBAL DEFAULT   1 func1_ifunc
+    2   0: 0000000c    40 IFUNC   GLOBAL DEFAULT   1 func1
diff --git a/ld/testsuite/ld-mips-elf/libifunc-2-n32.sym b/ld/testsuite/ld-mips-elf/libifunc-2-n32.sym
new file mode 100644
index 0000000..2c1e84e
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/libifunc-2-n32.sym
@@ -0,0 +1,4 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    2   0: 00000000    60 FUNC    GLOBAL DEFAULT   1 ref1
+    3   0: 00000040     0 FUNC    GLOBAL DEFAULT UND func1
diff --git a/ld/testsuite/ld-mips-elf/libifunc-2-n64.sym b/ld/testsuite/ld-mips-elf/libifunc-2-n64.sym
new file mode 100644
index 0000000..3bd53fd
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/libifunc-2-n64.sym
@@ -0,0 +1,4 @@
+Symbol table for image:
+  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name
+    2   0: 0000000000000000    60 FUNC    GLOBAL DEFAULT   1 ref1
+    3   0: 0000000000000040     0 FUNC    GLOBAL DEFAULT UND func1
diff --git a/ld/testsuite/ld-mips-elf/libifunc-2-o32.sym b/ld/testsuite/ld-mips-elf/libifunc-2-o32.sym
new file mode 100644
index 0000000..b831edd
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/libifunc-2-o32.sym
@@ -0,0 +1,5 @@
+Symbol table for image:
+  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
+    3   0: 00000000    76 FUNC    GLOBAL DEFAULT   1 ref1
+    4   0: 00000050     0 FUNC    GLOBAL DEFAULT UND func1
+    2   0: 00000000     0 SECTION GLOBAL DEFAULT ABS _gp_disp
diff --git a/ld/testsuite/ld-mips-elf/mips-ifunc.exp b/ld/testsuite/ld-mips-elf/mips-ifunc.exp
new file mode 100644
index 0000000..ae389cf
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/mips-ifunc.exp
@@ -0,0 +1,149 @@
+# Expect script for MIPS IFUNC linker tests
+#   Copyright 2013
+#   Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+if {![istarget mips*-*-*] || ![is_elf_format]} {
+    return
+}
+
+# General setup
+#############################################
+set has_newabi [expr [istarget *-*-irix6*] \
+		     || [istarget mips*-*-linux*] \
+		     || [istarget mips*-sde-elf*]]
+set linux_gnu [expr [istarget mips*-*-linux*]]
+set embedded_elf [expr [istarget mips*-*-elf]]
+
+# Set defaults.
+set abi_asflags(o32) ""
+set abi_asflags(n32) "-march=from-abi -n32 -EB"
+set abi_asflags(n64) "-march=from-abi -64 -EB"
+set abi_ldflags(o32) ""
+set abi_ldflags(n32) -melf32bmipn32
+set abi_ldflags(n64) -melf64bmip
+
+# Override as needed.
+if { [istarget *-*-irix6*] } {
+    set abi_asflags(o32) "-32 -EB"
+    set abi_ldflags(o32) -melf32bsmip
+} elseif { [istarget mips64*-linux*] } {
+    set abi_asflags(o32) "-32 -EB"
+    set abi_ldflags(o32) -melf32btsmip
+} elseif { [istarget mips64*-*freebsd*] } {
+    set abi_asflags(o32) "-32 -EB"
+    set abi_ldflags(o32) -melf32btsmip_fbsd
+}
+if { [istarget mips*-*-linux*] || [istarget mips*-sde-elf*] } {
+    set abi_ldflags(n32) -melf32btsmipn32
+    set abi_ldflags(n64) -melf64btsmip
+} elseif { [istarget mips64*-*freebsd*] } {
+    set abi_ldflags(n32) -melf32btsmipn32_fbsd
+    set abi_ldflags(n64) -melf64btsmip_fbsd
+}
+#############################################
+
+
+# STT_GNU_IFUNC testing:
+#
+#    1. Dso with ifunc defined code
+#    2. Dso that references external ifunc'ed routines
+#    3. Dynamic executable with ifunc defined code
+#    4. Static executable with ifunc defined and referenced code
+#    5. Dso with with ifunc defined and referenced code
+#    6. Dynamic executable with ifunc defined and referenced code
+# STT_GNU_IFUNC tests.
+set abis [concat o32 [expr {$has_newabi ? "n32 n64" : ""}]]
+foreach { abi } $abis {
+    run_ld_link_tests [list \
+	[list \
+	    "IFUNC 1 (Simple dso with def) ${abi}" \
+	    "$abi_ldflags($abi) -shared -T ifunc-dyn.ld" "" \
+	    "$abi_asflags($abi)" \
+	    [list ifunc-dyn-def.s] \
+	    [list "readelf -Ds libifunc-1-${abi}.sym"] \
+	    "libifunc-1-${abi}.so" \
+        ] \
+	[list \
+	    "IFUNC 2 (Simple dso with ref) ${abi}" \
+	    "$abi_ldflags($abi) -shared -T ifunc-dyn.ld" "" \
+	    "$abi_asflags($abi)" \
+	    [list ifunc-dyn-ref.s] \
+	    [list "readelf -Ds libifunc-2-${abi}.sym"] \
+	    "libifunc-2-${abi}.so" \
+        ] \
+	[list \
+	    "IFUNC 3 (Simple dynamic executable with def) ${abi}" \
+	    "$abi_ldflags($abi) -Bdynamic -L./tmpdir -lifunc-2-${abi} -T ifunc-dyn.ld" "" \
+	    "$abi_asflags($abi)" \
+	    [list ifunc-dyn-main.s ifunc-dyn-def.s] \
+	    [list "readelf -Ds ifunc-3-${abi}.sym" \
+                  "readelf -r ifunc-3-${abi}.r" \
+                  "objdump -dj.iplt ifunc-3-${abi}.t"] \
+	    "ifunc-3-${abi}" \
+        ] \
+	[list \
+	    "IFUNC 4 (Simple static executable with def and ref) ${abi}" \
+	    "$abi_ldflags($abi) -Bstatic -T ifunc-static.ld" "" \
+	    "$abi_asflags($abi) -non_shared" \
+	    [list ifunc-static-main.s ifunc-static-def.s ifunc-static-ref.s] \
+	    [list "readelf -s ifunc-4-${abi}.sym" \
+		  "readelf -r ifunc-4-${abi}.r" \
+                  "objdump -dj.iplt ifunc-4-${abi}.t"] \
+	    "ifunc-4-${abi}" \
+        ] \
+	[list \
+	    "IFUNC 5 (Dynamic shared object with def and ref) ${abi}" \
+	    "$abi_ldflags($abi) -shared -T ifunc-dyn.ld" "" \
+	    "$abi_asflags($abi) -KPIC" \
+	    [list ifunc-dyn-def.s ifunc-dyn-ref.s] \
+	    [list "readelf -Ds ifunc-5-${abi}.sym" \
+                  "readelf -r ifunc-5-${abi}.r"] \
+	    "ifunc-5-${abi}" \
+        ] \
+	[list \
+	    "IFUNC 6 (Dynamic executable with def and ref) ${abi}" \
+	    "$abi_ldflags($abi) -Bdynamic -L./tmpdir -lifunc-2-${abi} -T ifunc-dyn.ld" "" \
+	    "$abi_asflags($abi)" \
+	    [list ifunc-dyn-main.s ifunc-dyn-def.s ifunc-dyn-ref.s] \
+	    [list "readelf -Ds ifunc-6-${abi}.sym" \
+                  "readelf -r ifunc-6-${abi}.r"] \
+	    "ifunc-6-${abi}" \
+        ] \
+    ]
+}
+
+# IPLT sequences change based on how big the address of the
+# .igot.plt section is based on Mips loading immediate values.
+#
+set addrs { "0x400000" "0x400000000" "0x4000000000000" }
+foreach { addr } $addrs {
+    run_ld_link_tests [list \
+	[list \
+	    "IFUNC IPLT (Simple static executable with def and ref) ${addr}" \
+	    "$abi_ldflags(n64) -Bstatic -Ttext-segment ${addr}" "" \
+	    "$abi_asflags(n64) -non_shared" \
+	    [list ifunc-static-main.s ifunc-static-def.s ifunc-static-ref.s] \
+	    [list "objdump -dj.iplt ifunc-iplt-${addr}.t"] \
+	    "ifunc-iplt-${addr}" \
+        ] \
+    ]
+}
+
-- 
1.7.9.5


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]