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]: Sparc STT_GNU_IFUNC support


I have a GLIBC implementation as well which I am still refining
but the current patch is at:

	http://vger.kernel.org/~davem/sparc_glibc_ifunc.diff

All ifunc test cases in binutils pass with these changes.

All ifunc test cases in glibc pass except ifuncmain6pie, fixing that
will require adding GOTDATA_OP support to gcc and binutils which I do
plan to work on.

I fully tested both 64-bit and 32-bit.

ifuncmain6pie fails because it is a case where a shared library has a
relocation to an ifunc symbol, which is satisfied by a PIE executable.

The IFUNC resolving function in the PIE executable generates
relocations for the local symbol references to return the selected
function pointer.  ie. something like:

static int one(void)
{
  return 1;
}

void *foo_ifunc(void)
{
  return &one;
}

results in an R_SPARC_RELATIVE relocation against the GOT
for "one".

So depending upon the order in which the dynamic linker processes
relocations, the IFUNC function might get called before the
relocations within it are resolved.

i386 does not have this problem because it uses @GOTOFF for local
symbols referenced in PIC code.  Sparc's GOTDATA_OP will work
just like this, allowing us to do local symbol references in
PIC code without any dynamic relocations.

I think powerpc64 will hit this failure too, because what should
happen is we end up with a TOC for the PIC code, and a bunch
of R_POWERPC_RELATIVE relocations.  Alan, does ifuncmain6pie
pass on powerpc64?

Implementation wise I took the same approach H. J. Liu took on i386,
using a seperate hash table for handling local IFUNC symbols.
Everything, including emitting PLT entries, essentially "just works"
by running allocate_dynrelocs() over this new local hash table.

This depends upon "[PATCH]: Sparc BFD cleanups and fixes."

Ok to commit?

include/

2010-02-05  David S. Miller  <davem@davemloft.net>

	* elf/sparc.h (R_SPARC_JMP_IREL, R_SPARC_IRELATIVE): Define.

bfd/

2010-02-05  David S. Miller  <davem@davemloft.net>

	* reloc.c (BFD_RELOC_SPARC_JMP_IREL): New.
	(BFD_RELOC_SPARC_IRELATIVE): Likewise.
	* bfd-in2.h: Regenerate.
	* libbfd.h: Regenerate.

	* elfxx-sparc.h (_bfd_sparc_elf_link_hash_table): Add loc_hash_table
	and loc_hash_memory.
	(_bfd_sparc_elf_link_hash_table_free): Declare.
	* elf32-sparc.c (elf32_sparc_add_symbol_hook): New.
	(elf_backend_add_symbol_hook, elf_backend_post_process_headers,
	bfd_elf32_bfd_link_hash_table_free): Define.
	* elf64-sparc.c (elf64_sparc_add_symbol_hook): Set
	has_ifunc_symbols if STT_GNU_IFUNC.
	(bfd_elf64_bfd_link_hash_table_free): Define.
	(elf_backend_post_process_headers): Define always.
	* elfxx-sparc.c (sparc_jmp_irel_howto, sparc_irelative_howto): New.
	(sparc_reloc_map): Add entries for new IFUNC relocs.
	(_bfd_sparc_elf_reloc_type_lookup): Handle new IFUNC relocs.
	(_bfd_sparc_elf_info_to_howto_ptr): Likewise.
	(elf_sparc_local_htab_hash, elf_sparc_local_htab_eq,
	elf_sparc_get_local_sym_hash): New.
	(_bfd_sparc_elf_create_dynamic_sections): Move PLT ops initialization
	from here...
	(_bfd_sparc_elf_link_hash_table_create): ... to here.  Allocate
	local hash table.
	(_bfd_sparc_elf_link_hash_table_free): New.
	(create_ifunc_sections): New.
	(_bfd_sparc_elf_check_relocs): Unconditionally assign htab->elf.dynobj
	and call create_ifunc_sections().  For local STT_GNU_IFUNC symbols
	cons up a fake local hash table entry for it.  Unconditionally add
	a PLT refcount for STT_GNU_IFUNC symbols when h->def_regular.  Count
	dyn relocs for ifunc.
	(_bfd_sparc_elf_adjust_dynamic_symbol): Handle ifunc.
	(allocate_dynrelocs):  Unconditionally emit a PLT entry when STT_GNU_IFUNC
	and h->def_regular.  Count GOT dyn relocs for ifunc.
	(allocate_local_dynrelocs): New function.
	(_bfd_sparc_elf_size_dynamic_sections): Invoke it over the local hash table.
	Emit dynamic relocs to irelplt when not shared.  Treat iplt like splt.
	(_bfd_sparc_elf_relocate_section): Handle ifunc relocations by hand.
	(_bfd_sparc_elf_finish_dynamic_symbol): Adjust for non-dynamic ifunc plt
	in iplt/irelplt.

ld/testsuite/

2010-02-05  David S. Miller  <davem@davemloft.net>

	* ld-ifunc/ifunc.exp: Run for sparc.

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 8d9a464..8fa723d 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2503,6 +2503,8 @@ relocation types already defined.  */
   BFD_RELOC_SPARC_GOTDATA_OP_HIX22,
   BFD_RELOC_SPARC_GOTDATA_OP_LOX10,
   BFD_RELOC_SPARC_GOTDATA_OP,
+  BFD_RELOC_SPARC_JMP_IREL,
+  BFD_RELOC_SPARC_IRELATIVE,
 
 /* I think these are specific to SPARC a.out (e.g., Sun 4).  */
   BFD_RELOC_SPARC_BASE13,
