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]

[PATCH][MIPS] IFUNC support 2nd round


This is my second shot at Mips ifunc support. It was run through the
binutils test suite. I also have and abi for it attached.

Jack



This is the first MIPS ifunc patch for binutils. If you want to install build
and run this patch you will need the companion glibc patch.

This is targeting o32, n32 and n64. Patches for MicroMips, Mips16,and possibly
vxworks will follow this patch.

I attempted to follow the ARM implementation, but the traditional MIPS GOT
design forced me to dirverge a bit.

The only change to the traditional MIPS GOT was to use the .iplt stub address 
for the defined ifunc function instead of the function address in executables.
This should allow seamless multigot support. For defined ifunc routines in 
shared objects have no iplts and depend exclusively on the dynamic symbol
resolution to call the resolver for the correct runtime address.

I believe I have addressed all the issues of the first patch review with the 
exception of one of the forward declarations (fixing it was getting too messy)
and the penalty of shared executables with ifunc definitions forcing everything
through iplts stubs. Still not sure how to avoid that one, but the vast majority
of ifunc use should be by dsos so it may not be a priority.

Preemption and n32 have been tested with runtime testing, but are not yet in
the test-suite.

***************************************
I have a rough ABI description attached to this patch. Below is the abridged
version. The attached ABI needs to be critically reviewed as well, with
suggested wording hopefully.

If an iplt is needed:

        Generate iplt stubs (.iplt)
        Generate igot table (.igot.plt)
        Generate IRELATIVE relocations for .igot.plt

Iplts are needed for executables (a.outs) only.

Non-shared a.outs: 

        Always goes through the iplt with IRELATIVE relocations against
        the .igot.plt.

Dynamic executables (both fPIC and not):

        All a.out references are direct with got entries containing ifunc iplt
        addresses. 
        
        Dso references are through the iplt with IRELATIVE relocations
        against the igot and the dynsym entries being the iplt address
        and the symbol type changed STT_FUNC. The dynamic linker will update
        the igot table in the a.out.

Dynamic shared objects:

        No iplt, igot or IRELATIVE relocations.
        
        The GOT and .dynsym will have the ifunc values and the dynsym type
        will remain STT_GNU_IFUNC. The dynamic linker will detect that 
        the symbol is an STT_GNU_IFUNC and do the fixup based on the defining
        dynsym address.

In all cases the contents of the igot is the ifunc address with an
R_MIPS_IRELATIVE relocation against it.

***************************************

bfd/ChangeLog
	* bfd-in2.h (BFD_RELOC_MIPS_IRELATIVE): New relocation.
	* bfd/elf32-mips.c 
	(elf_mips_eh_howto table): Howto for BFD_RELOC_MIPS_IRELATIVE.
	(bfd_elf32_bfd_reloc_name_lookup): Case for elf_mips_irelative_howto.
	(mips_elf32_rtype_to_howto): Case for BFD_RELOC_MIPS_IRELATIVE.
	* elfxx-mips.c
	(struct mips_elf_link_hash_entry): Offset indexes into iplt and 
	igot.
	(mips_elf_link_hash_entry): Flag to indicate sym needs an iplt.
	(mips_elf_link_hash_table): New shortcut pointers to iplt related 
	sections. New size of an iplt stub.
	(mips_elf_allocate_ireloc): New function forward declaration. I tried
        to get rid of this and just got more undefines. FIXME.
	(mips32_exec_iplt_entry): Template for exec iplt instruction sequence.
	(mips64_exec_iplt_entry): Template for exec iplt instruction sequence.
	(mips_elf_link_hash_newfunc): Initialization of new 
	mips_elf_link_hash_entry elements.
	(mips_elf_allocate_iplt): New function.
	(mips_elf_check_symbols): Allocate an iplt for any IFUNC symbols.
	(mips_elf_create_ifunc_sections): New function. Create ELF sections 
	associated with ifunc use.
        (mips_elf_calculate_relocation): Point symbol value to iplt stub.
        (_bfd_mips_elf_section_processing): Size .igot.iplt section.
	(_bfd_mips_elf_add_symbol_hook): Mark output as has_gnu_symbols.
	(mips_elf_allocate_ireloc): New function.
	(_bfd_mips_elf_check_relocs): Check if we need to create iplt sections.
	If symbol is an IFUNC, don't convert it to an STT_FUNC.
	(allocate_dynrelocs): Call mips_elf_allocate_ireloc if R_MIPS_IRELATIVE.
	(_bfd_mips_elf_adjust_dynamic_symbol): Add STT_GNU_IFUNC to sanity 
	check.
	(mips_elf_create_iplt): New function.
	(_bfd_mips_elf_finish_dynamic_symbol): If the symbol has been marked as,
	needing an iplt call the iplt creation routine. 
	Set the iplt symbol value to the iplt entry address.
	* libbfd.h
	(bfd_reloc_code_real_names): Entry for BFD_RELOC_MIPS_IRELATIVE.
	* reloc.c
	(ENUMDOC): BFD_RELOC_MIPS_IRELATIVE entry.

