ChangeLog rotatation and copyright year update
[deliverable/binutils-gdb.git] / bfd / elfxx-sparc.c
index 73284db6454f5071b83d41df1607543eeba5524c..66f73c1a7c8650991cff8c1b870f293a95dfe90f 100644 (file)
@@ -1,5 +1,5 @@
 /* SPARC-specific support for ELF
-   Copyright 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -31,6 +31,8 @@
 #include "opcode/sparc.h"
 #include "elfxx-sparc.h"
 #include "elf-vxworks.h"
+#include "objalloc.h"
+#include "hashtab.h"
 
 /* In case we're on a 32-bit machine, construct a 64-bit "-1" value.  */
 #define MINUS_ONE (~ (bfd_vma) 0)
@@ -50,7 +52,7 @@
 
 static bfd_reloc_status_type
 init_insn_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
-                PTR data, asection *input_section, bfd *output_bfd,
+                void * data, asection *input_section, bfd *output_bfd,
                 bfd_vma *prelocation, bfd_vma *pinsn)
 {
   bfd_vma relocation;
@@ -94,7 +96,7 @@ static bfd_reloc_status_type
 sparc_elf_notsup_reloc (bfd *abfd ATTRIBUTE_UNUSED,
                        arelent *reloc_entry ATTRIBUTE_UNUSED,
                        asymbol *symbol ATTRIBUTE_UNUSED,
-                       PTR data ATTRIBUTE_UNUSED,
+                       void * data ATTRIBUTE_UNUSED,
                        asection *input_section ATTRIBUTE_UNUSED,
                        bfd *output_bfd ATTRIBUTE_UNUSED,
                        char **error_message ATTRIBUTE_UNUSED)
@@ -106,7 +108,7 @@ sparc_elf_notsup_reloc (bfd *abfd ATTRIBUTE_UNUSED,
 
 static bfd_reloc_status_type
 sparc_elf_wdisp16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
-                        PTR data, asection *input_section, bfd *output_bfd,
+                        void * data, asection *input_section, bfd *output_bfd,
                         char **error_message ATTRIBUTE_UNUSED)
 {
   bfd_vma relocation;
@@ -129,11 +131,39 @@ sparc_elf_wdisp16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
     return bfd_reloc_ok;
 }
 
+/* Handle the WDISP10 reloc.  */
+
+static bfd_reloc_status_type
+sparc_elf_wdisp10_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
+                        void * data, asection *input_section, bfd *output_bfd,
+                        char **error_message ATTRIBUTE_UNUSED)
+{
+  bfd_vma relocation;
+  bfd_vma insn;
+  bfd_reloc_status_type status;
+
+  status = init_insn_reloc (abfd, reloc_entry, symbol, data,
+                           input_section, output_bfd, &relocation, &insn);
+  if (status != bfd_reloc_other)
+    return status;
+
+  insn &= ~ (bfd_vma) 0x181fe0;
+  insn |= (((relocation >> 2) & 0x300) << 11)
+         | (((relocation >> 2) & 0xff) << 5);
+  bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
+
+  if ((bfd_signed_vma) relocation < - 0x1000
+      || (bfd_signed_vma) relocation > 0xfff)
+    return bfd_reloc_overflow;
+  else
+    return bfd_reloc_ok;
+}
+
 /* Handle the HIX22 reloc.  */
 
 static bfd_reloc_status_type
 sparc_elf_hix22_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
-                      PTR data, asection *input_section, bfd *output_bfd,
+                      void * data, asection *input_section, bfd *output_bfd,
                       char **error_message ATTRIBUTE_UNUSED)
 {
   bfd_vma relocation;
@@ -159,7 +189,7 @@ sparc_elf_hix22_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
 
 static bfd_reloc_status_type
 sparc_elf_lox10_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
-                      PTR data, asection *input_section, bfd *output_bfd,
+                      void * data, asection *input_section, bfd *output_bfd,
                       char **error_message ATTRIBUTE_UNUSED)
 {
   bfd_vma relocation;
@@ -264,7 +294,15 @@ static reloc_howto_type _bfd_sparc_elf_howto_table[] =
   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),