diff --git a/bfd/elf32-sparc.c b/bfd/elf32-sparc.c
index ae0ac2b..ab4f703 100644
--- a/bfd/elf32-sparc.c
+++ b/bfd/elf32-sparc.c
@@ -167,6 +167,23 @@ elf32_sparc_reloc_type_class (const Elf_Internal_Rela *rela)
     }
 }
 
+/* Hook called by the linker routine which adds symbols from an object
+   file.  */
+
+static bfd_boolean
+elf32_sparc_add_symbol_hook (bfd * abfd ATTRIBUTE_UNUSED,
+			     struct bfd_link_info * info ATTRIBUTE_UNUSED,
+			     Elf_Internal_Sym * sym,
+			     const char ** namep ATTRIBUTE_UNUSED,
+			     flagword * flagsp ATTRIBUTE_UNUSED,
+			     asection ** secp ATTRIBUTE_UNUSED,
+			     bfd_vma * valp ATTRIBUTE_UNUSED)
+{
+  if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+    elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+  return TRUE;
+}
+
 #define TARGET_BIG_SYM	bfd_elf32_sparc_vec
 #define TARGET_BIG_NAME	"elf32-sparc"
 #define ELF_ARCH	bfd_arch_sparc
@@ -188,6 +205,8 @@ elf32_sparc_reloc_type_class (const Elf_Internal_Rela *rela)
   _bfd_sparc_elf_reloc_name_lookup
 #define bfd_elf32_bfd_link_hash_table_create \
 					_bfd_sparc_elf_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_free \
+					_bfd_sparc_elf_link_hash_table_free
 #define bfd_elf32_bfd_relax_section	_bfd_sparc_elf_relax_section
 #define bfd_elf32_new_section_hook	_bfd_sparc_elf_new_section_hook
 #define elf_backend_copy_indirect_symbol \
@@ -220,6 +239,9 @@ elf32_sparc_reloc_type_class (const Elf_Internal_Rela *rela)
 #define elf_backend_got_header_size 4
 #define elf_backend_rela_normal 1
 
+#define elf_backend_post_process_headers	_bfd_elf_set_osabi
+#define elf_backend_add_symbol_hook		elf32_sparc_add_symbol_hook
+
 #include "elf32-target.h"
 
 /* A wrapper around _bfd_sparc_elf_link_hash_table_create that identifies
diff --git a/bfd/elf64-sparc.c b/bfd/elf64-sparc.c
index 18bdfb4..a1bde20 100644
--- a/bfd/elf64-sparc.c
+++ b/bfd/elf64-sparc.c
@@ -424,6 +424,9 @@ elf64_sparc_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
 {
   static const char *const stt_types[] = { "NOTYPE", "OBJECT", "FUNCTION" };
 
+  if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+    elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+
   if (ELF_ST_TYPE (sym->st_info) == STT_REGISTER)
     {
       int reg;
@@ -856,6 +859,8 @@ const struct elf_size_info elf64_sparc_size_info =
   _bfd_sparc_elf_plt_sym_val
 #define bfd_elf64_bfd_link_hash_table_create \
   _bfd_sparc_elf_link_hash_table_create
+#define bfd_elf64_bfd_link_hash_table_free \
+  _bfd_sparc_elf_link_hash_table_free
 #define elf_info_to_howto \
   _bfd_sparc_elf_info_to_howto
 #define elf_backend_copy_indirect_symbol \
@@ -910,6 +915,8 @@ const struct elf_size_info elf64_sparc_size_info =
 /* Section 5.2.4 of the ABI specifies a 256-byte boundary for the table.  */
 #define elf_backend_plt_alignment 8
 
+#define elf_backend_post_process_headers	_bfd_elf_set_osabi
+
 #include "elf64-target.h"
 
 /* FreeBSD support */
@@ -920,8 +927,6 @@ const struct elf_size_info elf64_sparc_size_info =
 #undef	ELF_OSABI
 #define	ELF_OSABI ELFOSABI_FREEBSD
 
-#undef  elf_backend_post_process_headers
-#define elf_backend_post_process_headers	_bfd_elf_set_osabi
 #undef  elf64_bed
 #define elf64_bed				elf64_sparc_fbsd_bed
 
diff --git a/bfd/elfxx-sparc.c b/bfd/elfxx-sparc.c
index e0d125f..bc36920 100644
--- a/bfd/elfxx-sparc.c
+++ b/bfd/elfxx-sparc.c
@@ -31,6 +31,8 @@
 #include "opcode/sparc.h"
 #include "elfxx-sparc.h"
 #include "elf-vxworks.h"
+#include "objalloc.h"
+#include "hashtab.h"
 
 /* In case we're on a 32-bit machine, construct a 64-bit "-1" value.  */
 #define MINUS_ONE (~ (bfd_vma) 0)
@@ -265,6 +267,10 @@ static reloc_howto_type _bfd_sparc_elf_howto_table[] =
   HOWTO(R_SPARC_GOTDATA_OP_LOX10,0,2,0,FALSE,0,complain_overflow_dont,  sparc_elf_lox10_reloc,  "R_SPARC_GOTDATA_OP_LOX10",FALSE,0,0x000003ff, FALSE),
   HOWTO(R_SPARC_GOTDATA_OP,0,0, 0,FALSE,0,complain_overflow_dont,   bfd_elf_generic_reloc,  "R_SPARC_GOTDATA_OP",FALSE,0,0x00000000,TRUE),
 };
