* elfxx-sparc.c (_bfd_sparc_elf_check_relocs): When STT_GNU_IFUNC and
[deliverable/binutils-gdb.git] / bfd / elfxx-sparc.c
index 63eed174aa26632e79bb9c7cda39294212f4c656..2137df088f2a4e6530ef799c0cb42202723e6dcc 100644 (file)
@@ -1,11 +1,11 @@
 /* SPARC-specific support for ELF
-   Copyright 2005, 2006 Free Software Foundation, Inc.
+   Copyright 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
    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 2 of the License, or
+   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,
 
    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.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
 
 /* This file handles functionality common to the different SPARC ABI's.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "bfdlink.h"
 #include "libbfd.h"
 #include "libiberty.h"
@@ -29,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)
@@ -256,8 +260,17 @@ static reloc_howto_type _bfd_sparc_elf_howto_table[] =
   HOWTO(R_SPARC_TLS_DTPOFF32,0,2,32,FALSE,0,complain_overflow_bitfield,bfd_elf_generic_reloc,"R_SPARC_TLS_DTPOFF32",FALSE,0,0xffffffff,TRUE),
   HOWTO(R_SPARC_TLS_DTPOFF64,0,4,64,FALSE,0,complain_overflow_bitfield,bfd_elf_generic_reloc,"R_SPARC_TLS_DTPOFF64",FALSE,0,MINUS_ONE,TRUE),
   HOWTO(R_SPARC_TLS_TPOFF32,0,0, 0,FALSE,0,complain_overflow_dont,   bfd_elf_generic_reloc,  "R_SPARC_TLS_TPOFF32",FALSE,0,0x00000000,TRUE),
-  HOWTO(R_SPARC_TLS_TPOFF64,0,0, 0,FALSE,0,complain_overflow_dont,   bfd_elf_generic_reloc,  "R_SPARC_TLS_TPOFF64",FALSE,0,0x00000000,TRUE)
+  HOWTO(R_SPARC_TLS_TPOFF64,0,0, 0,FALSE,0,complain_overflow_dont,   bfd_elf_generic_reloc,  "R_SPARC_TLS_TPOFF64",FALSE,0,0x00000000,TRUE),
+  HOWTO(R_SPARC_GOTDATA_HIX22,0,2,0,FALSE,0,complain_overflow_bitfield,sparc_elf_hix22_reloc,"R_SPARC_GOTDATA_HIX22",FALSE,0,0x003fffff, FALSE),
+  HOWTO(R_SPARC_GOTDATA_LOX10,0,2,0,FALSE,0,complain_overflow_dont,  sparc_elf_lox10_reloc,  "R_SPARC_GOTDATA_LOX10",FALSE,0,0x000003ff, FALSE),
+  HOWTO(R_SPARC_GOTDATA_OP_HIX22,0,2,0,FALSE,0,complain_overflow_bitfield,sparc_elf_hix22_reloc,"R_SPARC_GOTDATA_OP_HIX22",FALSE,0,0x003fffff, FALSE),
+  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 =
@@ -347,7 +360,14 @@ static const struct elf_reloc_map sparc_reloc_map[] =
   { BFD_RELOC_SPARC_H44, R_SPARC_H44 },
   { BFD_RELOC_SPARC_M44, R_SPARC_M44 },
   { BFD_RELOC_SPARC_L44, R_SPARC_L44 },
+  { BFD_RELOC_SPARC_GOTDATA_HIX22, R_SPARC_GOTDATA_HIX22 },
+  { BFD_RELOC_SPARC_GOTDATA_LOX10, R_SPARC_GOTDATA_LOX10 },
+  { BFD_RELOC_SPARC_GOTDATA_OP_HIX22, R_SPARC_GOTDATA_OP_HIX22 },
+  { 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 },
@@ -361,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;
 
@@ -384,11 +410,41 @@ _bfd_sparc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     return NULL;
 }
 
+reloc_howto_type *
+_bfd_sparc_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                                 const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0;
+       i < (sizeof (_bfd_sparc_elf_howto_table)
+           / sizeof (_bfd_sparc_elf_howto_table[0]));
+       i++)
+    if (_bfd_sparc_elf_howto_table[i].name != NULL
+       && strcasecmp (_bfd_sparc_elf_howto_table[i].name, r_name) == 0)
+      return &_bfd_sparc_elf_howto_table[i];
+
+  if (strcasecmp (sparc_vtinherit_howto.name, r_name) == 0)
+    return &sparc_vtinherit_howto;
+  if (strcasecmp (sparc_vtentry_howto.name, r_name) == 0)
+    return &sparc_vtentry_howto;
+  if (strcasecmp (sparc_rev32_howto.name, r_name) == 0)
+    return &sparc_rev32_howto;
+
+  return NULL;
+}
+
 reloc_howto_type *
 _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;
 
@@ -484,61 +540,47 @@ struct _bfd_sparc_elf_obj_tdata
 #define _bfd_sparc_elf_local_got_tls_type(abfd) \
   (_bfd_sparc_elf_tdata (abfd)->local_got_tls_type)
 
+#define is_sparc_elf(bfd)                              \
+  (bfd_get_flavour (bfd) == bfd_target_elf_flavour     \
+   && elf_tdata (bfd) != NULL                          \
+   && elf_object_id (bfd) == SPARC_ELF_DATA)
+
 bfd_boolean
 _bfd_sparc_elf_mkobject (bfd *abfd)
 {
-  if (abfd->tdata.any == NULL)
-    {
-      bfd_size_type amt = sizeof (struct _bfd_sparc_elf_obj_tdata);
-      abfd->tdata.any = bfd_zalloc (abfd, amt);
-      if (abfd->tdata.any == NULL)
-       return FALSE;
-    }
-  return bfd_elf_mkobject (abfd);
-}
-
-static void
-sparc_put_word_32 (bfd *bfd, bfd_vma val, void *ptr)
-{
-  bfd_put_32 (bfd, val, ptr);
+  return bfd_elf_allocate_object (abfd, sizeof (struct _bfd_sparc_elf_obj_tdata),
+                                 SPARC_ELF_DATA);
 }
 
 static void
-sparc_put_word_64 (bfd *bfd, bfd_vma val, void *ptr)
+sparc_put_word_32 (bfd *abfd, bfd_vma val, void *ptr)
 {
-  bfd_put_64 (bfd, val, ptr);
+  bfd_put_32 (abfd, val, ptr);
 }
 
 static void
-sparc_elf_append_rela_64 (bfd *abfd ATTRIBUTE_UNUSED,
-                         asection *s ATTRIBUTE_UNUSED,
-                         Elf_Internal_Rela *rel ATTRIBUTE_UNUSED)
+sparc_put_word_64 (bfd *abfd, bfd_vma val, void *ptr)
 {
-#ifdef BFD64
-  Elf64_External_Rela *loc64;
-
-  loc64 = (Elf64_External_Rela *) s->contents;
-  loc64 += s->reloc_count++;
-  bfd_elf64_swap_reloca_out (abfd, rel, (bfd_byte *) loc64);
-#endif
+  bfd_put_64 (abfd, val, ptr);
 }
 
 static void
-sparc_elf_append_rela_32 (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
+sparc_elf_append_rela (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
 {
-  Elf32_External_Rela *loc32;
+  const struct elf_backend_data *bed;
+  bfd_byte *loc;
 
-  loc32 = (Elf32_External_Rela *) s->contents;
-  loc32 += s->reloc_count++;
-  bfd_elf32_swap_reloca_out (abfd, rel, (bfd_byte *) loc32);
+  bed = get_elf_backend_data (abfd);
+  loc = s->contents + (s->reloc_count++ * bed->s->sizeof_rela);
+  bed->s->swap_reloca_out (abfd, rel, loc);
 }
 
 static bfd_vma
 sparc_elf_r_info_64 (Elf_Internal_Rela *in_rel ATTRIBUTE_UNUSED,
-                    bfd_vma index ATTRIBUTE_UNUSED,
+                    bfd_vma rel_index ATTRIBUTE_UNUSED,
                     bfd_vma type ATTRIBUTE_UNUSED)
 {
-  return ELF64_R_INFO (index,
+  return ELF64_R_INFO (rel_index,
                       (in_rel ?
                        ELF64_R_TYPE_INFO (ELF64_R_TYPE_DATA (in_rel->r_info),
                                           type) : type));
@@ -546,9 +588,9 @@ sparc_elf_r_info_64 (Elf_Internal_Rela *in_rel ATTRIBUTE_UNUSED,
 
 static bfd_vma
 sparc_elf_r_info_32 (Elf_Internal_Rela *in_rel ATTRIBUTE_UNUSED,
-                    bfd_vma index, bfd_vma type)
+                    bfd_vma rel_index, bfd_vma type)
 {
-  return ELF32_R_INFO (index, type);
+  return ELF32_R_INFO (rel_index, type);
 }
 
 static bfd_vma
@@ -612,7 +654,7 @@ sparc64_plt_entry_build (bfd *output_bfd, asection *splt, bfd_vma offset,
 {
   unsigned char *entry = splt->contents + offset;
   const unsigned int nop = SPARC_NOP;
-  int index;
+  int plt_index;
 
   if (offset < (PLT64_LARGE_THRESHOLD * PLT64_ENTRY_SIZE))
     {
@@ -620,9 +662,9 @@ sparc64_plt_entry_build (bfd *output_bfd, asection *splt, bfd_vma offset,
 
       *r_offset = offset;
 
-      index = (offset / PLT64_ENTRY_SIZE);
+      plt_index = (offset / PLT64_ENTRY_SIZE);
 
-      sethi = 0x03000000 | (index * PLT64_ENTRY_SIZE);
+      sethi = 0x03000000 | (plt_index * PLT64_ENTRY_SIZE);
       ba = 0x30680000
        | (((splt->contents + PLT64_ENTRY_SIZE) - (entry + 4)) / 4 & 0x7ffff);
 
@@ -669,7 +711,7 @@ sparc64_plt_entry_build (bfd *output_bfd, asection *splt, bfd_vma offset,
 
       ofs = offset % block_size;
 
-      index = (PLT64_LARGE_THRESHOLD +
+      plt_index = (PLT64_LARGE_THRESHOLD +
               (block * 160) +
               (ofs / insn_chunk_size));
 
@@ -699,7 +741,7 @@ sparc64_plt_entry_build (bfd *output_bfd, asection *splt, bfd_vma offset,
       bfd_put_64 (output_bfd, (bfd_vma) (splt->contents - (entry + 4)), ptr);
     }
 
-  return index - 4;
+  return plt_index - 4;
 }
 
 /* The format of the first PLT entry in a VxWorks executable.  */