+  HOWTO(R_SPARC_H34,12,2,22,FALSE,0,complain_overflow_unsigned,bfd_elf_generic_reloc,"R_SPARC_H34",FALSE,0,0x003fffff,FALSE),
+  HOWTO(R_SPARC_SIZE32,0,2,32,FALSE,0,complain_overflow_bitfield,bfd_elf_generic_reloc,"R_SPARC_SIZE32",FALSE,0,0xffffffff,TRUE),
+  HOWTO(R_SPARC_SIZE64,0,4,64,FALSE,0,complain_overflow_bitfield,bfd_elf_generic_reloc,"R_SPARC_SIZE64",FALSE,0,MINUS_ONE, TRUE),
+  HOWTO(R_SPARC_WDISP10,2,2,10,TRUE, 0,complain_overflow_signed,sparc_elf_wdisp10_reloc,"R_SPARC_WDISP10",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 =
@@ -272,107 +310,269 @@ static reloc_howto_type sparc_vtentry_howto =
 static reloc_howto_type sparc_rev32_howto =
   HOWTO(R_SPARC_REV32, 0,2,32,FALSE,0,complain_overflow_bitfield,bfd_elf_generic_reloc, "R_SPARC_REV32", FALSE,0,0xffffffff,TRUE);
 
-struct elf_reloc_map {
-  bfd_reloc_code_real_type bfd_reloc_val;
-  unsigned char elf_reloc_val;
-};
-
-static const struct elf_reloc_map sparc_reloc_map[] =
-{
-  { BFD_RELOC_NONE, R_SPARC_NONE, },
-  { BFD_RELOC_16, R_SPARC_16, },
-  { BFD_RELOC_16_PCREL, R_SPARC_DISP16 },
-  { BFD_RELOC_8, R_SPARC_8 },
-  { BFD_RELOC_8_PCREL, R_SPARC_DISP8 },
-  { BFD_RELOC_CTOR, R_SPARC_64 },
-  { BFD_RELOC_32, R_SPARC_32 },
-  { BFD_RELOC_32_PCREL, R_SPARC_DISP32 },
-  { BFD_RELOC_HI22, R_SPARC_HI22 },
-  { BFD_RELOC_LO10, R_SPARC_LO10, },
-  { BFD_RELOC_32_PCREL_S2, R_SPARC_WDISP30 },
-  { BFD_RELOC_64_PCREL, R_SPARC_DISP64 },
-  { BFD_RELOC_SPARC22, R_SPARC_22 },
-  { BFD_RELOC_SPARC13, R_SPARC_13 },
-  { BFD_RELOC_SPARC_GOT10, R_SPARC_GOT10 },
-  { BFD_RELOC_SPARC_GOT13, R_SPARC_GOT13 },
-  { BFD_RELOC_SPARC_GOT22, R_SPARC_GOT22 },
-  { BFD_RELOC_SPARC_PC10, R_SPARC_PC10 },
-  { BFD_RELOC_SPARC_PC22, R_SPARC_PC22 },
-  { BFD_RELOC_SPARC_WPLT30, R_SPARC_WPLT30 },
-  { BFD_RELOC_SPARC_COPY, R_SPARC_COPY },
-  { BFD_RELOC_SPARC_GLOB_DAT, R_SPARC_GLOB_DAT },
-  { BFD_RELOC_SPARC_JMP_SLOT, R_SPARC_JMP_SLOT },
-  { BFD_RELOC_SPARC_RELATIVE, R_SPARC_RELATIVE },
-  { BFD_RELOC_SPARC_WDISP22, R_SPARC_WDISP22 },
-  { BFD_RELOC_SPARC_UA16, R_SPARC_UA16 },
-  { BFD_RELOC_SPARC_UA32, R_SPARC_UA32 },
-  { BFD_RELOC_SPARC_UA64, R_SPARC_UA64 },
-  { BFD_RELOC_SPARC_10, R_SPARC_10 },
-  { BFD_RELOC_SPARC_11, R_SPARC_11 },
-  { BFD_RELOC_SPARC_64, R_SPARC_64 },
-  { BFD_RELOC_SPARC_OLO10, R_SPARC_OLO10 },
-  { BFD_RELOC_SPARC_HH22, R_SPARC_HH22 },
-  { BFD_RELOC_SPARC_HM10, R_SPARC_HM10 },
-  { BFD_RELOC_SPARC_LM22, R_SPARC_LM22 },
-  { BFD_RELOC_SPARC_PC_HH22, R_SPARC_PC_HH22 },
-  { BFD_RELOC_SPARC_PC_HM10, R_SPARC_PC_HM10 },
-  { BFD_RELOC_SPARC_PC_LM22, R_SPARC_PC_LM22 },
-  { BFD_RELOC_SPARC_WDISP16, R_SPARC_WDISP16 },
-  { BFD_RELOC_SPARC_WDISP19, R_SPARC_WDISP19 },
-  { BFD_RELOC_SPARC_7, R_SPARC_7 },
-  { BFD_RELOC_SPARC_5, R_SPARC_5 },
-  { BFD_RELOC_SPARC_6, R_SPARC_6 },
-  { BFD_RELOC_SPARC_DISP64, R_SPARC_DISP64 },
-  { BFD_RELOC_SPARC_TLS_GD_HI22, R_SPARC_TLS_GD_HI22 },
-  { BFD_RELOC_SPARC_TLS_GD_LO10, R_SPARC_TLS_GD_LO10 },
-  { BFD_RELOC_SPARC_TLS_GD_ADD, R_SPARC_TLS_GD_ADD },
-  { BFD_RELOC_SPARC_TLS_GD_CALL, R_SPARC_TLS_GD_CALL },
-  { BFD_RELOC_SPARC_TLS_LDM_HI22, R_SPARC_TLS_LDM_HI22 },
-  { BFD_RELOC_SPARC_TLS_LDM_LO10, R_SPARC_TLS_LDM_LO10 },
-  { BFD_RELOC_SPARC_TLS_LDM_ADD, R_SPARC_TLS_LDM_ADD },
-  { BFD_RELOC_SPARC_TLS_LDM_CALL, R_SPARC_TLS_LDM_CALL },
-  { BFD_RELOC_SPARC_TLS_LDO_HIX22, R_SPARC_TLS_LDO_HIX22 },
-  { BFD_RELOC_SPARC_TLS_LDO_LOX10, R_SPARC_TLS_LDO_LOX10 },
-  { BFD_RELOC_SPARC_TLS_LDO_ADD, R_SPARC_TLS_LDO_ADD },
-  { BFD_RELOC_SPARC_TLS_IE_HI22, R_SPARC_TLS_IE_HI22 },
-  { BFD_RELOC_SPARC_TLS_IE_LO10, R_SPARC_TLS_IE_LO10 },
-  { BFD_RELOC_SPARC_TLS_IE_LD, R_SPARC_TLS_IE_LD },
-  { BFD_RELOC_SPARC_TLS_IE_LDX, R_SPARC_TLS_IE_LDX },
-  { BFD_RELOC_SPARC_TLS_IE_ADD, R_SPARC_TLS_IE_ADD },
-  { BFD_RELOC_SPARC_TLS_LE_HIX22, R_SPARC_TLS_LE_HIX22 },
-  { BFD_RELOC_SPARC_TLS_LE_LOX10, R_SPARC_TLS_LE_LOX10 },
-  { BFD_RELOC_SPARC_TLS_DTPMOD32, R_SPARC_TLS_DTPMOD32 },
-  { BFD_RELOC_SPARC_TLS_DTPMOD64, R_SPARC_TLS_DTPMOD64 },
-  { BFD_RELOC_SPARC_TLS_DTPOFF32, R_SPARC_TLS_DTPOFF32 },
-  { BFD_RELOC_SPARC_TLS_DTPOFF64, R_SPARC_TLS_DTPOFF64 },
-  { BFD_RELOC_SPARC_TLS_TPOFF32, R_SPARC_TLS_TPOFF32 },
-  { BFD_RELOC_SPARC_TLS_TPOFF64, R_SPARC_TLS_TPOFF64 },
-  { BFD_RELOC_SPARC_PLT32, R_SPARC_PLT32 },
-  { BFD_RELOC_SPARC_PLT64, R_SPARC_PLT64 },
-  { BFD_RELOC_SPARC_HIX22, R_SPARC_HIX22 },
-  { BFD_RELOC_SPARC_LOX10, R_SPARC_LOX10 },
-  { 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_VTABLE_INHERIT, R_SPARC_GNU_VTINHERIT },
-  { BFD_RELOC_VTABLE_ENTRY, R_SPARC_GNU_VTENTRY },
-  { BFD_RELOC_SPARC_REV32, R_SPARC_REV32 },
-};
-
 reloc_howto_type *
 _bfd_sparc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
                                  bfd_reloc_code_real_type code)
 {
-  unsigned int i;
-
+  /* We explicitly handle each relocation type in the switch
+     instead of using a lookup table for efficiency.  */
   switch (code)
     {
+    case BFD_RELOC_NONE:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_NONE];
+
+    case BFD_RELOC_8:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_8];
+
+    case BFD_RELOC_16:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_16];
+
+    case BFD_RELOC_32:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_32];
+
+    case BFD_RELOC_8_PCREL:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_DISP8];
+
+    case BFD_RELOC_16_PCREL:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_DISP16];
+
+    case BFD_RELOC_32_PCREL:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_DISP32];
+
+    case BFD_RELOC_32_PCREL_S2:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_WDISP30];
+
+    case BFD_RELOC_SPARC_WDISP22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_WDISP22];
+
+    case BFD_RELOC_HI22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_HI22];
+
+    case BFD_RELOC_SPARC22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_22];
+
+    case BFD_RELOC_SPARC13:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_13];
+
+    case BFD_RELOC_LO10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_LO10];
+
+    case BFD_RELOC_SPARC_GOT10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOT10];
+
+    case BFD_RELOC_SPARC_GOT13:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOT13];
+
+    case BFD_RELOC_SPARC_GOT22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOT22];
+
+    case BFD_RELOC_SPARC_PC10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_PC10];
+
+    case BFD_RELOC_SPARC_PC22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_PC22];
+
+    case BFD_RELOC_SPARC_WPLT30:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_WPLT30];
+
+    case BFD_RELOC_SPARC_COPY:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_COPY];
+
+    case BFD_RELOC_SPARC_GLOB_DAT:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GLOB_DAT];
+
+    case BFD_RELOC_SPARC_JMP_SLOT:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_JMP_SLOT];
+
+    case BFD_RELOC_SPARC_RELATIVE:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_RELATIVE];
+
+    case BFD_RELOC_SPARC_UA32:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_UA32];
+
+    case BFD_RELOC_SPARC_PLT32:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_PLT32];
+
+    case BFD_RELOC_SPARC_10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_10];
+
+    case BFD_RELOC_SPARC_11:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_11];
+
+    case BFD_RELOC_SPARC_64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_64];
+
+    case BFD_RELOC_SPARC_OLO10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_OLO10];
+
+    case BFD_RELOC_SPARC_HH22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_HH22];
+
+    case BFD_RELOC_SPARC_HM10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_HM10];
+
+    case BFD_RELOC_SPARC_LM22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_LM22];
+
+    case BFD_RELOC_SPARC_PC_HH22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_PC_HH22];
+
+    case BFD_RELOC_SPARC_PC_HM10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_PC_HM10];
+
+    case BFD_RELOC_SPARC_PC_LM22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_PC_LM22];
+
+    case BFD_RELOC_SPARC_WDISP16:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_WDISP16];
+
+    case BFD_RELOC_SPARC_WDISP19:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_WDISP19];
+
+    case BFD_RELOC_SPARC_7:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_7];
+
+    case BFD_RELOC_SPARC_5:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_5];
+
+    case BFD_RELOC_SPARC_6:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_6];
+
+    case BFD_RELOC_SPARC_DISP64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_DISP64];
+
+    case BFD_RELOC_SPARC_PLT64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_PLT64];
+
+    case BFD_RELOC_SPARC_HIX22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_HIX22];
+
+    case BFD_RELOC_SPARC_LOX10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_LOX10];
+
+    case BFD_RELOC_SPARC_H44:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_H44];
+
+    case BFD_RELOC_SPARC_M44:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_M44];
+
+    case BFD_RELOC_SPARC_L44:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_L44];
+
+    case BFD_RELOC_SPARC_REGISTER:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_REGISTER];
+
+    case BFD_RELOC_SPARC_UA64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_UA64];
+
+    case BFD_RELOC_SPARC_UA16:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_UA16];
+
+    case BFD_RELOC_SPARC_TLS_GD_HI22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_GD_HI22];
+
+    case BFD_RELOC_SPARC_TLS_GD_LO10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_GD_LO10];
+
+    case BFD_RELOC_SPARC_TLS_GD_ADD:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_GD_ADD];
+
+    case BFD_RELOC_SPARC_TLS_GD_CALL:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_GD_CALL];
+
+    case BFD_RELOC_SPARC_TLS_LDM_HI22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LDM_HI22];
+
+    case BFD_RELOC_SPARC_TLS_LDM_LO10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LDM_LO10];
+
+    case BFD_RELOC_SPARC_TLS_LDM_ADD:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LDM_ADD];
+
+    case BFD_RELOC_SPARC_TLS_LDM_CALL:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LDM_CALL];
+
+    case BFD_RELOC_SPARC_TLS_LDO_HIX22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LDO_HIX22];
+
+    case BFD_RELOC_SPARC_TLS_LDO_LOX10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LDO_LOX10];
+
+    case BFD_RELOC_SPARC_TLS_LDO_ADD:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LDO_ADD];
+
+    case BFD_RELOC_SPARC_TLS_IE_HI22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_IE_HI22];
+
+    case BFD_RELOC_SPARC_TLS_IE_LO10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_IE_LO10];
+
+    case BFD_RELOC_SPARC_TLS_IE_LD:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_IE_LD];
+
+    case BFD_RELOC_SPARC_TLS_IE_LDX:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_IE_LDX];
+
+    case BFD_RELOC_SPARC_TLS_IE_ADD:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_IE_ADD];
+
+    case BFD_RELOC_SPARC_TLS_LE_HIX22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LE_HIX22];
+
+    case BFD_RELOC_SPARC_TLS_LE_LOX10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_LE_LOX10];
+
+    case BFD_RELOC_SPARC_TLS_DTPMOD32:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_DTPMOD32];
+
+    case BFD_RELOC_SPARC_TLS_DTPMOD64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_DTPMOD64];
+
+    case BFD_RELOC_SPARC_TLS_DTPOFF32:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_DTPOFF32];
+
+    case BFD_RELOC_SPARC_TLS_DTPOFF64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_DTPOFF64];
+
+    case BFD_RELOC_SPARC_TLS_TPOFF32:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_TPOFF32];
+
+    case BFD_RELOC_SPARC_TLS_TPOFF64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_TLS_TPOFF64];
+
+    case BFD_RELOC_SPARC_GOTDATA_HIX22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOTDATA_HIX22];
+
+    case BFD_RELOC_SPARC_GOTDATA_LOX10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOTDATA_LOX10];
+
+    case BFD_RELOC_SPARC_GOTDATA_OP_HIX22:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOTDATA_OP_HIX22];
+
+    case BFD_RELOC_SPARC_GOTDATA_OP_LOX10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOTDATA_OP_LOX10];
+
+    case BFD_RELOC_SPARC_GOTDATA_OP:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_GOTDATA_OP];
+
+    case BFD_RELOC_SPARC_H34:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_H34];
+
+    case BFD_RELOC_SPARC_SIZE32:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_SIZE32];
+
+    case BFD_RELOC_SPARC_SIZE64:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_SIZE64];
+
+    case BFD_RELOC_SPARC_WDISP10:
+      return &_bfd_sparc_elf_howto_table[R_SPARC_WDISP10];
+
+    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;
 