+static reloc_howto_type sparc_jmp_irel_howto =
+  HOWTO(R_SPARC_JMP_IREL,  0,0,00,FALSE,0,complain_overflow_dont,    bfd_elf_generic_reloc,  "R_SPARC_JMP_IREL",FALSE,0,0x00000000,TRUE);
+static reloc_howto_type sparc_irelative_howto =
+  HOWTO(R_SPARC_IRELATIVE,  0,0,00,FALSE,0,complain_overflow_dont,    bfd_elf_generic_reloc,  "R_SPARC_IRELATIVE",FALSE,0,0x00000000,TRUE);
 static reloc_howto_type sparc_vtinherit_howto =
   HOWTO (R_SPARC_GNU_VTINHERIT, 0,2,0,FALSE,0,complain_overflow_dont, NULL, "R_SPARC_GNU_VTINHERIT", FALSE,0, 0, FALSE);
 static reloc_howto_type sparc_vtentry_howto =
@@ -360,6 +366,8 @@ static const struct elf_reloc_map sparc_reloc_map[] =
   { BFD_RELOC_SPARC_GOTDATA_OP_LOX10, R_SPARC_GOTDATA_OP_LOX10 },
   { BFD_RELOC_SPARC_GOTDATA_OP, R_SPARC_GOTDATA_OP },
   { BFD_RELOC_SPARC_REGISTER, R_SPARC_REGISTER },
+  { BFD_RELOC_SPARC_JMP_IREL, R_SPARC_JMP_IREL },
+  { BFD_RELOC_SPARC_IRELATIVE, R_SPARC_IRELATIVE },
   { BFD_RELOC_VTABLE_INHERIT, R_SPARC_GNU_VTINHERIT },
   { BFD_RELOC_VTABLE_ENTRY, R_SPARC_GNU_VTENTRY },
   { BFD_RELOC_SPARC_REV32, R_SPARC_REV32 },
@@ -373,6 +381,12 @@ _bfd_sparc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 
   switch (code)
     {
+    case BFD_RELOC_SPARC_JMP_IREL:
+      return &sparc_jmp_irel_howto;
+
+    case BFD_RELOC_SPARC_IRELATIVE:
+      return &sparc_irelative_howto;
+
     case BFD_RELOC_VTABLE_INHERIT:
       return &sparc_vtinherit_howto;
 
@@ -425,6 +439,12 @@ _bfd_sparc_elf_info_to_howto_ptr (unsigned int r_type)
 {
   switch (r_type)
     {
+    case R_SPARC_JMP_IREL:
+      return &sparc_jmp_irel_howto;
+
+    case R_SPARC_IRELATIVE:
+      return &sparc_irelative_howto;
+
     case R_SPARC_GNU_VTINHERIT:
       return &sparc_vtinherit_howto;
 
@@ -831,6 +851,78 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
 #define ELF32_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
 #define ELF64_DYNAMIC_INTERPRETER "/usr/lib/sparcv9/ld.so.1"
 
+/* 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
+elf_sparc_local_htab_hash (const void *ptr)
+{
+  struct elf_link_hash_entry *h
+    = (struct elf_link_hash_entry *) ptr;
+  return ELF_LOCAL_SYMBOL_HASH (h->indx, h->dynstr_index);
+}
+
+/* Compare local hash entries.  */
+
+static int
+elf_sparc_local_htab_eq (const void *ptr1, const void *ptr2)
+{
+  struct elf_link_hash_entry *h1
+     = (struct elf_link_hash_entry *) ptr1;
+  struct elf_link_hash_entry *h2
+    = (struct elf_link_hash_entry *) ptr2;
+
+  return h1->indx == h2->indx && h1->dynstr_index == h2->dynstr_index;
+}
+
+/* Find and/or create a hash entry for local symbol.  */
+
+static struct elf_link_hash_entry *
+elf_sparc_get_local_sym_hash (struct _bfd_sparc_elf_link_hash_table *htab,
+			      bfd *abfd, const Elf_Internal_Rela *rel,
+			      bfd_boolean create)
+{
+  struct _bfd_sparc_elf_link_hash_entry e, *ret;
+  asection *sec = abfd->sections;
+  unsigned long r_symndx;
+  hashval_t h;
+  void **slot;
+
+  r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info);
+  h = ELF_LOCAL_SYMBOL_HASH (sec->id, r_symndx);
+
+  e.elf.indx = sec->id;
+  e.elf.dynstr_index = r_symndx;
+  slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h,
+				   create ? INSERT : NO_INSERT);
+
+  if (!slot)
+    return NULL;
+
+  if (*slot)
+    {
+      ret = (struct _bfd_sparc_elf_link_hash_entry *) *slot;
+      return &ret->elf;
+    }
+
+  ret = (struct _bfd_sparc_elf_link_hash_entry *)
+	objalloc_alloc ((struct objalloc *) htab->loc_hash_memory,
+			sizeof (struct _bfd_sparc_elf_link_hash_entry));
+  if (ret)
+    {
+      memset (ret, 0, sizeof (*ret));
+      ret->elf.indx = sec->id;
+      ret->elf.dynstr_index = r_symndx;
+      ret->elf.dynindx = -1;
+      ret->elf.plt.offset = (bfd_vma) -1;
+      ret->elf.got.offset = (bfd_vma) -1;
+      *slot = ret;
+    }
+  return &ret->elf;
+}
+
 /* Create a SPARC ELF linker hash table.  */
 
 struct bfd_link_hash_table *
@@ -857,6 +949,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
       ret->bytes_per_rela = sizeof (Elf64_External_Rela);
       ret->dynamic_interpreter = ELF64_DYNAMIC_INTERPRETER;
       ret->dynamic_interpreter_size = sizeof ELF64_DYNAMIC_INTERPRETER;
+
+      ret->build_plt_entry = sparc64_plt_entry_build;
+      ret->plt_header_size = PLT64_HEADER_SIZE;
+      ret->plt_entry_size = PLT64_ENTRY_SIZE;
     }
   else
     {
@@ -872,6 +968,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
       ret->bytes_per_rela = sizeof (Elf32_External_Rela);
       ret->dynamic_interpreter = ELF32_DYNAMIC_INTERPRETER;
       ret->dynamic_interpreter_size = sizeof ELF32_DYNAMIC_INTERPRETER;
+
+      ret->build_plt_entry = sparc32_plt_entry_build;
+      ret->plt_header_size = PLT32_HEADER_SIZE;
+      ret->plt_entry_size = PLT32_ENTRY_SIZE;
     }
 
   if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc,