include/ChangeLog
	* elf/mips.h
	(START_RELOC_NUMBERS): Entry for R_MIPS_IRELATIVE.

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-0x400000.t, ifunc-iplt-0x400000000.t,
	ifunc-iplt-0x4000000000000.t, ifunc-iplt.ld, 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.














diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 644f89d..0cddfa6 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2972,6 +2972,9 @@ to compensate for the borrow when the low bits are added.  */
   BFD_RELOC_MOXIE_10_PCREL,
 
 
+/* MIPS support for STT_GNU_IFUNC.  */
+  BFD_RELOC_MIPS_IRELATIVE,
+
 /* Fujitsu Frv Relocations.  */
   BFD_RELOC_FRV_LABEL16,
   BFD_RELOC_FRV_LABEL24,
diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
index eec2ef7..b05252d 100644
--- a/bfd/elf32-mips.c
+++ b/bfd/elf32-mips.c
@@ -1554,6 +1554,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.  */
 
@@ -2028,6 +2044,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;
     }
 }
 
@@ -2075,6 +2093,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;
 }
@@ -2101,6 +2121,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 1c64ad3..d3730b0 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -372,6 +372,12 @@ struct mips_elf_link_hash_entry
      being called returns a floating point value.  */
   asection *call_fp_stub;
 
+  /* Offset into the IPLT table.  */
+  unsigned int iplt_offset;
+
+  /* Offset into the IGOT table.  */
+  unsigned int igot_offset;
+
   /* The highest GGA_* value that satisfies all references to this symbol.  */
   unsigned int global_got_area : 2;
 
@@ -410,6 +416,9 @@ 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;
 };
 
 /* MIPS ELF linker hash table.  */
@@ -454,6 +463,9 @@ struct mips_elf_link_hash_table
   asection *srelplt2;
   asection *sgotplt;
   asection *splt;
+  asection *sigotplt;
+  asection *siplt;
+  asection *sreliplt;
   asection *sstubs;
   asection *sgot;
 
@@ -482,6 +494,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;
 
@@ -744,6 +759,9 @@ static bfd_boolean mips_elf_create_dynamic_relocation
    bfd_vma *, asection *);
 static bfd_vma mips_elf_adjust_gp
   (bfd *, struct mips_got_info *, bfd *);
+static void
+mips_elf_allocate_ireloc
+  (struct bfd_link_info *, struct mips_elf_link_hash_entry *);
 
 /* This will be used when we sort the dynamic relocation records.  */
 static bfd *reldyn_sorting_bfd;
@@ -1160,6 +1178,32 @@ static const bfd_vma mips_vxworks_shared_plt_entry[] =
   0x10000000,	/* b .PLT_resolver	*/
   0x24180000	/* li t8, <pltindex>	*/
 };