@@ -383,14 +583,7 @@ _bfd_sparc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       return &sparc_rev32_howto;
 
     default:
-      for (i = 0;
-          i < sizeof (sparc_reloc_map) / sizeof (struct elf_reloc_map);
-          i++)
-       {
-         if (sparc_reloc_map[i].bfd_reloc_val == code)
-           return (_bfd_sparc_elf_howto_table
-                   + (int) sparc_reloc_map[i].elf_reloc_val);
-       }
+      break;
     }
     bfd_set_error (bfd_error_bad_value);
     return NULL;
@@ -425,6 +618,12 @@ _bfd_sparc_elf_info_to_howto_ptr (unsigned int r_type)
 {
   switch (r_type)
     {
+    case R_SPARC_JMP_IREL:
+      return &sparc_jmp_irel_howto;
+
+    case R_SPARC_IRELATIVE:
+      return &sparc_irelative_howto;
+
     case R_SPARC_GNU_VTINHERIT:
       return &sparc_vtinherit_howto;
 
@@ -831,6 +1030,93 @@ 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;
+}
+
+/* Destroy a SPARC ELF linker hash table.  */
+
+static void
+_bfd_sparc_elf_link_hash_table_free (bfd *obfd)
+{
+  struct _bfd_sparc_elf_link_hash_table *htab
+    = (struct _bfd_sparc_elf_link_hash_table *) obfd->link.hash;
+
+  if (htab->loc_hash_table)
+    htab_delete (htab->loc_hash_table);
+  if (htab->loc_hash_memory)
+    objalloc_free ((struct objalloc *) htab->loc_hash_memory);
+  _bfd_elf_link_hash_table_free (obfd);
+}
+
 /* Create a SPARC ELF linker hash table.  */
 
 struct bfd_link_hash_table *
@@ -857,6 +1143,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
       ret->bytes_per_rela = sizeof (Elf64_External_Rela);
       ret->dynamic_interpreter = ELF64_DYNAMIC_INTERPRETER;
       ret->dynamic_interpreter_size = sizeof ELF64_DYNAMIC_INTERPRETER;
+
+      ret->build_plt_entry = sparc64_plt_entry_build;
+      ret->plt_header_size = PLT64_HEADER_SIZE;
+      ret->plt_entry_size = PLT64_ENTRY_SIZE;
     }
   else
     {
@@ -872,6 +1162,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
       ret->bytes_per_rela = sizeof (Elf32_External_Rela);
       ret->dynamic_interpreter = ELF32_DYNAMIC_INTERPRETER;
       ret->dynamic_interpreter_size = sizeof ELF32_DYNAMIC_INTERPRETER;
+
+      ret->build_plt_entry = sparc32_plt_entry_build;
+      ret->plt_header_size = PLT32_HEADER_SIZE;
+      ret->plt_entry_size = PLT32_ENTRY_SIZE;
     }
 
   if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc,