@@ -749,9 +791,6 @@ static const bfd_vma sparc_vxworks_shared_plt_entry[] =
 #define SPARC_ELF_PUT_WORD(htab, bfd, val, ptr)        \
        htab->put_word(bfd, val, ptr)
 
-#define SPARC_ELF_APPEND_RELA(htab, bfd, sec, rela)    \
-       htab->append_rela(bfd, sec, rela)
-
 #define SPARC_ELF_R_INFO(htab, in_rel, index, type)    \
        htab->r_info(in_rel, index, type)
 
@@ -812,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 *
@@ -827,7 +938,6 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
   if (ABI_64_P (abfd))
     {
       ret->put_word = sparc_put_word_64;
-      ret->append_rela = sparc_elf_append_rela_64;
       ret->r_info = sparc_elf_r_info_64;
       ret->r_symndx = sparc_elf_r_symndx_64;
       ret->dtpoff_reloc = R_SPARC_TLS_DTPOFF64;
@@ -839,11 +949,14 @@ _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
     {
       ret->put_word = sparc_put_word_32;
-      ret->append_rela = sparc_elf_append_rela_32;
       ret->r_info = sparc_elf_r_info_32;
       ret->r_symndx = sparc_elf_r_symndx_32;
       ret->dtpoff_reloc = R_SPARC_TLS_DTPOFF32;
@@ -855,10 +968,26 @@ _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,
-                                     sizeof (struct _bfd_sparc_elf_link_hash_entry)))
+                                     sizeof (struct _bfd_sparc_elf_link_hash_entry),
+                                     SPARC_ELF_DATA))
+    {
+      free (ret);
+      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;
@@ -867,41 +996,19 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
   return &ret->elf.root;
 }
 
-/* Create .got and .rela.got sections in DYNOBJ, and set up
-   shortcuts to them in our hash table.  */
+/* Destroy a SPARC ELF linker hash table.  */
 
-static bfd_boolean
-create_got_section (bfd *dynobj, struct bfd_link_info *info)
+void
+_bfd_sparc_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
 {
-  struct _bfd_sparc_elf_link_hash_table *htab;
-
-  if (! _bfd_elf_create_got_section (dynobj, info))
-    return FALSE;
-
-  htab = _bfd_sparc_elf_hash_table (info);
-  htab->sgot = bfd_get_section_by_name (dynobj, ".got");
-  BFD_ASSERT (htab->sgot != NULL);
-
-  htab->srelgot = bfd_make_section_with_flags (dynobj, ".rela.got",
-                                              SEC_ALLOC
-                                              | SEC_LOAD
-                                              | SEC_HAS_CONTENTS
-                                              | SEC_IN_MEMORY
-                                              | SEC_LINKER_CREATED
-                                              | SEC_READONLY);
-  if (htab->srelgot == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->srelgot,
-                                     htab->word_align_power))
-    return FALSE;
-
-  if (htab->is_vxworks)
-    {
-      htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
-      if (!htab->sgotplt)
-       return FALSE;
-    }
-
-  return TRUE;
+  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
@@ -915,14 +1022,11 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
   struct _bfd_sparc_elf_link_hash_table *htab;
 
   htab = _bfd_sparc_elf_hash_table (info);
-  if (!htab->sgot && !create_got_section (dynobj, info))
-    return FALSE;
+  BFD_ASSERT (htab != NULL);
 
   if (!_bfd_elf_create_dynamic_sections (dynobj, info))
     return FALSE;
 
-  htab->splt = bfd_get_section_by_name (dynobj, ".plt");
-  htab->srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
   htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss");
   if (!info->shared)
     htab->srelbss = bfd_get_section_by_name (dynobj, ".rela.bss");
@@ -946,29 +1050,45 @@ _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->splt || !htab->srelplt || !htab->sdynbss
+  if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
       || (!info->shared && !htab->srelbss))
     abort ();
 
   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
@@ -1082,7 +1202,8 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
     return TRUE;
 
   htab = _bfd_sparc_elf_hash_table (info);
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  BFD_ASSERT (htab != NULL);
+  symtab_hdr = &elf_symtab_hdr (abfd);
   sym_hashes = elf_sym_hashes (abfd);
   local_got_offsets = elf_local_got_offsets (abfd);
 
@@ -1092,12 +1213,21 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
     num_relocs = NUM_SHDR_ENTRIES (& elf_section_data (sec)->rel_hdr);
   else
     num_relocs = sec->reloc_count;
+
+  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);
@@ -1109,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];
@@ -1119,6 +1274,15 @@ _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->ref_regular = 1;
+             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)
@@ -1168,6 +1332,10 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_SPARC_GOT10:
        case R_SPARC_GOT13:
        case R_SPARC_GOT22:
+       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_TLS_GD_HI22:
        case R_SPARC_TLS_GD_LO10:
          /* This symbol requires a global offset table entry.  */
@@ -1180,6 +1348,8 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              case R_SPARC_GOT10:
              case R_SPARC_GOT13:
              case R_SPARC_GOT22:
+             case R_SPARC_GOTDATA_OP_HIX22:
+             case R_SPARC_GOTDATA_OP_LOX10:
                tls_type = GOT_NORMAL;
                break;
              case R_SPARC_TLS_GD_HI22:
@@ -1217,7 +1387,16 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    _bfd_sparc_elf_local_got_tls_type (abfd)
                      = (char *) (local_got_refcounts + symtab_hdr->sh_info);
                  }