+
+/* The format of non-pie IPLT entries.
+   In the case of mips1 the first nop will be issued before the
+   jr instuction  */
+static const bfd_vma mips32_exec_iplt_entry[] =
+{
+  0x3c0f0000,   /* lui $15, %hi(.got.iplt entry)        */
+  0x01f90000,   /* l[wd] $25, %lo(.got.iplt entry)($15) */
+  0x03200008,   /* jr $25                               */
+  0x00000000,   /* nop                                  */
+  0x00000000    /* nop                                  */
+};
+
+/* The format of non-pie 64 bit IPLT entries.  */
+static const bfd_vma mips64_exec_iplt_entry[] =
+{
+  0x3c0f0000,   /* lui $15, %highest(.got.iplt entry)        */
+  0x65ef0000,   /* daddiu $15, $15, %higher(.got.iplt entry) */
+  0x000f7c38,   /* dsll $15,$15, 16                          */
+  0x65ef0000,   /* daddiu $15, $15, %hi(.got.iplt entry)     */
+  0x000f7c38,   /* dsll $15,$15, 16                          */
+  0x01f90000,   /* l[wd] $25, %lo(.got.iplt entry)($15)      */
+  0x03200008,   /* jr $25                                    */
+  0x00000000,   /* nop                                       */
+};
+
 
 /* microMIPS 32-bit opcode helper installer.  */
 
@@ -1258,6 +1302,9 @@ 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->iplt_offset = -1;
+      ret->igot_offset = 0;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -1935,6 +1982,40 @@ mips_elf_add_la25_stub (struct bfd_link_info *info,
 	  : mips_elf_add_la25_intro (stub, info));
 }
 
+/* Reserve space in the iplt and igot tables for another ifunc entry.
+   Don't do anything if this is a dso link.  */
+static bfd_boolean
+mips_elf_allocate_iplt (bfd *abfd, struct mips_elf_link_hash_table *mhtab,
+			struct bfd_link_info *info,
+			struct mips_elf_link_hash_entry *mh)
+{
+
+  asection *s;
+
+  if (mh->needs_iplt || !info->executable)
+    return TRUE;
+
+  BFD_ASSERT (mhtab->siplt != NULL);
+
+  s = mhtab->siplt;
+  mh->iplt_offset = s->size;
+  s->size += mhtab->iplt_entry_size;
+
+  /* Create a symbol for the stub.  */
+  mips_elf_create_stub_symbol (info, mh, ".iplt.",s, mh->iplt_offset,
+			       mhtab->iplt_entry_size);
+
+  BFD_ASSERT (mhtab->sigotplt != NULL);
+  mh->igot_offset = mhtab->sigotplt->size;
+  mhtab->sigotplt->size += MIPS_ELF_GOT_SIZE (abfd);
+  mips_elf_allocate_ireloc (info, mh);
+
+/* This should be the only place needs_iplt is set */
+  mh->needs_iplt = TRUE;
+
+  return TRUE;
+}
+
 /* A mips_elf_link_hash_traverse callback that is called before sizing
    sections.  DATA points to a mips_htab_traverse_info structure.  */
 