@@ -882,40 +1176,19 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
       return NULL;
     }
 
-  return &ret->elf.root;
-}
-
-/* Create .got and .rela.got sections in DYNOBJ, and set up
-   shortcuts to them in our hash table.  */
-
-static bfd_boolean
-create_got_section (bfd *dynobj, struct bfd_link_info *info)
-{
-  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);
-  BFD_ASSERT (htab != NULL);
-
-  htab->sgot = bfd_get_section_by_name (dynobj, ".got");
-  BFD_ASSERT (htab->sgot != NULL);
-
-  htab->srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-  if (htab->srelgot == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->srelgot,
-                                     htab->word_align_power))
-    return FALSE;
-
-  if (htab->is_vxworks)
+  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)
     {
-      htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
-      if (!htab->sgotplt)
-       return FALSE;
+      _bfd_sparc_elf_link_hash_table_free (abfd);
+      return NULL;
     }
+  ret->elf.root.hash_table_free = _bfd_sparc_elf_link_hash_table_free;
 
-  return TRUE;
+  return &ret->elf.root;
 }
 
 /* Create .plt, .rela.plt, .got, .rela.got, .dynbss, and
@@ -931,17 +1204,12 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
   htab = _bfd_sparc_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
 
-  if (!htab->sgot && !create_got_section (dynobj, info))
-    return FALSE;
-
   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");
+  htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
   if (!info->shared)
-    htab->srelbss = bfd_get_section_by_name (dynobj, ".rela.bss");
+    htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss");
 
   if (htab->is_vxworks)
     {
@@ -962,29 +1230,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
@@ -1087,7 +1371,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   struct _bfd_sparc_elf_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
-  bfd_vma *local_got_offsets;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
@@ -1101,23 +1384,28 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   BFD_ASSERT (htab != NULL);
   symtab_hdr = &elf_symtab_hdr (abfd);
   sym_hashes = elf_sym_hashes (abfd);
-  local_got_offsets = elf_local_got_offsets (abfd);
 
   sreloc = NULL;
 
   if (ABI_64_P (abfd))
-    num_relocs = NUM_SHDR_ENTRIES (& elf_section_data (sec)->rel_hdr);
+    num_relocs = NUM_SHDR_ENTRIES (_bfd_elf_single_rel_hdr (sec));
   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);
@@ -1129,14 +1417,52 @@ _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];
          while (h->root.type == bfd_link_hash_indirect
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         /* PR15323, ref flags aren't set for references in the same
+            object.  */
+         h->root.non_ir_ref = 1;
+       }
+
+      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
@@ -1204,8 +1530,6 @@ _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:
                tls_type = GOT_NORMAL;