@@ -882,9 +982,35 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
       return NULL;
     }
 
+  ret->loc_hash_table = htab_try_create (1024,
+					 elf_sparc_local_htab_hash,
+					 elf_sparc_local_htab_eq,
+					 NULL);
+  ret->loc_hash_memory = objalloc_create ();
+  if (!ret->loc_hash_table || !ret->loc_hash_memory)
+    {
+      free (ret);
+      return NULL;
+    }
+
   return &ret->elf.root;
 }
 
+/* Destroy a SPARC ELF linker hash table.  */
+
+void
+_bfd_sparc_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
+{
+  struct _bfd_sparc_elf_link_hash_table *htab
+    = (struct _bfd_sparc_elf_link_hash_table *) 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_generic_link_hash_table_free (hash);
+}
+
 /* Create .plt, .rela.plt, .got, .rela.got, .dynbss, and
    .rela.bss sections in DYNOBJ, and set up shortcuts to them in our
    hash table.  */
@@ -924,21 +1050,6 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
 	    = 4 * ARRAY_SIZE (sparc_vxworks_exec_plt_entry);
 	}
     }
-  else
-    {
-      if (ABI_64_P (dynobj))
-	{
-	  htab->build_plt_entry = sparc64_plt_entry_build;
-	  htab->plt_header_size = PLT64_HEADER_SIZE;
-	  htab->plt_entry_size = PLT64_ENTRY_SIZE;
-	}
-      else
-	{
-	  htab->build_plt_entry = sparc32_plt_entry_build;
-	  htab->plt_header_size = PLT32_HEADER_SIZE;
-	  htab->plt_entry_size = PLT32_ENTRY_SIZE;
-	}
-    }
 
   if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
       || (!info->shared && !htab->srelbss))
@@ -947,6 +1058,37 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
   return TRUE;
 }
 
+static bfd_boolean
+create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+  flagword flags, pltflags;
+  asection *s;
+
+  if (htab->irelifunc != NULL || htab->iplt != NULL)
+    return TRUE;
+
+  flags = bed->dynamic_sec_flags;
+  pltflags = flags | SEC_ALLOC | SEC_CODE | SEC_LOAD;
+
+  s = bfd_make_section_with_flags (abfd, ".iplt", pltflags);
+  if (s == NULL
+      || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
+    return FALSE;
+  htab->iplt = s;
+
+  s = bfd_make_section_with_flags (abfd, ".rela.iplt",
+				   flags | SEC_READONLY);
+  if (s == NULL
+      || ! bfd_set_section_alignment (abfd, s,
+				      bed->s->log_file_align))
+    return FALSE;
+  htab->irelplt = s;
+
+  return TRUE;
+}
+
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 void
@@ -1074,12 +1216,18 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
   BFD_ASSERT (is_sparc_elf (abfd) || num_relocs == 0);
 
+  if (htab->elf.dynobj == NULL)
+    htab->elf.dynobj = abfd;
+  if (!create_ifunc_sections (htab->elf.dynobj, info))
+    return FALSE;
+
   rel_end = relocs + num_relocs;
   for (rel = relocs; rel < rel_end; rel++)
     {
       unsigned int r_type;
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
+      Elf_Internal_Sym *isym;
 
       r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info);
       r_type = SPARC_ELF_R_TYPE (rel->r_info);
@@ -1091,8 +1239,33 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  return FALSE;
 	}
 
+      isym = NULL;
       if (r_symndx < symtab_hdr->sh_info)
-	h = NULL;
+	{
+	  /* A local symbol.  */
+	  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+					abfd, r_symndx);
+	  if (isym == NULL)
+	    return FALSE;
+
+	  /* Check relocation against local STT_GNU_IFUNC symbol.  */
+	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+	    {
+	      h = elf_sparc_get_local_sym_hash (htab, abfd, rel,
+						TRUE);
+	      if (h == NULL)
+		return FALSE;
+	      
+	      /* Fake a STT_GNU_IFUNC symbol.  */
+	      h->type = STT_GNU_IFUNC;
+	      h->def_regular = 1;
+	      h->ref_regular = 1;
+	      h->forced_local = 1;
+	      h->root.type = bfd_link_hash_defined;
+	    }
+	  else
+	    h = NULL;
+	}
       else
 	{
 	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
@@ -1101,6 +1274,12 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 	}
 
+      if (h && h->type == STT_GNU_IFUNC)
+	{
+	  if (h->def_regular)
+	    h->plt.refcount += 1;
+	}
+
       /* Compatibility with old R_SPARC_REV32 reloc conflicting
 	 with R_SPARC_TLS_GD_HI22.  */
       if (! ABI_64_P (abfd) && ! checked_tlsgd)
@@ -1239,8 +1418,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	  if (htab->elf.sgot == NULL)
 	    {
-	      if (htab->elf.dynobj == NULL)
-		htab->elf.dynobj = abfd;
 	      if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
 		return FALSE;
 	    }