@@ -1947,6 +2028,15 @@ 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 the referenced symbol is ifunc, allocate an iplt stub for it.  */
+  if (h && !h->needs_iplt && h->root.type == STT_GNU_IFUNC)
+    {
+      struct bfd_link_info *info = hti->info;
+      if (!(mips_elf_allocate_iplt (info->output_bfd,
+				    mips_elf_hash_table (info), info, h)))
+	return FALSE;
+    }
+
   if (mips_elf_local_pic_function_p (h))
     {
       /* PR 12845: If H is in a section that has been garbage
@@ -4998,6 +5088,74 @@ mips_elf_create_compact_rel_section
   return TRUE;
 }
 
+/* Create the .iplt, .rel(a).iplt and .igot.plt sections.  */
+
+static bfd_boolean
+mips_elf_create_ifunc_sections (bfd *abfd, 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;
+
+  /* Don't do anything if a dso link.  */
+  if (!info->executable)
+    return TRUE;
+
+  htab = mips_elf_hash_table (info);
+  dynobj = (htab->root.dynobj) ? htab->root.dynobj : abfd;
+  bed = get_elf_backend_data (dynobj);
+  flags = bed->dynamic_sec_flags;
+
+  if (htab->root.iplt == NULL)
+    {
+      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;
+      htab->siplt = s;
+      if (!info->shared)
+	if (ABI_64_P (abfd))
+	  htab->iplt_entry_size = 4 * ARRAY_SIZE (mips64_exec_iplt_entry);
+	else
+	  htab->iplt_entry_size = 4 * ARRAY_SIZE (mips32_exec_iplt_entry);
+      else
+	{
+	  ;
+	}/* FIXME: None of the ifunc related sections should be built */
+    }
+
+  if (htab->root.igotplt == NULL )
+    {
+      s = bfd_make_section_anyway_with_flags (dynobj, ".igot.plt", flags);
+      if (s == NULL
+	  || !bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
+	return FALSE;
+      htab->root.igotplt = s;
+      htab->sigotplt = s;
+      mips_elf_section_data (s)->elf.this_hdr.sh_flags |= SHF_ALLOC |
+							  SHF_WRITE;
+    }
+
+  if (htab->sreliplt == NULL )
+    {
+      s = bfd_make_section_with_flags (dynobj,
+				       ".rel.iplt", /* This is magic currently */
+				       flags | SEC_READONLY);
+      if (s == NULL
+	  || !bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
+	return FALSE;
+      htab->sreliplt = s;
+      mips_elf_section_data (s)->elf.this_hdr.sh_entsize =
+	MIPS_ELF_REL_SIZE (dynobj);
+    }
+
+  return TRUE;
+}
+
 /* Create the .got section to hold the global offset table.  */
 
 static bfd_boolean
@@ -5466,6 +5624,14 @@ 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->siplt != NULL);
+      symbol = (htab->siplt->output_section->vma
+		+ htab->siplt->output_offset
+		+ h->iplt_offset);
+    }
 
   /* Make sure MIPS16 and microMIPS are not used together.  */
   if ((r_type == R_MIPS16_26 && target_is_micromips_code_p)
@@ -6829,6 +6995,8 @@ _bfd_mips_elf_section_processing (bfd *abfd, Elf_Internal_Shdr *hdr)
 		hdr->sh_size += hdr->sh_addralign - adjust;
 	    }
 	}
+      else if (strcmp (name, ".igot.plt") == 0)
+	hdr->sh_entsize =  ABI_64_P (abfd) ? 8 : 4;
     }
 
   return TRUE;
@@ -7167,6 +7335,10 @@ _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)
@@ -7680,6 +7852,29 @@ mips_elf_make_plt_record (bfd *abfd)
   return entry;
 }
 
+/* Reserve space for R_MIPS_IRELATIVE relocations.  If the link is
+   dynamic, the relocations should go in SRELOC, otherwise they should
+   go in the special .rel.iplt section.  */
+
+static void
+mips_elf_allocate_ireloc (struct bfd_link_info *info,
+			struct mips_elf_link_hash_entry *hmips ATTRIBUTE_UNUSED)
+{
+  struct mips_elf_link_hash_table *htab;
+  asection *srel = NULL;
+  bfd *dynobj = elf_hash_table (info)->dynobj;
+  BFD_ASSERT (dynobj != NULL);
+
+  htab = mips_elf_hash_table (info);
+  srel = (!elf_hash_table (info)->dynamic_sections_created)
+	  ? htab->sreliplt
+	  : mips_elf_rel_dyn_section (info, FALSE);
+  BFD_ASSERT (srel != NULL);
+
+  srel->size += ((htab->is_vxworks) ? MIPS_ELF_RELA_SIZE (dynobj)
+				    : MIPS_ELF_REL_SIZE (dynobj));
+}
+
 /* Look through the relocs for a section during the first phase, and
    allocate space in the global offset table and record the need for
    standard MIPS and compressed procedure linkage table entries.  */
@@ -7716,6 +7911,14 @@ _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->siplt &&
+      !mips_elf_create_ifunc_sections (htab->root.dynobj, info))
+    return FALSE;
+
   /* Check for the mips16 stub sections.  */
 
   name = bfd_get_section_name (abfd, sec);