@@ -1245,7 +1569,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];
              }
 
@@ -1275,11 +1608,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;
@@ -1373,6 +1704,7 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_SPARC_WDISP22:
        case R_SPARC_WDISP19:
        case R_SPARC_WDISP16:
+       case R_SPARC_WDISP10:
        case R_SPARC_8:
        case R_SPARC_16:
        case R_SPARC_32:
@@ -1397,6 +1729,7 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_SPARC_H44:
        case R_SPARC_M44:
        case R_SPARC_L44:
+       case R_SPARC_H34:
        case R_SPARC_UA64:
          if (h != NULL)
            h->non_got_ref = 1;
@@ -1434,14 +1767,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;
@@ -1451,9 +1787,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                 section in dynobj and make room for the reloc.  */
              if (sreloc == NULL)
                {
-                 if (htab->elf.dynobj == NULL)
-                   htab->elf.dynobj = abfd;
-
                  sreloc = _bfd_elf_make_dynamic_reloc_section
                    (sec, htab->elf.dynobj, htab->word_align_power,
                     abfd, /*rela?*/ TRUE);
@@ -1473,13 +1806,8 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                     easily.  Oh well.  */
                  asection *s;
                  void *vpp;
-                 Elf_Internal_Sym *isym;
-
-                 isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-                                               abfd, r_symndx);
-                 if (isym == NULL)
-                   return FALSE;
 
+                 BFD_ASSERT (isym != NULL);
                  s = bfd_section_from_elf_index (abfd, isym->st_shndx);
                  if (s == NULL)
                    s = sec;
@@ -1549,9 +1877,46 @@ _bfd_sparc_elf_gc_mark_hook (asection *sec,
        return NULL;
       }
 
+  /* FIXME: The test here, in check_relocs and in relocate_section
+     dealing with TLS optimization, ought to be !info->executable.  */
+  if (info->shared)
+    {
+      switch (SPARC_ELF_R_TYPE (rel->r_info))
+       {
+       case R_SPARC_TLS_GD_CALL:
+       case R_SPARC_TLS_LDM_CALL:
+         /* This reloc implicitly references __tls_get_addr.  We know
+            another reloc will reference the same symbol as the one
+            on this reloc, so the real symbol and section will be
+            gc marked when processing the other reloc.  That lets
+            us handle __tls_get_addr here.  */
+         h = elf_link_hash_lookup (elf_hash_table (info), "__tls_get_addr",
+                                   FALSE, FALSE, TRUE);
+         BFD_ASSERT (h != NULL);
+         h->mark = 1;
+         if (h->u.weakdef != NULL)
+           h->u.weakdef->mark = 1;
+         sym = NULL;
+       }
+    }
+
   return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
 
+static Elf_Internal_Rela *
+sparc_elf_find_reloc_at_ofs (Elf_Internal_Rela *rel,
+                            Elf_Internal_Rela *relend,
+                            bfd_vma offset)
+{
+  while (rel < relend)
+    {
+      if (rel->r_offset == offset)
+       return rel;
+      rel++;
+    }
+  return NULL;
+}
+
 /* Update the got entry reference counts for the section being removed.  */
 bfd_boolean
 _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
@@ -1605,7 +1970,7 @@ _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
        }
 
       r_type = SPARC_ELF_R_TYPE (rel->r_info);