-               local_got_refcounts[r_symndx] += 1;
+               switch (r_type)
+                 {
+                 case R_SPARC_GOTDATA_OP_HIX22:
+                 case R_SPARC_GOTDATA_OP_LOX10:
+                   break;
+
+                 default:
+                   local_got_refcounts[r_symndx] += 1;
+                   break;
+                 }
                old_tls_type = _bfd_sparc_elf_local_got_tls_type (abfd) [r_symndx];
              }
 
@@ -1247,11 +1426,9 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              }
          }
 
-         if (htab->sgot == NULL)
+         if (htab->elf.sgot == NULL)
            {
-             if (htab->elf.dynobj == NULL)
-               htab->elf.dynobj = abfd;
-             if (!create_got_section (htab->elf.dynobj, info))
+             if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
                return FALSE;
            }
          break;
@@ -1301,6 +1478,9 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    goto r_sparc_plt32;
                  break;
                }
+             /* PR 7027: We need similar behaviour for 64-bit binaries.  */
+             else if (r_type == R_SPARC_WPLT30)
+               break;
 
              /* It does not make sense to have a procedure linkage
                  table entry for a local symbol.  */
@@ -1403,14 +1583,17 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
               && (sec->flags & SEC_ALLOC) != 0
               && (! _bfd_sparc_elf_howto_table[r_type].pc_relative
                   || (h != NULL
-                      && (! info->symbolic
+                      && (! SYMBOLIC_BIND (info, h)
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
              || (!info->shared
                  && (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;
@@ -1420,42 +1603,12 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                 section in dynobj and make room for the reloc.  */
              if (sreloc == NULL)
                {
-                 const char *name;
-                 bfd *dynobj;
-
-                 name = (bfd_elf_string_from_elf_section
-                         (abfd,
-                          elf_elfheader (abfd)->e_shstrndx,
-                          elf_section_data (sec)->rel_hdr.sh_name));
-                 if (name == NULL)
-                   return FALSE;
-
-                 BFD_ASSERT (CONST_STRNEQ (name, ".rela")
-                             && strcmp (bfd_get_section_name (abfd, sec),
-                                        name + 5) == 0);
-
-                 if (htab->elf.dynobj == NULL)
-                   htab->elf.dynobj = abfd;
-                 dynobj = htab->elf.dynobj;
+                 sreloc = _bfd_elf_make_dynamic_reloc_section
+                   (sec, htab->elf.dynobj, htab->word_align_power,
+                    abfd, /*rela?*/ TRUE);
 
-                 sreloc = bfd_get_section_by_name (dynobj, name);
                  if (sreloc == NULL)
-                   {
-                     flagword flags;
-
-                     flags = (SEC_HAS_CONTENTS | SEC_READONLY
-                              | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-                     if ((sec->flags & SEC_ALLOC) != 0)
-                       flags |= SEC_ALLOC | SEC_LOAD;
-                     sreloc = bfd_make_section_with_flags (dynobj,
-                                                           name,
-                                                           flags);
-                     if (sreloc == NULL
-                         || ! bfd_set_section_alignment (dynobj, sreloc,
-                                                         htab->word_align_power))
-                       return FALSE;
-                   }
-                 elf_section_data (sec)->sreloc = sreloc;
+                   return FALSE;
                }
 
              /* If this is a global symbol, we count the number of
@@ -1467,14 +1620,13 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  /* Track dynamic relocs needed for local syms too.
                     We really need local syms available to do this
                     easily.  Oh well.  */
-
                  asection *s;
                  void *vpp;
 
-                 s = bfd_section_from_r_symndx (abfd, &htab->sym_sec,
-                                                sec, r_symndx);
+                 BFD_ASSERT (isym != NULL);
+                 s = bfd_section_from_elf_index (abfd, isym->st_shndx);
                  if (s == NULL)
-                   return FALSE;
+                   s = sec;
 
                  vpp = &elf_section_data (s)->local_dynrel;
                  head = (struct _bfd_sparc_elf_dyn_relocs **) vpp;
@@ -1508,7 +1660,9 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          break;
 
        case R_SPARC_GNU_VTENTRY:
-         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+         BFD_ASSERT (h != NULL);
+         if (h != NULL
+             && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
            return FALSE;
          break;
 
@@ -1532,35 +1686,14 @@ _bfd_sparc_elf_gc_mark_hook (asection *sec,
                             Elf_Internal_Sym *sym)
 {
   if (h != NULL)
-    {
-      struct _bfd_sparc_elf_link_hash_table *htab;
-
-      htab = _bfd_sparc_elf_hash_table (info);
-      switch (SPARC_ELF_R_TYPE (rel->r_info))
+    switch (SPARC_ELF_R_TYPE (rel->r_info))
       {
       case R_SPARC_GNU_VTINHERIT:
       case R_SPARC_GNU_VTENTRY:
-       break;
-
-      default:
-       switch (h->root.type)
-         {
-         case bfd_link_hash_defined:
-         case bfd_link_hash_defweak:
-           return h->root.u.def.section;
-
-         case bfd_link_hash_common:
-           return h->root.u.c.p->section;
-
-         default:
-           break;
-         }
+       return NULL;
       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
 
-  return NULL;
+  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
 
 /* Update the got entry reference counts for the section being removed.  */
@@ -1574,10 +1707,16 @@ _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
   bfd_signed_vma *local_got_refcounts;
   const Elf_Internal_Rela *rel, *relend;
 
+  if (info->relocatable)
+    return TRUE;
+
+  BFD_ASSERT (is_sparc_elf (abfd) || sec->reloc_count == 0);
+
   elf_section_data (sec)->local_dynrel = NULL;
 
   htab = _bfd_sparc_elf_hash_table (info);
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  BFD_ASSERT (htab != NULL);
+  symtab_hdr = &elf_symtab_hdr (abfd);
   sym_hashes = elf_sym_hashes (abfd);
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
@@ -1626,6 +1765,10 @@ _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
        case R_SPARC_GOT10:
        case R_SPARC_GOT13:
        case R_SPARC_GOT22:
+       case R_SPARC_GOTDATA_HIX22:
+       case R_SPARC_GOTDATA_LOX10:
+       case R_SPARC_GOTDATA_OP_HIX22:
+       case R_SPARC_GOTDATA_OP_LOX10:
          if (h != NULL)
            {
              if (h->got.refcount > 0)
@@ -1633,8 +1776,17 @@ _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
            }
          else
            {
-             if (local_got_refcounts[r_symndx] > 0)
-               local_got_refcounts[r_symndx]--;
+             switch (r_type)
+               {
+               case R_SPARC_GOTDATA_OP_HIX22:
+               case R_SPARC_GOTDATA_OP_LOX10:
+                 break;
+
+               default:
+                 if (local_got_refcounts[r_symndx] > 0)
+                   local_got_refcounts[r_symndx]--;
+                 break;
+               }
            }
          break;
 
@@ -1716,13 +1868,14 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   struct _bfd_sparc_elf_link_hash_entry * eh;
   struct _bfd_sparc_elf_dyn_relocs *p;
   asection *s;
-  unsigned int power_of_two;
 
   htab = _bfd_sparc_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
 
   /* 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
@@ -1736,6 +1889,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
@@ -1743,11 +1897,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
-         || (! info->shared
-             && !h->def_dynamic
-             && !h->ref_dynamic
-             && h->root.type != bfd_link_hash_undefweak
-             && h->root.type != bfd_link_hash_undefined))
+         || (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
@@ -1790,6 +1943,13 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   if (!h->non_got_ref)
     return TRUE;
 
+  /* If -z nocopyreloc was given, we won't generate them either.  */
+  if (info->nocopyreloc)
+    {
+      h->non_got_ref = 0;
+      return TRUE;
+    }
+
   eh = (struct _bfd_sparc_elf_link_hash_entry *) h;
   for (p = eh->dyn_relocs; p != NULL; p = p->next)
     {
@@ -1833,29 +1993,9 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       h->needs_copy = 1;
     }
 
-  /* We need to figure out the alignment required for this symbol.  I
-     have no idea how ELF linkers handle this.  */
-  power_of_two = bfd_log2 (h->size);
-  if (power_of_two > htab->align_power_max)
-    power_of_two = htab->align_power_max;
-
-  /* Apply the required alignment.  */
   s = htab->sdynbss;
-  s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two));
-  if (power_of_two > bfd_get_section_alignment (dynobj, s))
-    {
-      if (! bfd_set_section_alignment (dynobj, s, power_of_two))
-       return FALSE;
-    }
-
-  /* Define the symbol as being at this point in the section.  */
-  h->root.u.def.section = s;
-  h->root.u.def.value = s->size;
-
-  /* Increment the section size to make room for the symbol.  */
-  s->size += h->size;
 
-  return TRUE;
+  return _bfd_elf_adjust_dynamic_copy (h, s);
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
@@ -1880,9 +2020,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
 
   info = (struct bfd_link_info *) 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
+         && h->ref_regular))
     {
       /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
@@ -1893,9 +2037,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
+             && h->def_regular))
        {
-         asection *s = htab->splt;
+         asection *s = htab->elf.splt;
+
+         if (s == NULL)
+           s = htab->elf.iplt;
 
          /* Allocate room for the header.  */
          if (s->size == 0)
@@ -1945,12 +2094,15 @@ 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->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)
            {
              /* Allocate space for the .got.plt entry.  */
-             htab->sgotplt->size += 4;
+             htab->elf.sgotplt->size += 4;
 
              /* ...and for the .rela.plt.unloaded relocations.  */
              if (!info->shared)
@@ -1991,7 +2143,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
            return FALSE;
        }
 
-      s = htab->sgot;
+      s = htab->elf.sgot;
       h->got.offset = s->size;
       s->size += SPARC_ELF_WORD_BYTES (htab);
       /* R_SPARC_TLS_GD_HI{22,LO10} needs 2 consecutive GOT slots.  */
@@ -2002,12 +2154,13 @@ 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)
-       htab->srelgot->size += SPARC_ELF_RELA_BYTES (htab);
+         || 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->srelgot->size += 2 * SPARC_ELF_RELA_BYTES (htab);
+       htab->elf.srelgot->size += 2 * SPARC_ELF_RELA_BYTES (htab);
       else if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h))
-       htab->srelgot->size += SPARC_ELF_RELA_BYTES (htab);
+       htab->elf.srelgot->size += SPARC_ELF_RELA_BYTES (htab);
     }
   else
     h->got.offset = (bfd_vma) -1;
@@ -2024,9 +2177,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
 
   if (info->shared)
     {
-      if (h->def_regular
-         && (h->forced_local
-             || info->symbolic))
+      if (SYMBOL_CALLS_LOCAL (info, h))
        {
          struct _bfd_sparc_elf_dyn_relocs **pp;
 
@@ -2041,6 +2192,19 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
            }
        }
 
+      if (htab->is_vxworks)
+       {
+         struct _bfd_sparc_elf_dyn_relocs **pp;
+
+         for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+           {
+             if (strcmp (p->sec->output_section->name, ".tls_vars") == 0)
+               *pp = p->next;
+             else
+               pp = &p->next;
+           }
+       }
+
       /* Also discard relocs on undefined weak syms with non-default
         visibility.  */
       if (eh->dyn_relocs != NULL
@@ -2102,6 +2266,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
@@ -2160,6 +2343,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
   bfd *ibfd;
 
   htab = _bfd_sparc_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
   dynobj = htab->elf.dynobj;
   BFD_ASSERT (dynobj != NULL);
 
@@ -2186,7 +2370,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
       Elf_Internal_Shdr *symtab_hdr;
       asection *srel;
 
-      if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+      if (! is_sparc_elf (ibfd))
        continue;
 
       for (s = ibfd->sections; s != NULL; s = s->next)
@@ -2203,9 +2387,18 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
                     linker script /DISCARD/, so we'll be discarding
                     the relocs too.  */
                }
+             else if (htab->is_vxworks
+                      && strcmp (p->sec->output_section->name,
+                                 ".tls_vars") == 0)
+               {
+                 /* Relocations in vxworks .tls_vars sections are
+                    handled specially by the loader.  */
+               }
              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;
@@ -2217,12 +2410,12 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
       if (!local_got)
        continue;
 
-      symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
+      symtab_hdr = &elf_symtab_hdr (ibfd);
       locsymcount = symtab_hdr->sh_info;
       end_local_got = local_got + locsymcount;
       local_tls_type = _bfd_sparc_elf_local_got_tls_type (ibfd);
-      s = htab->sgot;
-      srel = htab->srelgot;
+      s = htab->elf.sgot;
+      srel = htab->elf.srelgot;
       for (; local_got < end_local_got; ++local_got, ++local_tls_type)
        {
          if (*local_got > 0)
@@ -2245,9 +2438,9 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
     {
       /* Allocate 2 got entries and 1 dynamic reloc for
         R_SPARC_TLS_LDM_{HI22,LO10} relocs.  */
-      htab->tls_ldm_got.offset = htab->sgot->size;
-      htab->sgot->size += (2 * SPARC_ELF_WORD_BYTES (htab));
-      htab->srelgot->size += SPARC_ELF_RELA_BYTES (htab);
+      htab->tls_ldm_got.offset = htab->elf.sgot->size;
+      htab->elf.sgot->size += (2 * SPARC_ELF_WORD_BYTES (htab));
+      htab->elf.srelgot->size += SPARC_ELF_RELA_BYTES (htab);
     }
   else
     htab->tls_ldm_got.offset = -1;
@@ -2256,20 +2449,23 @@ _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)
     {
       /* Make space for the trailing nop in .plt.  */
-      if (htab->splt->size > 0)
-       htab->splt->size += 1 * SPARC_INSN_BYTES;
+      if (htab->elf.splt->size > 0)
+       htab->elf.splt->size += 1 * SPARC_INSN_BYTES;
 
       /* If the .got section is more than 0x1000 bytes, we add
         0x1000 to the value of _GLOBAL_OFFSET_TABLE_, so that 13
         bit relocations have a greater chance of working.
 
         FIXME: Make this optimization work for 64-bit too.  */
-      if (htab->sgot->size >= 0x1000
+      if (htab->elf.sgot->size >= 0x1000
          && elf_hash_table (info)->hgot->root.u.def.value == 0)
        elf_hash_table (info)->hgot->root.u.def.value = 0x1000;
     }
@@ -2282,10 +2478,11 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
 
-      if (s == htab->splt
-         || s == htab->sgot
+      if (s == htab->elf.splt
+         || s == htab->elf.sgot
          || s == htab->sdynbss
-         || s == htab->sgotplt)
+         || s == htab->elf.iplt
+         || s == htab->elf.sgotplt)
        {
          /* Strip this section if we don't need it; see the
             comment below.  */
@@ -2347,7 +2544,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
            return FALSE;
        }
 
-      if (htab->srelplt->size != 0)
+      if (htab->elf.srelplt->size != 0)
        {
          if (!add_dynamic_entry (DT_PLTGOT, 0)
              || !add_dynamic_entry (DT_PLTRELSZ, 0)
@@ -2428,6 +2625,9 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
                eht->dynsymcount++;
              }
        }
+      if (htab->is_vxworks
+         && !elf_vxworks_add_dynamic_entries (output_bfd, info))
+       return FALSE;
     }
 #undef add_dynamic_entry
 
@@ -2457,6 +2657,10 @@ _bfd_sparc_elf_relax_section (bfd *abfd ATTRIBUTE_UNUSED,
                              struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
                              bfd_boolean *again)
 {
+  if (link_info->relocatable)
+    (*link_info->callbacks->einfo)
+      (_("%P%F: --relax and -r may not be used together\n"));
+
   *again = FALSE;
   sec_do_relax (section) = 1;
   return TRUE;
@@ -2489,13 +2693,32 @@ tpoff (struct bfd_link_info *info, bfd_vma address)
   return address - htab->tls_size - htab->tls_sec->vma;
 }
 
+/* Return the relocation value for a %gdop relocation.  */
+
+static bfd_vma
+gdopoff (struct bfd_link_info *info, bfd_vma address)
+{
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+  bfd_vma got_base;
+
+  got_base = (htab->hgot->root.u.def.value
+             + htab->hgot->root.u.def.section->output_offset
+             + htab->hgot->root.u.def.section->output_section->vma);
+
+  return address - got_base;
+}
+
 /* Relocate a SPARC ELF section.  */
 
 bfd_boolean
-_bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
-                                bfd *input_bfd, asection *input_section,
-                                bfd_byte *contents, Elf_Internal_Rela *relocs,
-                                Elf_Internal_Sym *local_syms, asection **local_sections)
+_bfd_sparc_elf_relocate_section (bfd *output_bfd,
+                                struct bfd_link_info *info,
+                                bfd *input_bfd,
+                                asection *input_section,
+                                bfd_byte *contents,
+                                Elf_Internal_Rela *relocs,
+                                Elf_Internal_Sym *local_syms,
+                                asection **local_sections)
 {
   struct _bfd_sparc_elf_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
@@ -2506,12 +2729,11 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
   int num_relocs;
-
-  if (info->relocatable)
-    return TRUE;
+  bfd_boolean is_vxworks_tls;
 
   htab = _bfd_sparc_elf_hash_table (info);
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  BFD_ASSERT (htab != NULL);
+  symtab_hdr = &elf_symtab_hdr (input_bfd);
   sym_hashes = elf_sym_hashes (input_bfd);
   local_got_offsets = elf_local_got_offsets (input_bfd);
 
@@ -2521,6 +2743,11 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
     got_base = elf_hash_table (info)->hgot->root.u.def.value;
 
   sreloc = elf_section_data (input_section)->sreloc;
+  /* We have to handle relocations in vxworks .tls_vars sections
+     specially, because the dynamic loader is 'weird'.  */
+  is_vxworks_tls = (htab->is_vxworks && info->shared
+                   && !strcmp (input_section->output_section->name,
+                               ".tls_vars"));
 
   rel = relocs;
   if (ABI_64_P (output_bfd))
@@ -2553,7 +2780,6 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
        }
       howto = _bfd_sparc_elf_howto_table + r_type;
 
-      /* This is a final link.  */
       r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info);
       h = NULL;
       sym = NULL;