@@ -8217,10 +8420,14 @@ _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.  */
-	      h->needs_plt = 1;
-	      h->type = STT_FUNC;
-	    }
+		 _bfd_elf_adjust_dynamic_symbol.  If it is an ifunc
+		 symbol it will go into an iplt section and not plt.  */
+	      if (h->type != STT_GNU_IFUNC)
+		{
+		  h->needs_plt = 1;
+		  h->type = STT_FUNC;
+		}
+	      }
 	  break;
 
 	case R_MIPS_GOT_PAGE:
@@ -8669,6 +8876,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   dynobj = elf_hash_table (info)->dynobj;
   hmips = (struct mips_elf_link_hash_entry *) h;
 
+  /* Record any ifunc symbols */
+  if (h && hmips->needs_iplt)
+    {
+      mips_elf_allocate_ireloc (info, hmips);
+      return TRUE;
+    }
+
   /* VxWorks executables are handled elsewhere; we only need to
      allocate relocations in shared objects.  */
   if (htab->is_vxworks && !info->shared)
@@ -8758,6 +8972,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
@@ -9518,6 +9733,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)
 	{
@@ -10244,6 +10461,134 @@ mips_elf_irix6_finish_dynamic_symbol (bfd *abfd ATTRIBUTE_UNUSED,
 	}
 }
 
+/* Create the contents of a iplt entry for the ifunc symbol.  */
+
+static bfd_boolean
+mips_elf_create_iplt (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)
+{
+  /* We've decided to create an IPLT entry for this symbol.  */
+  bfd_byte *loc;
+  bfd_vma igot_index = 0;
+  bfd_vma igotplt_address = 0;
+  bfd_vma load;
+  const bfd_vma *iplt_entry;
+  asection *gotsect, *relsect;
+
+  gotsect = htab->sigotplt;
+  igot_index = hmips->igot_offset/MIPS_ELF_GOT_SIZE (dynobj);
+
+  relsect = (!elf_hash_table (info)->dynamic_sections_created)
+		  ? htab->sreliplt
+		  : mips_elf_rel_dyn_section (info, FALSE);
+
+  /* Calculate the address of the .igot.plt entry.  */
+  igotplt_address = (gotsect->output_section->vma +
+		     gotsect->output_offset +
+		     hmips->igot_offset);
+
+  /* Initially point the .got.iplt entry at the user ifunc routine.  */
+  if (!gotsect->contents)
+    gotsect->contents = bfd_zalloc(output_bfd,gotsect->size);
+
+  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);
+
+  /* Find out where the .iplt entry should go.  */
+  if (!htab->siplt->contents)
+    htab->siplt->contents = bfd_zalloc (output_bfd,htab->siplt->size);
+  loc = htab->siplt->contents + hmips->iplt_offset;
+
+  /* Pick the load opcode.  */
+  load = MIPS_ELF_LOAD_WORD (output_bfd);
+
+  /* Fill in the IPLT entry itself.  */
+  if (!info->shared)
+    {
+      if (ABI_64_P (output_bfd) && igotplt_address > 0xffffffffffffL)
+	{
+	  /* 64 bit */
+	  bfd_vma highest = mips_elf_highest (igotplt_address);
+	  bfd_vma higher = mips_elf_higher (igotplt_address);
+	  bfd_vma high = mips_elf_high (igotplt_address);
+	  bfd_vma low = igotplt_address & 0xffff;
+
+	  iplt_entry = mips64_exec_iplt_entry;
+	  bfd_put_32 (output_bfd, iplt_entry[0] | highest, loc);
+	  bfd_put_32 (output_bfd, iplt_entry[1] | higher, loc + 4);
+	  bfd_put_32 (output_bfd, iplt_entry[2], loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[3] | high , loc + 12);
+	  bfd_put_32 (output_bfd, iplt_entry[4], loc + 16);
+	  bfd_put_32 (output_bfd, iplt_entry[5] | low | load, loc + 20);
+	  bfd_put_32 (output_bfd, iplt_entry[6], loc + 24);
+	  bfd_put_32 (output_bfd, iplt_entry[7], loc + 28);
+	}
+      else if (ABI_64_P (output_bfd) && igotplt_address > 0xffffffffL)
+	{
+	  /* 48 bit */
+	  bfd_vma higher = mips_elf_higher (igotplt_address);
+	  bfd_vma high = mips_elf_high (igotplt_address);
+	  bfd_vma low = igotplt_address & 0xffff;
+
+	  iplt_entry = mips64_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[5] | low | load, loc + 12);
+	  bfd_put_32 (output_bfd, iplt_entry[6], loc + 16);
+	  bfd_put_32 (output_bfd, iplt_entry[7], loc + 20);
+	}
+      else
+	{
+	  /* 32 bit */
+	  bfd_vma high = mips_elf_high (igotplt_address);
+	  bfd_vma low = igotplt_address & 0xffff;
+
+	  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 | load, 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);
+	    }
+	}
+    }
+
+  /* Emit an R_MIPS_IRELATIVE relocation against the igot entry.  */
+    if (relsect->contents == NULL)
+      {
+	/* Allocate memory for the relocation section contents.  */
+	relsect->contents = bfd_zalloc (dynobj, relsect->size);
+	if (relsect->contents == NULL)
+	  {
+	    bfd_set_error (bfd_error_no_memory);
+	    return FALSE;
+	  }
+      }
+    mips_elf_output_dynamic_relocation (output_bfd, relsect,
+					relsect->reloc_count++,
+					0 /* sym_indx */,
+					R_MIPS_IRELATIVE, igotplt_address);
+
+  return TRUE;
+}
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -10568,6 +10913,17 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
       sym->st_other = other;
     }
 