-      r_type = sparc_elf_tls_transition (info, abfd, r_type, h != NULL);
+      r_type = sparc_elf_tls_transition (info, abfd, r_type, h == NULL);
       switch (r_type)
        {
        case R_SPARC_TLS_LDM_HI22:
@@ -1632,8 +1997,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;
 
@@ -1655,6 +2029,7 @@ _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
        case R_SPARC_WDISP22:
        case R_SPARC_WDISP19:
        case R_SPARC_WDISP16:
+       case R_SPARC_WDISP10:
        case R_SPARC_8:
        case R_SPARC_16:
        case R_SPARC_32:
@@ -1680,6 +2055,7 @@ _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
        case R_SPARC_H44:
        case R_SPARC_M44:
        case R_SPARC_L44:
+       case R_SPARC_H34:
        case R_SPARC_UA64:
          if (info->shared)
            break;
@@ -1722,6 +2098,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* Make sure we know what is going on here.  */
   BFD_ASSERT (htab->elf.dynobj != NULL
              && (h->needs_plt
+                 || h->type == STT_GNU_IFUNC
                  || h->u.weakdef != NULL
                  || (h->def_dynamic
                      && h->ref_regular
@@ -1735,6 +2112,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
@@ -1742,11 +2120,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
@@ -1789,6 +2166,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)
     {
@@ -1805,13 +2189,6 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       return TRUE;
     }
 
-  if (h->size == 0)
-    {
-      (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"),
-                            h->root.root.string);
-      return TRUE;
-    }
-
   /* We must allocate the symbol in our .dynbss section, which will
      become part of the .bss section of the executable.  There will be
      an entry for this symbol in the .dynsym section.  The dynamic
@@ -1826,7 +2203,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      to copy the initial value out of the dynamic object and into the
      runtime process image.  We need to remember the offset into the
      .rel.bss section we are going to use.  */
-  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
     {
       htab->srelbss->size += SPARC_ELF_RELA_BYTES (htab);
       h->needs_copy = 1;
@@ -1834,14 +2211,14 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
   s = htab->sdynbss;
 
-  return _bfd_elf_adjust_dynamic_copy (h, s);
+  return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
    dynamic relocs.  */
 
 static bfd_boolean
-allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
+allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 {
   struct bfd_link_info *info;
   struct _bfd_sparc_elf_link_hash_table *htab;
@@ -1851,18 +2228,15 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
-  if (h->root.type == bfd_link_hash_warning)
-    /* When warning symbols are created, they **replace** the "real"
-       entry in the hash table, thus we never get to see the real
-       symbol in a hash traversal.  So look at it now.  */
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
   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.  */
@@ -1873,9 +2247,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)
@@ -1925,12 +2304,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)
@@ -1971,7 +2353,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.  */
@@ -1982,12 +2364,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;
@@ -2004,9 +2387,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;
 
@@ -2095,17 +2476,33 @@ 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
-readonly_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
+readonly_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 {
   struct _bfd_sparc_elf_link_hash_entry *eh;
   struct _bfd_sparc_elf_dyn_relocs *p;
 
-  if (h->root.type == bfd_link_hash_warning)
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
   eh = (struct _bfd_sparc_elf_link_hash_entry *) h;
   for (p = eh->dyn_relocs; p != NULL; p = p->next)
     {
@@ -2162,7 +2559,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
       /* Set the contents of the .interp section to the interpreter.  */
       if (info->executable)
        {
-         s = bfd_get_section_by_name (dynobj, ".interp");
+         s = bfd_get_linker_section (dynobj, ".interp");
          BFD_ASSERT (s != NULL);
          s->size = htab->dynamic_interpreter_size;
          s->contents = (unsigned char *) htab->dynamic_interpreter;
@@ -2171,7 +2568,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
 
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       bfd_signed_vma *local_got;
       bfd_signed_vma *end_local_got;
@@ -2207,6 +2604,8 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
              else if (p->count != 0)
                {
                  srel = elf_section_data (p->sec)->sreloc;
+                 if (!htab->elf.dynamic_sections_created)
+                   srel = htab->elf.irelplt;
                  srel->size += p->count * SPARC_ELF_RELA_BYTES (htab);
                  if ((p->sec->output_section->flags & SEC_READONLY) != 0)
                    info->flags |= DF_TEXTREL;
@@ -2222,8 +2621,8 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
       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)
@@ -2246,31 +2645,34 @@ _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;
 
   /* Allocate global sym .plt and .got entries, and space for global
      sym dynamic relocs.  */
-  elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info);
+  elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, 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;
     }
@@ -2283,10 +2685,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.  */
@@ -2348,7 +2751,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)
@@ -2366,8 +2769,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
       /* If any dynamic relocs apply to a read-only section,
         then we need a DT_TEXTREL entry.  */
       if ((info->flags & DF_TEXTREL) == 0)
-       elf_link_hash_traverse (&htab->elf, readonly_dynrelocs,
-                               (PTR) info);
+       elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, info);
 
       if (info->flags & DF_TEXTREL)
        {
@@ -2414,6 +2816,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
                entry->isym.st_info = ELF_ST_INFO (app_regs [reg].bind,
                                                   STT_REGISTER);
                entry->isym.st_shndx = app_regs [reg].shndx;
+               entry->isym.st_target_internal = 0;
                entry->next = NULL;
                entry->input_bfd = output_bfd;
                entry->input_indx = -1;
@@ -2490,11 +2893,31 @@ static bfd_vma
 tpoff (struct bfd_link_info *info, bfd_vma address)
 {
   struct elf_link_hash_table *htab = elf_hash_table (info);
+  const struct elf_backend_data *bed = get_elf_backend_data (info->output_bfd);
+  bfd_vma static_tls_size;
 
   /* If tls_sec is NULL, we should have signalled an error already.  */
   if (htab->tls_sec == NULL)
     return 0;
-  return address - htab->tls_size - htab->tls_sec->vma;
+
+  /* Consider special static TLS alignment requirements.  */
+  static_tls_size = BFD_ALIGN (htab->tls_size, bed->static_tls_alignment);
+  return address - static_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.  */
@@ -2540,7 +2963,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 
   rel = relocs;
   if (ABI_64_P (output_bfd))
-    num_relocs = NUM_SHDR_ENTRIES (& elf_section_data (input_section)->rel_hdr);
+    num_relocs = NUM_SHDR_ENTRIES (_bfd_elf_single_rel_hdr (input_section));
   else
     num_relocs = input_section->reloc_count;
   relend = relocs + num_relocs;
@@ -2579,15 +3002,29 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
          sym = local_syms + r_symndx;
          sec = local_sections[r_symndx];
          relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+
+         if (!info->relocatable
+             && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+           {
+             /* Relocate against local STT_GNU_IFUNC symbol.  */
+             h = elf_sparc_get_local_sym_hash (htab, input_bfd,
+                                               rel, FALSE);
+             if (h == NULL)
+               abort ();
+
+             /* Set STT_GNU_IFUNC symbol value.  */
+             h->root.u.def.value = sym->st_value;
+             h->root.u.def.section = sec;
+           }
        }
       else
        {
-         bfd_boolean warned;
+         bfd_boolean warned, ignored;
 
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h, sec, relocation,
-                                  unresolved_reloc, warned);
+                                  unresolved_reloc, warned, ignored);
          if (warned)
            {
              /* To avoid generating warning messages about truncated
@@ -2600,43 +3037,165 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
            }
        }
 
-      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 (sec != NULL && discarded_section (sec))
+       RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+                                        rel, 1, relend, howto, 0, contents);
 
       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_HIX22:
-       case R_SPARC_GOTDATA_LOX10:
        case R_SPARC_GOTDATA_OP_HIX22:
        case R_SPARC_GOTDATA_OP_LOX10:
-         /* We don't support these code transformation optimizations
-            yet, so just leave the sequence alone and treat as
-            GOT22/GOT10.  */
-         if (r_type == R_SPARC_GOTDATA_HIX22
-             || r_type == R_SPARC_GOTDATA_OP_HIX22)
-           r_type = R_SPARC_GOT22;
+         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_SPARC_GOT10;
-         /* Fall through. */
+           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)
@@ -2649,10 +3208,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 
              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
@@ -2671,7 +3227,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                  else
                    {
                      SPARC_ELF_PUT_WORD (htab, output_bfd, relocation,
-                                         htab->sgot->contents + off);
+                                         htab->elf.sgot->contents + off);
                      h->got.offset |= 1;
                    }
                }
@@ -2700,11 +3256,11 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 
                      /* 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);
@@ -2714,11 +3270,11 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                    }
 
                  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:
@@ -2749,7 +3305,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
              if (h == NULL)
                break;
            }
-         /* PR 7027: We need similar behaviour for 64-bit binaries.  */ 
+         /* PR 7027: We need similar behaviour for 64-bit binaries.  */
          else if (r_type == R_SPARC_WPLT30 && h == NULL)
            break;
          else
@@ -2757,7 +3313,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
              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
@@ -2765,8 +3321,8 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
              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)