@@ -2564,6 +2790,20 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
          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
        {
@@ -2585,14 +2825,173 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
            }
        }
 
+      if (sec != NULL && elf_discarded_section (sec))
+       {
+         /* For relocs against symbols from removed linkonce
+            sections, or sections discarded by a linker script, we
+            just want the section contents zeroed.  Avoid any
+            special processing.  */
+         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
+         rel->r_info = 0;
+         rel->r_addend = 0;
+         continue;
+       }
+
+      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_OP:
+             continue;
+
+           case R_SPARC_GOTDATA_OP_HIX22:
+           case R_SPARC_GOTDATA_OP_LOX10:
+             r_type = (r_type == R_SPARC_GOTDATA_OP_HIX22
+                       ? R_SPARC_GOT22
+                       : R_SPARC_GOT10);
+             howto = _bfd_sparc_elf_howto_table + r_type;
+             /* Fall through.  */
+
+           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_OP_HIX22:
+       case R_SPARC_GOTDATA_OP_LOX10:
+         if (SYMBOL_REFERENCES_LOCAL (info, h))
+           r_type = (r_type == R_SPARC_GOTDATA_OP_HIX22
+                     ? R_SPARC_GOTDATA_HIX22
+                     : R_SPARC_GOTDATA_LOX10);
+         else
+           r_type = (r_type == R_SPARC_GOTDATA_OP_HIX22
+                     ? R_SPARC_GOT22
+                     : R_SPARC_GOT10);
+         howto = _bfd_sparc_elf_howto_table + r_type;
+         break;
+
+       case R_SPARC_GOTDATA_OP:
+         if (SYMBOL_REFERENCES_LOCAL (info, h))
+           {
+             bfd_vma insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+             /* {ld,ldx} [%rs1 + %rs2], %rd --> add %rs1, %rs2, %rd */
+             relocation = 0x80000000 | (insn & 0x3e07c01f);
+             bfd_put_32 (output_bfd, relocation, contents + rel->r_offset);
+           }
+         continue;
+       }
+
+      switch (r_type)
+       {
+       case R_SPARC_GOTDATA_HIX22:
+       case R_SPARC_GOTDATA_LOX10:
+         relocation = gdopoff (info, relocation);
+         break;
+
        case R_SPARC_GOT10:
        case R_SPARC_GOT13:
        case R_SPARC_GOT22:
          /* Relocation is to the entry for this symbol in the global
             offset table.  */
-         if (htab->sgot == NULL)
+         if (htab->elf.sgot == NULL)
            abort ();
 
          if (h != NULL)
@@ -2605,10 +3004,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
              if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
                  || (info->shared
-                     && (info->symbolic
-                         || h->dynindx == -1
-                         || h->forced_local)
-                     && h->def_regular))
+                     && SYMBOL_REFERENCES_LOCAL (info, h)))
                {
                  /* This is actually a static link, or it is a
                     -Bsymbolic link and the symbol is defined
@@ -2627,7 +3023,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                  else
                    {
                      SPARC_ELF_PUT_WORD (htab, output_bfd, relocation,
-                                         htab->sgot->contents + off);
+                                         htab->elf.sgot->contents + off);
                      h->got.offset |= 1;
                    }
                }
@@ -2656,25 +3052,25 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
                      /* We need to generate a R_SPARC_RELATIVE reloc
                         for the dynamic linker.  */
-                     s = htab->srelgot;
+                     s = htab->elf.srelgot;
                      BFD_ASSERT (s != NULL);
 
-                     outrel.r_offset = (htab->sgot->output_section->vma
-                                        + htab->sgot->output_offset
+                     outrel.r_offset = (htab->elf.sgot->output_section->vma
+                                        + htab->elf.sgot->output_offset
                                         + off);
                      outrel.r_info = SPARC_ELF_R_INFO (htab, NULL,
                                                        0, R_SPARC_RELATIVE);
                      outrel.r_addend = relocation;
                      relocation = 0;
-                     SPARC_ELF_APPEND_RELA (htab, output_bfd, s, &outrel);
+                     sparc_elf_append_rela (output_bfd, s, &outrel);
                    }
 
                  SPARC_ELF_PUT_WORD (htab, output_bfd, relocation,
-                                     htab->sgot->contents + off);
+                                     htab->elf.sgot->contents + off);
                  local_got_offsets[r_symndx] |= 1;
                }
            }