+  /* IFUNC symbols get an iplt stub  */
+  if (hmips->needs_iplt)
+    {
+      if (!(mips_elf_create_iplt (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.  */
@@ -10731,6 +11087,15 @@ _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->siplt->output_section->vma +
+		      htab->siplt->output_offset +
+		      hmips->iplt_offset;
+      sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_FUNC);
+    }
+
   return TRUE;
 }
 
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 4f98108..7acc620 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -1194,6 +1194,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
 
   "BFD_RELOC_MOXIE_10_PCREL",
 
+  "BFD_RELOC_MIPS_IRELATIVE",
   "BFD_RELOC_FRV_LABEL16",
   "BFD_RELOC_FRV_LABEL24",
   "BFD_RELOC_FRV_LO16",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 8778e1d..dd564f0 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2434,6 +2434,11 @@ ENUMDOC
 COMMENT
 
 ENUM
+  BFD_RELOC_MIPS_IRELATIVE
+ENUMDOC
+  MIPS support for STT_GNU_IFUNC.
+
+ENUM
   BFD_RELOC_FRV_LABEL16
 ENUMX
   BFD_RELOC_FRV_LABEL24
diff --git a/include/elf/mips.h b/include/elf/mips.h
index 2c5a9a6..2cfd003 100644
--- a/include/elf/mips.h
+++ b/include/elf/mips.h
@@ -110,6 +110,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)
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..702d441
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n32.r
@@ -0,0 +1,4 @@
+
+Relocation section '.rel.dyn' at offset 0x12000 contains 1 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+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..c586de3
--- /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  13 __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..acabe49
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n32.t
@@ -0,0 +1,11 @@
+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
+ 410:	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..8b6e5ba
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-n64.r
@@ -0,0 +1,5 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 1 entries:
+  Offset          Info           Type           Sym. Value    Sym. Name
+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..a85c426
--- /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  13 __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..702d441
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-3-o32.r
@@ -0,0 +1,4 @@
+
+Relocation section '.rel.dyn' at offset 0x12000 contains 1 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+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..94d3db6
--- /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  13 __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..9259df6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00080800  00000080 R_MIPS_IRELATIVE 
+00000000  00000000 R_MIPS_NONE      
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..cbad3aa
--- /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: 00081010     0 SECTION LOCAL  DEFAULT    5 
+     6: 00000000     0 SECTION LOCAL  DEFAULT    6 
+     7: 00000000     0 SECTION LOCAL  DEFAULT    7 
+     8: 00000000     0 SECTION LOCAL  DEFAULT    8 
+     9: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref_main_1.c
+    10: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc.c
+    11: 00080430    36 FUNC    LOCAL  DEFAULT    2 f1_a
+    12: 00080454    36 FUNC    LOCAL  DEFAULT    2 f1_b
+    13: 00080478    36 FUNC    LOCAL  DEFAULT    2 f1_c
+    14: 000804d0     0 NOTYPE  LOCAL  DEFAULT    2 \$L8
+    15: 000804d8     0 NOTYPE  LOCAL  DEFAULT    2 \$L9
+    16: 000804f8     0 NOTYPE  LOCAL  DEFAULT    2 \$L10
+    17: 00080520     0 NOTYPE  LOCAL  DEFAULT    2 \$L13
+    18: 00080518     0 NOTYPE  LOCAL  DEFAULT    2 \$L12
+    19: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref.c
+    20: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
+    21: 00080000    20 FUNC    LOCAL  DEFAULT    1 .iplt.func1
+    22: 0008049c   156 IFUNC   GLOBAL DEFAULT    2 func1
+    23: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND _start
+    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..5daf80f
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n32.t
@@ -0,0 +1,11 @@
+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
+   80010:	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..5c736a0
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-n64.r
@@ -0,0 +1,8 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 2 entries:
+  Offset          Info           Type           Sym. Value    Sym. Name
+000000080800  000000000080 R_MIPS_IRELATIVE 
+                    Type2: R_MIPS_NONE      
+                    Type3: R_MIPS_NONE      
+000000000000  000000000000 R_MIPS_NONE      
+                    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..b4b950e
--- /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: 0000000000081020     0 SECTION LOCAL  DEFAULT    5 
+     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
+     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
+     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
+     9: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref_main_1.c
+    10: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS ifunc.c
+    11: 0000000000080430    36 FUNC    LOCAL  DEFAULT    2 f1_a
+    12: 0000000000080454    36 FUNC    LOCAL  DEFAULT    2 f1_b
+    13: 0000000000080478    36 FUNC    LOCAL  DEFAULT    2 f1_c
+    14: 00000000000804d0     0 NOTYPE  LOCAL  DEFAULT    2 \$L8
+    15: 00000000000804d8     0 NOTYPE  LOCAL  DEFAULT    2 \$L9
+    16: 00000000000804f8     0 NOTYPE  LOCAL  DEFAULT    2 \$L10
+    17: 0000000000080520     0 NOTYPE  LOCAL  DEFAULT    2 \$L13
+    18: 0000000000080518     0 NOTYPE  LOCAL  DEFAULT    2 \$L12
+    19: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref.c
+    20: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
+    21: 0000000000080000    32 FUNC    LOCAL  DEFAULT    1 .iplt.func1
+    22: 000000000008049c   156 IFUNC   GLOBAL DEFAULT    2 func1
+    23: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _start
+    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..9259df6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-4-o32.r
@@ -0,0 +1,4 @@
+Relocation section '.rel.dyn' at offset 0x11000 contains 2 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00080800  00000080 R_MIPS_IRELATIVE 
+00000000  00000000 R_MIPS_NONE      
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..e88cc29
--- /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: 00081010     0 SECTION LOCAL  DEFAULT    5 
+     6: 00000000     0 SECTION LOCAL  DEFAULT    6 
+     7: 00000000     0 SECTION LOCAL  DEFAULT    7 
+     8: 00000000     0 SECTION LOCAL  DEFAULT    8 
+     9: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref_main_1.c
+    10: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc.c
+    11: 00080430    36 FUNC    LOCAL  DEFAULT    2 f1_a
+    12: 00080454    36 FUNC    LOCAL  DEFAULT    2 f1_b
+    13: 00080478    36 FUNC    LOCAL  DEFAULT    2 f1_c
+    14: 00000000     0 FILE    LOCAL  DEFAULT  ABS ifunc_ref.c
+    15: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
+    16: 00080000    20 FUNC    LOCAL  DEFAULT    1 .iplt.func1
+    17: 0008049c   156 IFUNC   GLOBAL DEFAULT    2 func1
+    18: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND _start
+    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.g b/ld/testsuite/ld-mips-elf/ifunc-5-n32.g
new file mode 100644
index 0000000..e69de29
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..64d10f6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-n32.r
@@ -0,0 +1 @@
+There are no relocations in this file.
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.g b/ld/testsuite/ld-mips-elf/ifunc-5-n64.g
new file mode 100644
index 0000000..e69de29
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..64d10f6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-n64.r
@@ -0,0 +1 @@
+There are no relocations in this file.
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.g b/ld/testsuite/ld-mips-elf/ifunc-5-o32.g
new file mode 100644
index 0000000..e69de29
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..64d10f6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-5-o32.r
@@ -0,0 +1 @@
+There are no relocations in this file.
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..e19667e
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-n32.r
@@ -0,0 +1,3 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 1 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+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..6b930fc
--- /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  11 __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..8b6e5ba
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-n64.r
@@ -0,0 +1,5 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 1 entries:
+  Offset          Info           Type           Sym. Value    Sym. Name
+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..11e8f97
--- /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  11 __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..e19667e
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/ifunc-6-o32.r
@@ -0,0 +1,3 @@
+Relocation section '.rel.dyn' at offset 0x12000 contains 1 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+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..8cb795d
--- /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  11 __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..ea79ec8
--- /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.plt : { *(.igot.plt) }
+
+  . = 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..57f60b8
--- /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:
+
+0000000000400148 <.iplt.func1>:
+  400148:	3c0f0041 	lui	t3,0x41
+  40014c:	ddf902e0 	ld	t9,736\(t3\)
+  400150:	03200008 	jr	t9
+  400154:	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..a0e9aaf
--- /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:
+
+0000000400000148 <.iplt.func1>:
+   400000148:	3c0f0004 	lui	t3,0x4
+   40000014c:	65ef0001 	daddiu	t3,t3,1
+   400000150:	000f7c38 	dsll	t3,t3,0x10
+   400000154:	ddf902e0 	ld	t9,736\(t3\)
+   400000158:	03200008 	jr	t9
+   40000015c:	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..e559746
--- /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:
+
+0004000000000148 <.iplt.func1>:
+   4000000000148:	3c0f0004 	lui	t3,0x4
+   400000000014c:	65ef0000 	daddiu	t3,t3,0
+   4000000000150:	000f7c38 	dsll	t3,t3,0x10
+   4000000000154:	65ef0001 	daddiu	t3,t3,1
+   4000000000158:	000f7c38 	dsll	t3,t3,0x10
+   400000000015c:	ddf902e0 	ld	t9,736\(t3\)
+   4000000000160:	03200008 	jr	t9
+   4000000000164:	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..c013c77
--- /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.plt : { *(.igot.plt) }
+
+  . = 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..0579e69
--- /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.plt : { *(.igot.plt) }
+
+  . = 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-elf.exp b/ld/testsuite/ld-mips-elf/mips-elf.exp
index a66a289..9c7dfa4 100644
--- a/ld/testsuite/ld-mips-elf/mips-elf.exp
+++ b/ld/testsuite/ld-mips-elf/mips-elf.exp
@@ -837,3 +837,4 @@ run_dump_test "attr-gnu-8-12"
 run_dump_test "attr-gnu-8-20"
 run_dump_test "attr-gnu-8-21"
 run_dump_test "attr-gnu-8-22"
+
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..c346f0b
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/mips-ifunc.exp
@@ -0,0 +1,151 @@
+# 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}" \
+        ] \
+    ]
+}
+
+
+

Attachment: ifunc_abi.txt
Description: ifunc_abi.txt


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