@@ -2794,6 +3350,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
        case R_SPARC_WDISP22:
        case R_SPARC_WDISP19:
        case R_SPARC_WDISP16:
+       case R_SPARC_WDISP10:
        case R_SPARC_8:
        case R_SPARC_16:
        case R_SPARC_32:
@@ -2818,6 +3375,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
        case R_SPARC_H44:
        case R_SPARC_M44:
        case R_SPARC_L44:
+       case R_SPARC_H34:
        case R_SPARC_UA64:
        r_sparc_plt32:
          if ((input_section->flags & SEC_ALLOC) == 0
@@ -2829,10 +3387,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                   || 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
@@ -2909,8 +3464,11 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                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);
@@ -2932,7 +3490,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                      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;
@@ -3058,7 +3616,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
            }
 
        r_sparc_tlsldm:
-         if (htab->sgot == NULL)
+         if (htab->elf.sgot == NULL)
            abort ();
 
          if ((off & 1) != 0)
@@ -3068,12 +3626,13 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
              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)
@@ -3085,7 +3644,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
              else
                outrel.r_addend = 0;
              outrel.r_info = SPARC_ELF_R_INFO (htab, NULL, indx, dr_type);
-             sparc_elf_append_rela (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)
@@ -3095,25 +3654,25 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                      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 (output_bfd, htab->srelgot,
+                     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)));
                }
            }
@@ -3121,7 +3680,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
          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;
@@ -3154,7 +3713,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
          if (info->shared)
            {
              Elf_Internal_Rela outrel;
-             bfd_boolean skip, relocate = FALSE;
+             bfd_boolean skip;
 
              BFD_ASSERT (sreloc != NULL);
              skip = FALSE;
@@ -3164,7 +3723,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
              if (outrel.r_offset == (bfd_vma) -1)
                skip = TRUE;
              else if (outrel.r_offset == (bfd_vma) -2)
-               skip = TRUE, relocate = TRUE;
+               skip = TRUE;
              outrel.r_offset += (input_section->output_section->vma
                                  + input_section->output_offset);
              if (skip)
@@ -3200,6 +3759,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
          if (! info->shared
              || (r_type == R_SPARC_TLS_GD_CALL && tls_type == GOT_TLS_IE))
            {
+             Elf_Internal_Rela *rel2;
              bfd_vma insn;
 
              if (!info->shared && (h == NULL || h->dynindx == -1))
@@ -3235,7 +3795,26 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                  continue;
                }
 
-             bfd_put_32 (output_bfd, 0x9001c008, contents + rel->r_offset);
+             /* We cannot just overwrite the delay slot instruction,
+                as it might be what puts the %o0 argument to
+                __tls_get_addr into place.  So we have to transpose
+                the delay slot with the add we patch in.  */
+             insn = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+             bfd_put_32 (output_bfd, insn,
+                         contents + rel->r_offset);
+             bfd_put_32 (output_bfd, 0x9001c008,
+                         contents + rel->r_offset + 4);
+
+             rel2 = rel;
+             while ((rel2 = sparc_elf_find_reloc_at_ofs (rel2 + 1, relend,
+                                                         rel->r_offset + 4))
+                    != NULL)
+               {
+                 /* If the instruction we moved has a relocation attached to
+                    it, adjust the offset so that it will apply to the correct
+                    instruction.  */
+                 rel2->r_offset -= 4;
+               }
              continue;
            }
 
@@ -3284,11 +3863,6 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
            }
          continue;
 
-       case R_SPARC_GOTDATA_OP:
-         /* We don't support gotdata code transformation optimizations
-            yet, so simply leave the sequence as-is.  */
-         continue;
-
        case R_SPARC_TLS_IE_LD:
        case R_SPARC_TLS_IE_LDX:
          if (! info->shared && (h == NULL || h->dynindx == -1))
@@ -3323,7 +3897,9 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
         not process them.  */
       if (unresolved_reloc
          && !((input_section->flags & SEC_DEBUGGING) != 0
-              && h->def_dynamic))
+              && h->def_dynamic)
+         && _bfd_elf_section_offset (output_bfd, info, input_section,
+                                     rel->r_offset) != (bfd_vma) -1)
        (*_bfd_error_handler)
          (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
           input_bfd,
@@ -3366,6 +3942,25 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                | ((relocation >> 2) & 0x3fff));
          bfd_put_32 (input_bfd, x, contents + rel->r_offset);
 
+         r = bfd_check_overflow (howto->complain_on_overflow,
+                                 howto->bitsize, howto->rightshift,
+                                 bfd_arch_bits_per_address (input_bfd),
+                                 relocation);
+       }
+      else if (r_type == R_SPARC_WDISP10)
+       {
+         bfd_vma x;
+
+         relocation += rel->r_addend;
+         relocation -= (input_section->output_section->vma
+                        + input_section->output_offset);
+         relocation -= rel->r_offset;
+
+         x = bfd_get_32 (input_bfd, contents + rel->r_offset);
+         x |= ((((relocation >> 2) & 0x300) << 11)
+               | (((relocation >> 2) & 0xff) << 5));
+         bfd_put_32 (input_bfd, x, contents + rel->r_offset);
+
          r = bfd_check_overflow (howto->complain_on_overflow,
                                  howto->bitsize, howto->rightshift,
                                  bfd_arch_bits_per_address (input_bfd),
@@ -3412,12 +4007,15 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 
          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);
@@ -3428,12 +4026,17 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
                                  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;
@@ -3533,10 +4136,12 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
        }
 
       if (r == bfd_reloc_continue)