-         relocation = htab->sgot->output_offset + off - got_base;
+         relocation = htab->elf.sgot->output_offset + off - got_base;
          break;
 
        case R_SPARC_PLT32:
@@ -2705,12 +3101,15 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
              if (h == NULL)
                break;
            }
+         /* PR 7027: We need similar behaviour for 64-bit binaries.  */ 
+         else if (r_type == R_SPARC_WPLT30 && h == NULL)
+           break;
          else
            {
              BFD_ASSERT (h != NULL);
            }
 
-         if (h->plt.offset == (bfd_vma) -1 || htab->splt == NULL)
+         if (h->plt.offset == (bfd_vma) -1 || htab->elf.splt == NULL)
            {
              /* We didn't make a PLT entry for this symbol.  This
                 happens when statically linking PIC code, or when
@@ -2718,8 +3117,8 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
              break;
            }
 
-         relocation = (htab->splt->output_section->vma
-                       + htab->splt->output_offset
+         relocation = (htab->elf.splt->output_section->vma
+                       + htab->elf.splt->output_offset
                        + h->plt.offset);
          unresolved_reloc = FALSE;
          if (r_type == R_SPARC_PLT32 || r_type == R_SPARC_PLT64)
@@ -2773,11 +3172,8 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
        case R_SPARC_L44:
        case R_SPARC_UA64:
        r_sparc_plt32:
-         /* r_symndx will be zero only for relocs against symbols
-            from removed linkonce sections, or sections discarded by
-            a linker script.  */
-         if (r_symndx == 0
-             || (input_section->flags & SEC_ALLOC) == 0)
+         if ((input_section->flags & SEC_ALLOC) == 0
+             || is_vxworks_tls)
            break;
 
          if ((info->shared
@@ -2785,10 +3181,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                   || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
                   || h->root.type != bfd_link_hash_undefweak)
               && (! howto->pc_relative
-                  || (h != NULL
-                      && h->dynindx != -1
-                      && (! info->symbolic
-                          || !h->def_regular))))
+                  || !SYMBOL_CALLS_LOCAL (info, h)))
              || (!info->shared
                  && h != NULL
                  && h->dynindx != -1
@@ -2865,8 +3258,11 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                memset (&outrel, 0, sizeof outrel);
              /* h->dynindx may be -1 if the symbol was marked to
                 become local.  */
-             else if (h != NULL && ! is_plt
-                      && ((! info->symbolic && h->dynindx != -1)
+             else if (h != NULL &&
+                      h->dynindx != -1
+                      && (! is_plt
+                          || !info->shared
+                          || !SYMBOLIC_BIND (info, h)
                           || !h->def_regular))
                {
                  BFD_ASSERT (h->dynindx != -1);
@@ -2885,8 +3281,10 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                    {
                      long indx;
 
+                     outrel.r_addend = relocation + rel->r_addend;
+
                      if (is_plt)
-                       sec = htab->splt;
+                       sec = htab->elf.splt;
 
                      if (bfd_is_abs_section (sec))
                        indx = 0;
@@ -2899,9 +3297,20 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                        {
                          asection *osec;
 
+                         /* We are turning this relocation into one
+                            against a section symbol.  It would be
+                            proper to subtract the symbol's value,
+                            osec->vma, from the emitted reloc addend,
+                            but ld.so expects buggy relocs.  */
                          osec = sec->output_section;
                          indx = elf_section_data (osec)->dynindx;
 
+                         if (indx == 0)
+                           {
+                             osec = htab->elf.text_index_section;
+                             indx = elf_section_data (osec)->dynindx;
+                           }
+
                          /* FIXME: we really should be able to link non-pic
                             shared libraries.  */
                          if (indx == 0)
@@ -2915,12 +3324,12 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                            }
                        }
 
-                     outrel.r_info = SPARC_ELF_R_INFO (htab, rel, indx, r_type);
-                     outrel.r_addend = relocation + rel->r_addend;
+                     outrel.r_info = SPARC_ELF_R_INFO (htab, rel, indx,
+                                                       r_type);
                    }
                }
 
-             SPARC_ELF_APPEND_RELA (htab, output_bfd, sreloc, &outrel);
+             sparc_elf_append_rela (output_bfd, sreloc, &outrel);
 
              /* This reloc will be computed at runtime, so there's no
                 need to do anything now.  */
@@ -3001,7 +3410,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
            }
 
        r_sparc_tlsldm:
-         if (htab->sgot == NULL)
+         if (htab->elf.sgot == NULL)
            abort ();
 
          if ((off & 1) != 0)
@@ -3011,12 +3420,13 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
              Elf_Internal_Rela outrel;
              int dr_type, indx;
 
-             if (htab->srelgot == NULL)
+             if (htab->elf.srelgot == NULL)
                abort ();
 
-             SPARC_ELF_PUT_WORD (htab, output_bfd, 0, htab->sgot->contents + off);
-             outrel.r_offset = (htab->sgot->output_section->vma
-                                + htab->sgot->output_offset + off);
+             SPARC_ELF_PUT_WORD (htab, output_bfd, 0,
+                                 htab->elf.sgot->contents + off);
+             outrel.r_offset = (htab->elf.sgot->output_section->vma
+                                + htab->elf.sgot->output_offset + off);
              indx = h && h->dynindx != -1 ? h->dynindx : 0;
              if (r_type == R_SPARC_TLS_IE_HI22
                  || r_type == R_SPARC_TLS_IE_LO10)
@@ -3028,7 +3438,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
              else
                outrel.r_addend = 0;
              outrel.r_info = SPARC_ELF_R_INFO (htab, NULL, indx, dr_type);
-             SPARC_ELF_APPEND_RELA (htab, output_bfd, htab->srelgot, &outrel);
+             sparc_elf_append_rela (output_bfd, htab->elf.srelgot, &outrel);
 
              if (r_type == R_SPARC_TLS_GD_HI22
                  || r_type == R_SPARC_TLS_GD_LO10)
@@ -3038,24 +3448,25 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                      BFD_ASSERT (! unresolved_reloc);
                      SPARC_ELF_PUT_WORD (htab, output_bfd,
                                          relocation - dtpoff_base (info),
-                                         (htab->sgot->contents + off
+                                         (htab->elf.sgot->contents + off
                                           + SPARC_ELF_WORD_BYTES (htab)));
                    }
                  else
                    {
                      SPARC_ELF_PUT_WORD (htab, output_bfd, 0,
-                                         (htab->sgot->contents + off
+                                         (htab->elf.sgot->contents + off
                                           + SPARC_ELF_WORD_BYTES (htab)));
                      outrel.r_info = SPARC_ELF_R_INFO (htab, NULL, indx,
                                                        SPARC_ELF_DTPOFF_RELOC (htab));
                      outrel.r_offset += SPARC_ELF_WORD_BYTES (htab);
-                     SPARC_ELF_APPEND_RELA (htab, output_bfd, htab->srelgot, &outrel);
+                     sparc_elf_append_rela (output_bfd, htab->elf.srelgot,
+                                            &outrel);
                    }
                }
              else if (dr_type == SPARC_ELF_DTPMOD_RELOC (htab))
                {
                  SPARC_ELF_PUT_WORD (htab, output_bfd, 0,
-                                     (htab->sgot->contents + off
+                                     (htab->elf.sgot->contents + off
                                       + SPARC_ELF_WORD_BYTES (htab)));
                }
            }
@@ -3063,7 +3474,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
          if (off >= (bfd_vma) -2)
            abort ();
 
-         relocation = htab->sgot->output_offset + off - got_base;
+         relocation = htab->elf.sgot->output_offset + off - got_base;
          unresolved_reloc = FALSE;
          howto = _bfd_sparc_elf_howto_table + r_type;
          break;
@@ -3118,7 +3529,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                                    + rel->r_addend;
                }
 