@@ -1403,7 +1580,10 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		  && (sec->flags & SEC_ALLOC) != 0
 		  && h != NULL
 		  && (h->root.type == bfd_link_hash_defweak
-		      || !h->def_regular)))
+		      || !h->def_regular))
+	      || (!info->shared
+		  && h != NULL
+		  && h->type == STT_GNU_IFUNC))
 	    {
 	      struct _bfd_sparc_elf_dyn_relocs *p;
 	      struct _bfd_sparc_elf_dyn_relocs **head;
@@ -1413,9 +1593,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		 section in dynobj and make room for the reloc.  */
 	      if (sreloc == NULL)
 		{
-		  if (htab->elf.dynobj == NULL)
-		    htab->elf.dynobj = abfd;
-
 		  sreloc = _bfd_elf_make_dynamic_reloc_section
 		    (sec, htab->elf.dynobj, htab->word_align_power,
 		     abfd, /*rela?*/ TRUE);
@@ -1435,13 +1612,8 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		     easily.  Oh well.  */
 		  asection *s;
 		  void *vpp;
-		  Elf_Internal_Sym *isym;
-
-		  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-						abfd, r_symndx);
-		  if (isym == NULL)
-		    return FALSE;
 
+		  BFD_ASSERT (isym != NULL);
 		  s = bfd_section_from_elf_index (abfd, isym->st_shndx);
 		  if (s == NULL)
 		    s = sec;
@@ -1684,6 +1856,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* Make sure we know what is going on here.  */
   BFD_ASSERT (htab->elf.dynobj != NULL
 	      && (h->needs_plt
+		  || h->type == STT_GNU_IFUNC
 		  || h->u.weakdef != NULL
 		  || (h->def_dynamic
 		      && h->ref_regular
@@ -1697,6 +1870,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      some of their functions as STT_NOTYPE when they really should be
      STT_FUNC.  */
   if (h->type == STT_FUNC
+      || h->type == STT_GNU_IFUNC
       || h->needs_plt
       || (h->type == STT_NOTYPE
 	  && (h->root.type == bfd_link_hash_defined
@@ -1704,9 +1878,10 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 	  && (h->root.u.def.section->flags & SEC_CODE) != 0))
     {
       if (h->plt.refcount <= 0
-	  || SYMBOL_CALLS_LOCAL (info, h)
-	  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
-	      && h->root.type == bfd_link_hash_undefweak))
+	  || (h->type != STT_GNU_IFUNC
+	      && (SYMBOL_CALLS_LOCAL (info, h)
+		  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+		      && h->root.type == bfd_link_hash_undefweak))))
 	{
 	  /* This case can occur if we saw a WPLT30 reloc in an input
 	     file, but the symbol was never referred to by a dynamic
@@ -1828,8 +2003,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
   htab = _bfd_sparc_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
 
-  if (htab->elf.dynamic_sections_created
-      && h->plt.refcount > 0)
+  if ((htab->elf.dynamic_sections_created
+       && h->plt.refcount > 0)
+      || (h->type == STT_GNU_IFUNC
+	  && h->def_regular))
     {
       /* Make sure this symbol is output as a dynamic symbol.
 	 Undefined weak syms won't yet be marked as dynamic.  */
@@ -1840,10 +2017,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
 	    return FALSE;
 	}
 
-      if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h))
+      if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)
+	  || h->type == STT_GNU_IFUNC)
 	{
 	  asection *s = htab->elf.splt;
 
+	  if (s == NULL)
+	    s = htab->elf.iplt;
+
 	  /* Allocate room for the header.  */
 	  if (s->size == 0)
 	    {
@@ -1892,7 +2073,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
 	  s->size += htab->plt_entry_size;
 
 	  /* We also need to make an entry in the .rela.plt section.  */
-	  htab->elf.srelplt->size += SPARC_ELF_RELA_BYTES (htab);
+	  if (s == htab->elf.splt)
+	    htab->elf.srelplt->size += SPARC_ELF_RELA_BYTES (htab);
+	  else
+	    htab->elf.irelplt->size += SPARC_ELF_RELA_BYTES (htab);
 
 	  if (htab->is_vxworks)
 	    {
@@ -1949,7 +2133,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
 	 R_SPARC_TLS_GD_{HI22,LO10} needs one if local symbol and two if
 	 global.  */
       if ((tls_type == GOT_TLS_GD && h->dynindx == -1)
-	  || tls_type == GOT_TLS_IE)
+	  || tls_type == GOT_TLS_IE
+	  || h->type == STT_GNU_IFUNC)
 	htab->elf.srelgot->size += SPARC_ELF_RELA_BYTES (htab);
       else if (tls_type == GOT_TLS_GD)
 	htab->elf.srelgot->size += 2 * SPARC_ELF_RELA_BYTES (htab);
@@ -2060,6 +2245,25 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
   return TRUE;
 }
 
+/* Allocate space in .plt, .got and associated reloc sections for
+   local dynamic relocs.  */
+
+static bfd_boolean
+allocate_local_dynrelocs (void **slot, void *inf)
+{
+  struct elf_link_hash_entry *h
+    = (struct elf_link_hash_entry *) *slot;
+
+  if (h->type != STT_GNU_IFUNC
+      || !h->def_regular
+      || !h->ref_regular
+      || !h->forced_local
+      || h->root.type != bfd_link_hash_defined)
+    abort ();
+
+  return allocate_dynrelocs (h, inf);
+}
+
 /* Find any dynamic relocs that apply to read-only sections.  */
 
 static bfd_boolean
@@ -2172,6 +2376,8 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
 	      else if (p->count != 0)
 		{
 		  srel = elf_section_data (p->sec)->sreloc;
+		  if (!htab->elf.dynamic_sections_created)
+		    srel = htab->elf.irelplt;
 		  srel->size += p->count * SPARC_ELF_RELA_BYTES (htab);
 		  if ((p->sec->output_section->flags & SEC_READONLY) != 0)
 		    info->flags |= DF_TEXTREL;
@@ -2222,6 +2428,9 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
      sym dynamic relocs.  */
   elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info);
 
+  /* Allocate .plt and .got entries, and space for local symbols.  */
+  htab_traverse (htab->loc_hash_table, allocate_local_dynrelocs, info);
+
   if (! ABI_64_P (output_bfd)
       && !htab->is_vxworks
       && elf_hash_table (info)->dynamic_sections_created)
@@ -2251,6 +2460,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
       if (s == htab->elf.splt
 	  || s == htab->elf.sgot
 	  || s == htab->sdynbss
+	  || s == htab->elf.iplt
 	  || s == htab->elf.sgotplt)
 	{
 	  /* Strip this section if we don't need it; see the
@@ -2544,6 +2754,20 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	  sym = local_syms + r_symndx;
 	  sec = local_sections[r_symndx];
 	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+
+	  if (!info->relocatable
+	      && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+	    {
+	      /* Relocate against local STT_GNU_IFUNC symbol.  */
+	      h = elf_sparc_get_local_sym_hash (htab, input_bfd,
+						rel, FALSE);
+	      if (h == NULL)
+		abort ();
+
+	      /* Set STT_GNU_IFUNC symbol value.  */ 
+	      h->root.u.def.value = sym->st_value;
+	      h->root.u.def.section = sec;
+	    }
 	}
       else
 	{
@@ -2580,6 +2804,111 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
       if (info->relocatable)
 	continue;
 
+      if (h != NULL
+	  && h->type == STT_GNU_IFUNC
+	  && h->def_regular)
+	{
+	  asection *plt_sec;
+	  const char *name;
+
+	  if ((input_section->flags & SEC_ALLOC) == 0
+	      || h->plt.offset == (bfd_vma) -1)
+	    abort ();
+
+	  plt_sec = htab->elf.splt;
+	  if (! plt_sec)
+	    plt_sec =htab->elf.iplt;
+
+	  switch (r_type)
+	    {
+	    case R_SPARC_GOTDATA_HIX22:
+	    case R_SPARC_GOTDATA_LOX10:
+	    case R_SPARC_GOTDATA_OP_HIX22:
+	    case R_SPARC_GOTDATA_OP_LOX10:
+	    case R_SPARC_GOT10:
+	    case R_SPARC_GOT13:
+	    case R_SPARC_GOT22:
+	      if (htab->elf.sgot == NULL)
+		abort ();
+	      off = h->got.offset;
+	      if (off == (bfd_vma) -1)
+		abort();
+	      relocation = htab->elf.sgot->output_offset + off - got_base;
+	      goto do_relocation;
+
+	    case R_SPARC_WPLT30:
+	    case R_SPARC_WDISP30:
+	      relocation = (plt_sec->output_section->vma
+			    + plt_sec->output_offset + h->plt.offset);
+	      goto do_relocation;
+
+	    case R_SPARC_32:
+	    case R_SPARC_64:
+	      if (info->shared && h->non_got_ref)
+		{
+		  Elf_Internal_Rela outrel;
+		  bfd_vma offset;
+
+		  offset = _bfd_elf_section_offset (output_bfd, info,
+						    input_section,
+						    rel->r_offset);
+		  if (offset == (bfd_vma) -1
+		      || offset == (bfd_vma) -2)
+		    abort();
+
+		  outrel.r_offset = (input_section->output_section->vma
+				     + input_section->output_offset
+				     + offset);
+
+		  if (h->dynindx == -1
+		      || h->forced_local
+		      || info->executable)
+		    {
+		      outrel.r_info = SPARC_ELF_R_INFO (htab, NULL,
+							0, R_SPARC_IRELATIVE);
+		      outrel.r_addend = relocation + rel->r_addend;
+		    }
+		  else
+		    {
+		      if (h->dynindx == -1)
+			abort();
+		      outrel.r_info = SPARC_ELF_R_INFO (htab, rel, h->dynindx, r_type);
+		      outrel.r_addend = rel->r_addend;
+		    }
+
+		  sparc_elf_append_rela (output_bfd, sreloc, &outrel);
+		  continue;
+		}
+
+	      relocation = (plt_sec->output_section->vma
+			    + plt_sec->output_offset + h->plt.offset);
+	      goto do_relocation;
+
+	    case R_SPARC_HI22:
+	    case R_SPARC_LO10:
+	      /* We should only see such relocs in static links.  */
+	      if (info->shared)
+		abort();
+	      relocation = (plt_sec->output_section->vma
+			    + plt_sec->output_offset + h->plt.offset);
+	      goto do_relocation;
+
+	    default:
+	      if (h->root.root.string)
+		name = h->root.root.string;
+	      else
+		name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+					 NULL);
+	      (*_bfd_error_handler)
+		(_("%B: relocation %s against STT_GNU_IFUNC "
+		   "symbol `%s' isn't handled by %s"), input_bfd,
+		 _bfd_sparc_elf_howto_table[r_type].name,
+		 name, __FUNCTION__);
+	      bfd_set_error (bfd_error_bad_value);
+	      return FALSE;
+	    }
+	}
+
       switch (r_type)
 	{
 	case R_SPARC_GOTDATA_HIX22:
@@ -3496,10 +3825,12 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	}
 
       if (r == bfd_reloc_continue)