-       r = _bfd_final_link_relocate (howto, input_bfd, input_section,
-                                     contents, rel->r_offset,
-                                     relocation, rel->r_addend);
-
+       {
+do_relocation:
+         r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                       contents, rel->r_offset,
+                                       relocation, rel->r_addend);
+       }
       if (r != bfd_reloc_ok)
        {
          switch (r)
@@ -3548,11 +4153,13 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
              {
                const char *name;
 
-               /* The Solaris native linker silently disregards overflows. 
+               /* The Solaris native linker silently disregards overflows.
                   We don't, but this breaks stabs debugging info, whose
                   relocations are only 32-bits wide.  Ignore overflows in
                   this case and also for discarded entries.  */
-               if ((r_type == R_SPARC_32 || r_type == R_SPARC_DISP32)
+               if ((r_type == R_SPARC_32
+                    || r_type == R_SPARC_UA32
+                    || r_type == R_SPARC_DISP32)
                    && (((input_section->flags & SEC_DEBUGGING) != 0
                         && strcmp (bfd_section_name (input_bfd,
                                                      input_section),
@@ -3634,33 +4241,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)
@@ -3669,8 +4276,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;
@@ -3684,8 +4291,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;
@@ -3702,13 +4309,11 @@ _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);
   BFD_ASSERT (htab != NULL);
-  dynobj = htab->elf.dynobj;
   bed = get_elf_backend_data (output_bfd);
 
   if (h->plt.offset != (bfd_vma) -1)
@@ -3720,13 +4325,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)
@@ -3745,33 +4358,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.
@@ -3806,8 +4463,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
@@ -3819,12 +4476,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);
@@ -3848,8 +4522,8 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
       /* This symbols needs a copy reloc.  Set it up.  */
       BFD_ASSERT (h->dynindx != -1);
 
-      s = bfd_get_section_by_name (h->root.u.def.section->owner,
-                                  ".rela.bss");
+      s = bfd_get_linker_section (h->root.u.def.section->owner,
+                                 ".rela.bss");
       BFD_ASSERT (s != NULL);
 
       rela.r_offset = (h->root.u.def.value
@@ -3863,9 +4537,10 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
   /* Mark some specially defined symbols as absolute.  On VxWorks,
      _GLOBAL_OFFSET_TABLE_ is not absolute: it is relative to the
      ".got" section.  Likewise _PROCEDURE_LINKAGE_TABLE_ and ".plt".  */
-  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || (!htab->is_vxworks
-         && (h == htab->elf.hgot || h == htab->elf.hplt)))
+  if (sym != NULL
+      && (h == htab->elf.hdynamic
+         || (!htab->is_vxworks
+             && (h == htab->elf.hgot || h == htab->elf.hplt))))
     sym->st_shndx = SHN_ABS;
 
   return TRUE;
@@ -3903,9 +4578,9 @@ sparc_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
        {
          /* 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;
+             dyn.d_un.d_val -= htab->elf.srelplt->size;
              bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
            }
        }
@@ -3913,10 +4588,10 @@ sparc_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
        {
          /* 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->sgotplt->output_section->vma
-                               + htab->sgotplt->output_offset);
+             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);
            }
        }
@@ -3988,25 +4663,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);
@@ -4058,7 +4733,22 @@ sparc_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info)
 
   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
@@ -4072,13 +4762,13 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i
   BFD_ASSERT (htab != NULL);
   dynobj = htab->elf.dynobj;
 
-  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+  sdyn = bfd_get_linker_section (dynobj, ".dynamic");
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       asection *splt;
 
-      splt = bfd_get_section_by_name (dynobj, ".plt");
+      splt = htab->elf.splt;
       BFD_ASSERT (splt != NULL && sdyn != NULL);
 
       if (!sparc_finish_dyn (output_bfd, info, dynobj, sdyn, splt))
@@ -4103,26 +4793,30 @@ _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->is_vxworks || !ABI_64_P (output_bfd))
-         ? 0 : htab->plt_entry_size;
+      if (elf_section_data (splt->output_section) != NULL)
+        elf_section_data (splt->output_section)->this_hdr.sh_entsize
+          = ((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;
 }
 
@@ -4187,3 +4881,46 @@ _bfd_sparc_elf_plt_sym_val (bfd_vma i, const asection *plt, const arelent *rel)
   else
     return rel->address;
 }
+
+/* Merge backend specific data from an object file to the output
+   object file when linking.  */
+
+bfd_boolean
+_bfd_sparc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+{
+  obj_attribute *in_attr, *in_attrs;
+  obj_attribute *out_attr, *out_attrs;
+
+  if (!elf_known_obj_attributes_proc (obfd)[0].i)
+    {
+      /* This is the first object.  Copy the attributes.  */
+      _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+      /* Use the Tag_null value to indicate the attributes have been
+        initialized.  */
+      elf_known_obj_attributes_proc (obfd)[0].i = 1;
+
+      return TRUE;
+    }
+
+  in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+  out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+
+  in_attr = &in_attrs[Tag_GNU_Sparc_HWCAPS];
+  out_attr = &out_attrs[Tag_GNU_Sparc_HWCAPS];
+  
+  out_attr->i |= in_attr->i;
+  out_attr->type = 1;
+
+  in_attr = &in_attrs[Tag_GNU_Sparc_HWCAPS2];
+  out_attr = &out_attrs[Tag_GNU_Sparc_HWCAPS2];
+  
+  out_attr->i |= in_attr->i;
+  out_attr->type = 1;
+
+
+  /* Merge Tag_compatibility attributes and any common GNU ones.  */
+  _bfd_elf_merge_object_attributes (ibfd, obfd);
+
+  return TRUE;
+}
This page took 0.070714 seconds and 4 git commands to generate.