-             SPARC_ELF_APPEND_RELA (htab, output_bfd, sreloc, &outrel);
+             sparc_elf_append_rela (output_bfd, sreloc, &outrel);
              continue;
            }
          relocation = tpoff (info, relocation);
@@ -3349,12 +3760,15 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
          r = bfd_reloc_ok;
        }
-      else if (r_type == R_SPARC_HIX22)
+      else if (r_type == R_SPARC_HIX22
+              || r_type == R_SPARC_GOTDATA_HIX22)
        {
          bfd_vma x;
 
          relocation += rel->r_addend;
-         relocation = relocation ^ MINUS_ONE;
+         if (r_type == R_SPARC_HIX22
+             || (bfd_signed_vma) relocation < 0)
+           relocation = relocation ^ MINUS_ONE;
 
          x = bfd_get_32 (input_bfd, contents + rel->r_offset);
          x = (x & ~(bfd_vma) 0x3fffff) | ((relocation >> 10) & 0x3fffff);
@@ -3365,12 +3779,17 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                                  bfd_arch_bits_per_address (input_bfd),
                                  relocation);
        }
-      else if (r_type == R_SPARC_LOX10)
+      else if (r_type == R_SPARC_LOX10
+              || r_type == R_SPARC_GOTDATA_LOX10)
        {
          bfd_vma x;
 
          relocation += rel->r_addend;
-         relocation = (relocation & 0x3ff) | 0x1c00;
+         if (r_type == R_SPARC_LOX10
+             || (bfd_signed_vma) relocation < 0)
+           relocation = (relocation & 0x3ff) | 0x1c00;
+         else
+           relocation = (relocation & 0x3ff);
 
          x = bfd_get_32 (input_bfd, contents + rel->r_offset);
          x = (x & ~(bfd_vma) 0x1fff) | relocation;
@@ -3470,10 +3889,12 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
        }
 
       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)