-	r = _bfd_final_link_relocate (howto, input_bfd, input_section,
-				      contents, rel->r_offset,
-				      relocation, rel->r_addend);
-
+	{
+do_relocation:
+	  r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+					contents, rel->r_offset,
+					relocation, rel->r_addend);
+	}
       if (r != bfd_reloc_ok)
 	{
 	  switch (r)
@@ -3681,13 +4012,21 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
       bfd_vma r_offset, got_offset;
       int rela_index;
 
-      /* This symbol has an entry in the PLT.  Set it up.  */
-
-      BFD_ASSERT (h->dynindx != -1);
+      /* When building a static executable, use .iplt and
+	 .rela.iplt sections for STT_GNU_IFUNC symbols.  */
+      if (htab->elf.splt != NULL)
+	{
+	  splt = htab->elf.splt;
+	  srela = htab->elf.srelplt;
+	}
+      else
+	{
+	  splt = htab->elf.iplt;
+	  srela = htab->elf.irelplt;
+	}
 
-      splt = htab->elf.splt;
-      srela = htab->elf.srelplt;
-      BFD_ASSERT (splt != NULL && srela != NULL);
+      if (splt == NULL || srela == NULL)
+	abort ();
 
       /* Fill in the entry in the .rela.plt section.  */
       if (htab->is_vxworks)
@@ -3710,29 +4049,73 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 			   + htab->elf.sgotplt->output_offset
 			   + got_offset);
 	  rela.r_addend = 0;