@@ -3554,6 +3975,8 @@ sparc_vxworks_build_plt_entry (bfd *output_bfd, struct bfd_link_info *info,
   Elf_Internal_Rela rela;
 
   htab = _bfd_sparc_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
   if (info->shared)
     {
       plt_entry = sparc_vxworks_shared_plt_entry;
@@ -3569,33 +3992,33 @@ sparc_vxworks_build_plt_entry (bfd *output_bfd, struct bfd_link_info *info,
 
   /* Fill in the entry in the procedure linkage table.  */
   bfd_put_32 (output_bfd, plt_entry[0] + ((got_base + got_offset) >> 10),
-             htab->splt->contents + plt_offset);
+             htab->elf.splt->contents + plt_offset);
   bfd_put_32 (output_bfd, plt_entry[1] + ((got_base + got_offset) & 0x3ff),
-             htab->splt->contents + plt_offset + 4);
+             htab->elf.splt->contents + plt_offset + 4);
   bfd_put_32 (output_bfd, plt_entry[2],
-             htab->splt->contents + plt_offset + 8);
+             htab->elf.splt->contents + plt_offset + 8);
   bfd_put_32 (output_bfd, plt_entry[3],
-             htab->splt->contents + plt_offset + 12);
+             htab->elf.splt->contents + plt_offset + 12);
   bfd_put_32 (output_bfd, plt_entry[4],
-             htab->splt->contents + plt_offset + 16);
+             htab->elf.splt->contents + plt_offset + 16);
   bfd_put_32 (output_bfd, plt_entry[5] + (plt_index >> 10),
-             htab->splt->contents + plt_offset + 20);
+             htab->elf.splt->contents + plt_offset + 20);
   /* PC-relative displacement for a branch to the start of
      the PLT section.  */
   bfd_put_32 (output_bfd, plt_entry[6] + (((-plt_offset - 24) >> 2)
                                          & 0x003fffff),
-             htab->splt->contents + plt_offset + 24);
+             htab->elf.splt->contents + plt_offset + 24);
   bfd_put_32 (output_bfd, plt_entry[7] + (plt_index & 0x3ff),
-             htab->splt->contents + plt_offset + 28);
+             htab->elf.splt->contents + plt_offset + 28);
 
   /* Fill in the .got.plt entry, pointing initially at the
      second half of the PLT entry.  */
-  BFD_ASSERT (htab->sgotplt != NULL);
+  BFD_ASSERT (htab->elf.sgotplt != NULL);
   bfd_put_32 (output_bfd,
-             htab->splt->output_section->vma
-             + htab->splt->output_offset
+             htab->elf.splt->output_section->vma
+             + htab->elf.splt->output_offset
              + plt_offset + 20,
-             htab->sgotplt->contents + got_offset);
+             htab->elf.sgotplt->contents + got_offset);
 
   /* Add relocations to .rela.plt.unloaded.  */
   if (!info->shared)
@@ -3604,8 +4027,8 @@ sparc_vxworks_build_plt_entry (bfd *output_bfd, struct bfd_link_info *info,
             + (2 + 3 * plt_index) * sizeof (Elf32_External_Rela));
 
       /* Relocate the initial sethi.  */
-      rela.r_offset = (htab->splt->output_section->vma
-                      + htab->splt->output_offset
+      rela.r_offset = (htab->elf.splt->output_section->vma
+                      + htab->elf.splt->output_offset
                       + plt_offset);
       rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_HI22);
       rela.r_addend = got_offset;
@@ -3619,8 +4042,8 @@ sparc_vxworks_build_plt_entry (bfd *output_bfd, struct bfd_link_info *info,
       loc += sizeof (Elf32_External_Rela);
 
       /* Relocate the .got.plt entry.  */
-      rela.r_offset = (htab->sgotplt->output_section->vma
-                      + htab->sgotplt->output_offset
+      rela.r_offset = (htab->elf.sgotplt->output_section->vma
+                      + htab->elf.sgotplt->output_offset
                       + got_offset);
       rela.r_info = ELF32_R_INFO (htab->elf.hplt->indx, R_SPARC_32);
       rela.r_addend = plt_offset + 20;
@@ -3637,11 +4060,12 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
                                      struct elf_link_hash_entry *h,
                                      Elf_Internal_Sym *sym)
 {
-  bfd *dynobj;
   struct _bfd_sparc_elf_link_hash_table *htab;
+  const struct elf_backend_data *bed;
 
   htab = _bfd_sparc_elf_hash_table (info);
-  dynobj = htab->elf.dynobj;
+  BFD_ASSERT (htab != NULL);
+  bed = get_elf_backend_data (output_bfd);
 
   if (h->plt.offset != (bfd_vma) -1)
     {
@@ -3652,13 +4076,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->splt;
-      srela = htab->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)
@@ -3677,33 +4109,77 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
          /* On VxWorks, the relocation points to the .got.plt entry,
             not the .plt entry.  */
-         rela.r_offset = (htab->sgotplt->output_section->vma
-                          + htab->sgotplt->output_offset
+         rela.r_offset = (htab->elf.sgotplt->output_section->vma
+                          + 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.
@@ -3711,18 +4187,8 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
         thus .plt[4] has corresponding .rela.plt[0] and so on.  */
 
       loc = srela->contents;
-#ifdef BFD64
-      if (ABI_64_P (output_bfd))
-       {
-         loc += rela_index * sizeof (Elf64_External_Rela);
-         bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
-       }
-      else
-#endif
-       {
-         loc += rela_index * sizeof (Elf32_External_Rela);
-         bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
-       }
+      loc += rela_index * bed->s->sizeof_rela;
+      bed->s->swap_reloca_out (output_bfd, &rela, loc);
 
       if (!h->def_regular)
        {
@@ -3748,8 +4214,8 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
       /* This symbol has an entry in the GOT.  Set it up.  */
 
-      sgot = htab->sgot;
-      srela = htab->srelgot;
+      sgot = htab->elf.sgot;
+      srela = htab->elf.srelgot;
       BFD_ASSERT (sgot != NULL && srela != NULL);
 
       rela.r_offset = (sgot->output_section->vma
@@ -3761,12 +4227,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
-         && (info->symbolic || h->dynindx == -1)
+      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);
@@ -3779,7 +4262,7 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
       SPARC_ELF_PUT_WORD (htab, output_bfd, 0,
                          sgot->contents + (h->got.offset & ~(bfd_vma) 1));
-      SPARC_ELF_APPEND_RELA (htab, output_bfd, srela, &rela);
+      sparc_elf_append_rela (output_bfd, srela, &rela);
     }
 
   if (h->needs_copy)
@@ -3799,15 +4282,16 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
                       + h->root.u.def.section->output_offset);
       rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, R_SPARC_COPY);
       rela.r_addend = 0;
-      SPARC_ELF_APPEND_RELA (htab, output_bfd, s, &rela);
+      sparc_elf_append_rela (output_bfd, s, &rela);
     }
 
   /* 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;
@@ -3815,104 +4299,67 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
 /* Finish up the dynamic sections.  */
 
-#ifdef BFD64
 static bfd_boolean
-sparc64_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
-                   bfd *dynobj, asection *sdyn,
-                   asection *splt ATTRIBUTE_UNUSED)
+sparc_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
+                 bfd *dynobj, asection *sdyn,
+                 asection *splt ATTRIBUTE_UNUSED)
 {
-  Elf64_External_Dyn *dyncon, *dynconend;
-  int stt_regidx = -1;
-
-  dyncon = (Elf64_External_Dyn *) sdyn->contents;
-  dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->size);
-  for (; dyncon < dynconend; dyncon++)
-    {
-      Elf_Internal_Dyn dyn;
-      const char *name;
-      bfd_boolean size;
-
-      bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn);
-
-      switch (dyn.d_tag)
-       {
-       case DT_PLTGOT:   name = ".plt"; size = FALSE; break;
-       case DT_PLTRELSZ: name = ".rela.plt"; size = TRUE; break;
-       case DT_JMPREL:   name = ".rela.plt"; size = FALSE; break;
-       case DT_SPARC_REGISTER:
-         if (stt_regidx == -1)
-           {
-             stt_regidx =
-               _bfd_elf_link_lookup_local_dynindx (info, output_bfd, -1);
-             if (stt_regidx == -1)
-               return FALSE;
-           }
-         dyn.d_un.d_val = stt_regidx++;
-         bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
-         /* fallthrough */
-       default:          name = NULL; size = FALSE; break;
-       }
-
-      if (name != NULL)
-       {
-         asection *s;
-
-         s = bfd_get_section_by_name (output_bfd, name);
-         if (s == NULL)
-           dyn.d_un.d_val = 0;
-         else
-           {
-             if (! size)
-               dyn.d_un.d_ptr = s->vma;
-             else
-               dyn.d_un.d_val = s->size;
-           }
-         bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
-       }
-    }
-  return TRUE;
-}
-#endif
-
-static bfd_boolean
-sparc32_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
-                   bfd *dynobj, asection *sdyn,
-                   asection *splt ATTRIBUTE_UNUSED)
-{
-  Elf32_External_Dyn *dyncon, *dynconend;
   struct _bfd_sparc_elf_link_hash_table *htab;
+  const struct elf_backend_data *bed;
+  bfd_byte *dyncon, *dynconend;
+  size_t dynsize;
+  int stt_regidx = -1;
+  bfd_boolean abi_64_p;
 
   htab = _bfd_sparc_elf_hash_table (info);
-  dyncon = (Elf32_External_Dyn *) sdyn->contents;
-  dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size);
-  for (; dyncon < dynconend; dyncon++)
+  BFD_ASSERT (htab != NULL);
+  bed = get_elf_backend_data (output_bfd);
+  dynsize = bed->s->sizeof_dyn;
+  dynconend = sdyn->contents + sdyn->size;
+  abi_64_p = ABI_64_P (output_bfd);
+  for (dyncon = sdyn->contents; dyncon < dynconend; dyncon += dynsize)
     {
       Elf_Internal_Dyn dyn;
       const char *name;
       bfd_boolean size;
 
-      bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
+      bed->s->swap_dyn_in (dynobj, dyncon, &dyn);
 
       if (htab->is_vxworks && dyn.d_tag == DT_RELASZ)
        {
          /* On VxWorks, DT_RELASZ should not include the relocations
             in .rela.plt.  */
-         if (htab->srelplt)
+         if (htab->elf.srelplt)
            {
-             dyn.d_un.d_val -= htab->srelplt->size;
-             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             dyn.d_un.d_val -= htab->elf.srelplt->size;
+             bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
            }
        }
       else if (htab->is_vxworks && dyn.d_tag == DT_PLTGOT)
        {
          /* On VxWorks, DT_PLTGOT should point to the start of the GOT,
             not to the start of the PLT.  */
-         if (htab->sgotplt)
+         if (htab->elf.sgotplt)
+           {
+             dyn.d_un.d_val = (htab->elf.sgotplt->output_section->vma
+                               + htab->elf.sgotplt->output_offset);
+             bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
+           }
+       }
+      else if (htab->is_vxworks
+              && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
+       bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
+      else if (abi_64_p && dyn.d_tag == DT_SPARC_REGISTER)
+       {
+         if (stt_regidx == -1)
            {
-             dyn.d_un.d_val = (htab->sgotplt->output_section->vma
-                               + htab->sgotplt->output_offset);
-             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             stt_regidx =
+               _bfd_elf_link_lookup_local_dynindx (info, output_bfd, -1);
+             if (stt_regidx == -1)
+               return FALSE;
            }
+         dyn.d_un.d_val = stt_regidx++;
+         bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
        }
       else
        {
@@ -3921,7 +4368,7 @@ sparc32_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
            case DT_PLTGOT:   name = ".plt"; size = FALSE; break;
            case DT_PLTRELSZ: name = ".rela.plt"; size = TRUE; break;
            case DT_JMPREL:   name = ".rela.plt"; size = FALSE; break;
-           default:      name = NULL; size = FALSE; break;
+           default:          name = NULL; size = FALSE; break;
            }
 
          if (name != NULL)
@@ -3938,7 +4385,7 @@ sparc32_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
                  else
                    dyn.d_un.d_val = s->size;
                }
-             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
            }
        }
     }
@@ -3957,6 +4404,7 @@ sparc_vxworks_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
   bfd_byte *loc;
 
   htab = _bfd_sparc_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
 
   /* Calculate the absolute value of _GLOBAL_OFFSET_TABLE_.  */
   got_base = (htab->elf.hgot->root.u.def.section->output_section->vma
@@ -3966,25 +4414,25 @@ sparc_vxworks_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
   /* Install the initial PLT entry.  */
   bfd_put_32 (output_bfd,
              sparc_vxworks_exec_plt0_entry[0] + ((got_base + 8) >> 10),
-             htab->splt->contents);
+             htab->elf.splt->contents);
   bfd_put_32 (output_bfd,
              sparc_vxworks_exec_plt0_entry[1] + ((got_base + 8) & 0x3ff),
-             htab->splt->contents + 4);
+             htab->elf.splt->contents + 4);
   bfd_put_32 (output_bfd,
              sparc_vxworks_exec_plt0_entry[2],
-             htab->splt->contents + 8);
+             htab->elf.splt->contents + 8);
   bfd_put_32 (output_bfd,
              sparc_vxworks_exec_plt0_entry[3],
-             htab->splt->contents + 12);
+             htab->elf.splt->contents + 12);
   bfd_put_32 (output_bfd,
              sparc_vxworks_exec_plt0_entry[4],
-             htab->splt->contents + 16);
+             htab->elf.splt->contents + 16);
 
   loc = htab->srelplt2->contents;
 
   /* Add an unloaded relocation for the initial entry's "sethi".  */
-  rela.r_offset = (htab->splt->output_section->vma
-                  + htab->splt->output_offset);
+  rela.r_offset = (htab->elf.splt->output_section->vma
+                  + htab->elf.splt->output_offset);
   rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_HI22);
   rela.r_addend = 8;
   bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
@@ -4032,9 +4480,26 @@ sparc_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info)
   unsigned int i;
 
   htab = _bfd_sparc_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
   for (i = 0; i < ARRAY_SIZE (sparc_vxworks_shared_plt0_entry); i++)
     bfd_put_32 (output_bfd, sparc_vxworks_shared_plt0_entry[i],
-               htab->splt->contents + i * 4);
+               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
@@ -4045,6 +4510,7 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i
   struct _bfd_sparc_elf_link_hash_table *htab;
 
   htab = _bfd_sparc_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
   dynobj = htab->elf.dynobj;
 
   sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
@@ -4052,20 +4518,12 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       asection *splt;
-      bfd_boolean ret;
 
       splt = bfd_get_section_by_name (dynobj, ".plt");
       BFD_ASSERT (splt != NULL && sdyn != NULL);
 
-#ifdef BFD64
-      if (ABI_64_P (output_bfd))
-       ret = sparc64_finish_dyn (output_bfd, info, dynobj, sdyn, splt);
-      else
-#endif
-       ret = sparc32_finish_dyn (output_bfd, info, dynobj, sdyn, splt);
-
-      if (ret != TRUE)
-       return ret;
+      if (!sparc_finish_dyn (output_bfd, info, dynobj, sdyn, splt))
+       return FALSE;
 
       /* Initialize the contents of the .plt section.  */
       if (splt->size > 0)
@@ -4087,24 +4545,28 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i
        }
 
       elf_section_data (splt->output_section)->this_hdr.sh_entsize
-       = htab->plt_entry_size;
+       = (htab->is_vxworks || !ABI_64_P (output_bfd))
+         ? 0 : htab->plt_entry_size;
     }
 
   /* Set the first entry in the global offset table to the address of
      the dynamic section.  */
-  if (htab->sgot && htab->sgot->size > 0)
+  if (htab->elf.sgot && htab->elf.sgot->size > 0)
     {
       bfd_vma val = (sdyn ?
                     sdyn->output_section->vma + sdyn->output_offset :
                     0);
 
-      SPARC_ELF_PUT_WORD (htab, output_bfd, val, htab->sgot->contents);
+      SPARC_ELF_PUT_WORD (htab, output_bfd, val, htab->elf.sgot->contents);
     }
 
-  if (htab->sgot)
-    elf_section_data (htab->sgot->output_section)->this_hdr.sh_entsize =
+  if (htab->elf.sgot)
+    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;
 }
 
This page took 0.052918 seconds and 4 git commands to generate.