+	  rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx,
+					  R_SPARC_JMP_SLOT);
 	}
       else
 	{
+	  bfd_boolean ifunc = FALSE;
+
 	  /* Fill in the entry in the procedure linkage table.  */
 	  rela_index = SPARC_ELF_BUILD_PLT_ENTRY (htab, output_bfd, splt,
 						  h->plt.offset, splt->size,
 						  &r_offset);
 
+	  if (h == NULL
+	      || h->dynindx == -1
+	      || ((info->executable
+		   || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+		  && h->def_regular
+		  && h->type == STT_GNU_IFUNC))
+	    {
+	      ifunc = TRUE;
+	      BFD_ASSERT (h == NULL
+			  || (h->type == STT_GNU_IFUNC
+			      && h->def_regular
+			      && (h->root.type == bfd_link_hash_defined
+				  || h->root.type == bfd_link_hash_defweak)));
+	    }
+
 	  rela.r_offset = r_offset
 	    + (splt->output_section->vma + splt->output_offset);
-	  if (! ABI_64_P (output_bfd)
-	      || h->plt.offset < (PLT64_LARGE_THRESHOLD * PLT64_ENTRY_SIZE))
+	  if (ABI_64_P (output_bfd)
+	      && h->plt.offset >= (PLT64_LARGE_THRESHOLD * PLT64_ENTRY_SIZE))
 	    {
-	      rela.r_addend = 0;
+	      if (ifunc)
+		{
+		  rela.r_addend = (h->root.u.def.section->output_section->vma
+				   + h->root.u.def.section->output_offset
+				   + h->root.u.def.value);
+		  rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0,
+						  R_SPARC_IRELATIVE);
+		}
+	      else
+		{
+		  rela.r_addend = (-(h->plt.offset + 4)
+				   - splt->output_section->vma
+				   - splt->output_offset);
+		  rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx,
+						  R_SPARC_JMP_SLOT);
+		}
 	    }
 	  else
 	    {
-	      rela.r_addend = (-(h->plt.offset + 4)
-			       - splt->output_section->vma
-			       - splt->output_offset);
+	      if (ifunc)
+		{
+		  rela.r_addend = (h->root.u.def.section->output_section->vma
+				   + h->root.u.def.section->output_offset
+				   + h->root.u.def.value);
+		  rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0,
+						  R_SPARC_JMP_IREL);
+		}
+	      else
+		{
+		  rela.r_addend = 0;
+		  rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx,
+						  R_SPARC_JMP_SLOT);
+		}
 	    }
 	}
-      rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, R_SPARC_JMP_SLOT);
 
       /* Adjust for the first 4 reserved elements in the .plt section
 	 when setting the offset in the .rela.plt section.
@@ -3780,11 +4163,29 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 	 the symbol was forced to be local because of a version file.
 	 The entry in the global offset table will already have been
 	 initialized in the relocate_section function.  */
-      if (info->shared
-	  && SYMBOL_REFERENCES_LOCAL (info, h))
+      if (! info->shared
+	  && h->type == STT_GNU_IFUNC
+	  && h->def_regular)
+	{
+	  asection *plt;
+
+	  /* We load the GOT entry with the PLT entry.  */
+	  plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
+	  SPARC_ELF_PUT_WORD (htab, output_bfd,
+			      (plt->output_section->vma
+			       + plt->output_offset + h->plt.offset),
+			      htab->elf.sgot->contents
+			      + (h->got.offset & ~(bfd_vma) 1));
+	  return TRUE;
+	}
+      else if (info->shared
+	       && SYMBOL_REFERENCES_LOCAL (info, h))
 	{
 	  asection *sec = h->root.u.def.section;
-	  rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_RELATIVE);
+	  if (h->type == STT_GNU_IFUNC)
+	    rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_IRELATIVE);
+	  else
+	    rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_RELATIVE);
 	  rela.r_addend = (h->root.u.def.value
 			   + sec->output_section->vma
 			   + sec->output_offset);
@@ -3823,9 +4224,10 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
   /* Mark some specially defined symbols as absolute.  On VxWorks,
      _GLOBAL_OFFSET_TABLE_ is not absolute: it is relative to the
      ".got" section.  Likewise _PROCEDURE_LINKAGE_TABLE_ and ".plt".  */
-  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || (!htab->is_vxworks
-	  && (h == htab->elf.hgot || h == htab->elf.hplt)))
+  if (sym != NULL
+      && (strcmp (h->root.root.string, "_DYNAMIC") == 0
+	  || (!htab->is_vxworks
+	      && (h == htab->elf.hgot || h == htab->elf.hplt))))
     sym->st_shndx = SHN_ABS;
 
   return TRUE;
@@ -4021,6 +4423,21 @@ sparc_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info)
 		htab->elf.splt->contents + i * 4);
 }
 
+/* Finish up local dynamic symbol handling.  We set the contents of
+   various dynamic sections here.  */
+
+static bfd_boolean
+finish_local_dynamic_symbol (void **slot, void *inf)
+{
+  struct elf_link_hash_entry *h
+    = (struct elf_link_hash_entry *) *slot;
+  struct bfd_link_info *info
+    = (struct bfd_link_info *) inf; 
+
+  return _bfd_sparc_elf_finish_dynamic_symbol (info->output_bfd, info,
+					       h, NULL);
+}
+
 bfd_boolean
 _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
 {
@@ -4083,6 +4500,9 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i
     elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize =
       SPARC_ELF_WORD_BYTES (htab);
 
+  /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
+  htab_traverse (htab->loc_hash_table, finish_local_dynamic_symbol, info);
+
   return TRUE;
 }
 
diff --git a/bfd/elfxx-sparc.h b/bfd/elfxx-sparc.h
index a20855a..36748ec 100644
--- a/bfd/elfxx-sparc.h
+++ b/bfd/elfxx-sparc.h
@@ -59,6 +59,10 @@ struct _bfd_sparc_elf_link_hash_table
   /* Small local sym cache.  */
   struct sym_cache sym_cache;
 
+  /* Used by local STT_GNU_IFUNC symbols.  */
+  htab_t loc_hash_table;
+  void *loc_hash_memory;
+
   /* True if the target system is VxWorks.  */
   int is_vxworks;
 
@@ -102,6 +106,8 @@ extern bfd_boolean _bfd_sparc_elf_mkobject
   (bfd *);
 extern struct bfd_link_hash_table *_bfd_sparc_elf_link_hash_table_create
   (bfd *);
+extern void _bfd_sparc_elf_link_hash_table_free
+  (struct bfd_link_hash_table *);
 extern bfd_boolean _bfd_sparc_elf_create_dynamic_sections
   (bfd *, struct bfd_link_info *);
 extern void _bfd_sparc_elf_copy_indirect_symbol
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 2be8fe5..fa9d187 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -928,6 +928,8 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_SPARC_GOTDATA_OP_HIX22",
   "BFD_RELOC_SPARC_GOTDATA_OP_LOX10",
   "BFD_RELOC_SPARC_GOTDATA_OP",
+  "BFD_RELOC_SPARC_JMP_IREL",
+  "BFD_RELOC_SPARC_IRELATIVE",
   "BFD_RELOC_SPARC_BASE13",
   "BFD_RELOC_SPARC_BASE22",
   "BFD_RELOC_SPARC_10",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 9451740..dca56d9 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -1870,6 +1870,10 @@ ENUMX
   BFD_RELOC_SPARC_GOTDATA_OP_LOX10
 ENUMX
   BFD_RELOC_SPARC_GOTDATA_OP
+ENUMX
+  BFD_RELOC_SPARC_JMP_IREL
+ENUMX
+  BFD_RELOC_SPARC_IRELATIVE
 ENUMDOC
   SPARC ELF relocations.  There is probably some overlap with other
   relocation types already defined.
diff --git a/include/elf/sparc.h b/include/elf/sparc.h
index 68d6285..f7458df 100644
--- a/include/elf/sparc.h
+++ b/include/elf/sparc.h
@@ -164,6 +164,8 @@ START_RELOC_NUMBERS (elf_sparc_reloc_type)
   
   EMPTY_RELOC  (R_SPARC_max_std)
 
+  RELOC_NUMBER (R_SPARC_JMP_IREL, 248)
+  RELOC_NUMBER (R_SPARC_IRELATIVE, 249)
   RELOC_NUMBER (R_SPARC_GNU_VTINHERIT, 250)
   RELOC_NUMBER (R_SPARC_GNU_VTENTRY, 251)
   RELOC_NUMBER (R_SPARC_REV32, 252)
diff --git a/ld/testsuite/ld-ifunc/ifunc.exp b/ld/testsuite/ld-ifunc/ifunc.exp
index cdd4ff8..be519ce 100644
--- a/ld/testsuite/ld-ifunc/ifunc.exp
+++ b/ld/testsuite/ld-ifunc/ifunc.exp
@@ -23,11 +23,12 @@
 # Written by Nick Clifton <nickc@redhat.com>
 
 
-# IFUNC support has only been implemented for the ix86, x86_64 and powerpc
-# so far.
+# IFUNC support has only been implemented for the ix86, x86_64, powerpc,
+# and sparc so far.
 if {!(([istarget "i?86-*-*"]
        || [istarget "x86_64-*-*"]
-       || [istarget "powerpc*-*-*"])
+       || [istarget "powerpc*-*-*"]
+       || [istarget "sparc*-*-*"])
       && ([istarget "*-*-elf*"]
 	  || ([istarget "*-*-linux*"]
 	      && ![istarget "*-*-*aout*"]


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