daily update
[external/binutils.git] / bfd / elfxx-sparc.c
index 7f1bc7f..9796849 100644 (file)
@@ -1,11 +1,11 @@
 /* SPARC-specific support for ELF
-   Copyright 2005 Free Software Foundation, Inc.
+   Copyright 2005, 2006, 2007 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
 
 /* This file handles functionality common to the different SPARC ABI's.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "bfdlink.h"
 #include "libbfd.h"
+#include "libiberty.h"
 #include "elf-bfd.h"
 #include "elf/sparc.h"
 #include "opcode/sparc.h"
 #include "elfxx-sparc.h"
+#include "elf-vxworks.h"
 
 /* In case we're on a 32-bit machine, construct a 64-bit "-1" value.  */
 #define MINUS_ONE (~ (bfd_vma) 0)
@@ -383,6 +387,30 @@ _bfd_sparc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 }
 
 reloc_howto_type *
+_bfd_sparc_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                                 const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0;
+       i < (sizeof (_bfd_sparc_elf_howto_table)
+           / sizeof (_bfd_sparc_elf_howto_table[0]));
+       i++)
+    if (_bfd_sparc_elf_howto_table[i].name != NULL
+       && strcasecmp (_bfd_sparc_elf_howto_table[i].name, r_name) == 0)
+      return &_bfd_sparc_elf_howto_table[i];
+
+  if (strcasecmp (sparc_vtinherit_howto.name, r_name) == 0)
+    return &sparc_vtinherit_howto;
+  if (strcasecmp (sparc_vtentry_howto.name, r_name) == 0)
+    return &sparc_vtentry_howto;
+  if (strcasecmp (sparc_rev32_howto.name, r_name) == 0)
+    return &sparc_rev32_howto;
+
+  return NULL;
+}
+
+reloc_howto_type *
 _bfd_sparc_elf_info_to_howto_ptr (unsigned int r_type)
 {
   switch (r_type)
@@ -397,7 +425,12 @@ _bfd_sparc_elf_info_to_howto_ptr (unsigned int r_type)
       return &sparc_rev32_howto;
 
     default:
-      BFD_ASSERT (r_type < (unsigned int) R_SPARC_max_std);
+      if (r_type >= (unsigned int) R_SPARC_max_std)
+       {
+         (*_bfd_error_handler) (_("invalid relocation type %d"),
+                                (int) r_type);
+         r_type = R_SPARC_NONE;
+       }
       return &_bfd_sparc_elf_howto_table[r_type];
     }
 }
@@ -480,11 +513,14 @@ struct _bfd_sparc_elf_obj_tdata
 bfd_boolean
 _bfd_sparc_elf_mkobject (bfd *abfd)
 {
-  bfd_size_type amt = sizeof (struct _bfd_sparc_elf_obj_tdata);
-  abfd->tdata.any = bfd_zalloc (abfd, amt);
   if (abfd->tdata.any == NULL)
-    return FALSE;
-  return TRUE;
+    {
+      bfd_size_type amt = sizeof (struct _bfd_sparc_elf_obj_tdata);
+      abfd->tdata.any = bfd_zalloc (abfd, amt);
+      if (abfd->tdata.any == NULL)
+       return FALSE;
+    }
+  return bfd_elf_mkobject (abfd);
 }
 
 static void
@@ -500,27 +536,20 @@ sparc_put_word_64 (bfd *bfd, bfd_vma val, void *ptr)
 }
 
 static void
-sparc_elf_append_rela_64 (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
-{
-  Elf64_External_Rela *loc64;
-
-  loc64 = (Elf64_External_Rela *) s->contents;
-  loc64 += s->reloc_count++;
-  bfd_elf64_swap_reloca_out (abfd, rel, (bfd_byte *) loc64);
-}
-
-static void
-sparc_elf_append_rela_32 (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
+sparc_elf_append_rela (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
 {
-  Elf32_External_Rela *loc32;
+  const struct elf_backend_data *bed;
+  bfd_byte *loc;
 
-  loc32 = (Elf32_External_Rela *) s->contents;
-  loc32 += s->reloc_count++;
-  bfd_elf32_swap_reloca_out (abfd, rel, (bfd_byte *) loc32);
+  bed = get_elf_backend_data (abfd);
+  loc = s->contents + (s->reloc_count++ * bed->s->sizeof_rela);
+  bed->s->swap_reloca_out (abfd, rel, loc);
 }
 
 static bfd_vma
-sparc_elf_r_info_64 (Elf_Internal_Rela *in_rel, bfd_vma index, bfd_vma type)
+sparc_elf_r_info_64 (Elf_Internal_Rela *in_rel ATTRIBUTE_UNUSED,
+                    bfd_vma index ATTRIBUTE_UNUSED,
+                    bfd_vma type ATTRIBUTE_UNUSED)
 {
   return ELF64_R_INFO (index,
                       (in_rel ?
@@ -538,7 +567,8 @@ sparc_elf_r_info_32 (Elf_Internal_Rela *in_rel ATTRIBUTE_UNUSED,
 static bfd_vma
 sparc_elf_r_symndx_64 (bfd_vma r_info)
 {
-  return ELF64_R_SYM (r_info);
+  bfd_vma r_symndx = ELF32_R_SYM (r_info);
+  return (r_symndx >> 24);
 }
 
 static bfd_vma
@@ -685,12 +715,53 @@ sparc64_plt_entry_build (bfd *output_bfd, asection *splt, bfd_vma offset,
   return index - 4;
 }
 
+/* The format of the first PLT entry in a VxWorks executable.  */
+static const bfd_vma sparc_vxworks_exec_plt0_entry[] =
+  {
+    0x05000000,        /* sethi  %hi(_GLOBAL_OFFSET_TABLE_+8), %g2 */
+    0x8410a000,        /* or     %g2, %lo(_GLOBAL_OFFSET_TABLE_+8), %g2 */
+    0xc4008000,        /* ld     [ %g2 ], %g2 */
+    0x81c08000,        /* jmp    %g2 */
+    0x01000000 /* nop */
+  };
+
+/* The format of subsequent PLT entries.  */
+static const bfd_vma sparc_vxworks_exec_plt_entry[] =
+  {
+    0x03000000,        /* sethi  %hi(_GLOBAL_OFFSET_TABLE_+f@got), %g1 */
+    0x82106000,        /* or     %g1, %lo(_GLOBAL_OFFSET_TABLE_+f@got), %g1 */
+    0xc2004000,        /* ld     [ %g1 ], %g1 */
+    0x81c04000,        /* jmp    %g1 */
+    0x01000000,        /* nop */
+    0x03000000,        /* sethi  %hi(f@pltindex), %g1 */
+    0x10800000,        /* b      _PLT_resolve */
+    0x82106000 /* or     %g1, %lo(f@pltindex), %g1 */
+  };
+
+/* The format of the first PLT entry in a VxWorks shared object.  */
+static const bfd_vma sparc_vxworks_shared_plt0_entry[] =
+  {
+    0xc405e008,        /* ld     [ %l7 + 8 ], %g2 */
+    0x81c08000,        /* jmp    %g2 */
+    0x01000000 /* nop */
+  };
+
+/* The format of subsequent PLT entries.  */
+static const bfd_vma sparc_vxworks_shared_plt_entry[] =
+  {
+    0x03000000,        /* sethi  %hi(f@got), %g1 */
+    0x82106000,        /* or     %g1, %lo(f@got), %g1 */
+    0xc205c001,        /* ld     [ %l7 + %g1 ], %g1 */
+    0x81c04000,        /* jmp    %g1 */
+    0x01000000,        /* nop */
+    0x03000000,        /* sethi  %hi(f@pltindex), %g1 */
+    0x10800000,        /* b      _PLT_resolve */
+    0x82106000 /* or     %g1, %lo(f@pltindex), %g1 */
+  };
+
 #define SPARC_ELF_PUT_WORD(htab, bfd, val, ptr)        \
        htab->put_word(bfd, val, ptr)
 
-#define SPARC_ELF_APPEND_RELA(htab, bfd, sec, rela)    \
-       htab->append_rela(bfd, sec, rela)
-
 #define SPARC_ELF_R_INFO(htab, in_rel, index, type)    \
        htab->r_info(in_rel, index, type)
 
@@ -766,10 +837,8 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
   if (ABI_64_P (abfd))
     {
       ret->put_word = sparc_put_word_64;
-      ret->append_rela = sparc_elf_append_rela_64;
       ret->r_info = sparc_elf_r_info_64;
       ret->r_symndx = sparc_elf_r_symndx_64;
-      ret->build_plt_entry = sparc64_plt_entry_build;
       ret->dtpoff_reloc = R_SPARC_TLS_DTPOFF64;
       ret->dtpmod_reloc = R_SPARC_TLS_DTPMOD64;
       ret->tpoff_reloc = R_SPARC_TLS_TPOFF64;
@@ -783,10 +852,8 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
   else
     {
       ret->put_word = sparc_put_word_32;
-      ret->append_rela = sparc_elf_append_rela_32;
       ret->r_info = sparc_elf_r_info_32;
       ret->r_symndx = sparc_elf_r_symndx_32;
-      ret->build_plt_entry = sparc32_plt_entry_build;
       ret->dtpoff_reloc = R_SPARC_TLS_DTPOFF32;
       ret->dtpmod_reloc = R_SPARC_TLS_DTPMOD32;
       ret->tpoff_reloc = R_SPARC_TLS_TPOFF32;
@@ -798,7 +865,8 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
       ret->dynamic_interpreter_size = sizeof ELF32_DYNAMIC_INTERPRETER;
     }
 
-  if (! _bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc))
+  if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc,
+                                     sizeof (struct _bfd_sparc_elf_link_hash_entry)))
     {
       free (ret);
       return NULL;
@@ -822,17 +890,25 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
   htab->sgot = bfd_get_section_by_name (dynobj, ".got");
   BFD_ASSERT (htab->sgot != NULL);
 
-  htab->srelgot = bfd_make_section (dynobj, ".rela.got");
+  htab->srelgot = bfd_make_section_with_flags (dynobj, ".rela.got",
+                                              SEC_ALLOC
+                                              | SEC_LOAD
+                                              | SEC_HAS_CONTENTS
+                                              | SEC_IN_MEMORY
+                                              | SEC_LINKER_CREATED
+                                              | SEC_READONLY);
   if (htab->srelgot == NULL
-      || ! bfd_set_section_flags (dynobj, htab->srelgot, SEC_ALLOC
-                                                        | SEC_LOAD
-                                                        | SEC_HAS_CONTENTS
-                                                        | SEC_IN_MEMORY
-                                                        | SEC_LINKER_CREATED
-                                                        | SEC_READONLY)
       || ! bfd_set_section_alignment (dynobj, htab->srelgot,
                                      htab->word_align_power))
     return FALSE;
+
+  if (htab->is_vxworks)
+    {
+      htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
+      if (!htab->sgotplt)
+       return FALSE;
+    }
+
   return TRUE;
 }
 
@@ -859,6 +935,41 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
   if (!info->shared)
     htab->srelbss = bfd_get_section_by_name (dynobj, ".rela.bss");
 
+  if (htab->is_vxworks)
+    {
+      if (!elf_vxworks_create_dynamic_sections (dynobj, info, &htab->srelplt2))
+       return FALSE;
+      if (info->shared)
+       {
+         htab->plt_header_size
+           = 4 * ARRAY_SIZE (sparc_vxworks_shared_plt0_entry);
+         htab->plt_entry_size
+           = 4 * ARRAY_SIZE (sparc_vxworks_shared_plt_entry);
+       }
+      else
+       {
+         htab->plt_header_size
+           = 4 * ARRAY_SIZE (sparc_vxworks_exec_plt0_entry);
+         htab->plt_entry_size
+           = 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
       || (!info->shared && !htab->srelbss))
     abort ();
@@ -869,7 +980,7 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 void
-_bfd_sparc_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
+_bfd_sparc_elf_copy_indirect_symbol (struct bfd_link_info *info,
                                     struct elf_link_hash_entry *dir,
                                     struct elf_link_hash_entry *ind)
 {
@@ -885,10 +996,7 @@ _bfd_sparc_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
          struct _bfd_sparc_elf_dyn_relocs **pp;
          struct _bfd_sparc_elf_dyn_relocs *p;
 
-         if (ind->root.type == bfd_link_hash_indirect)
-           abort ();
-
-         /* Add reloc counts against the weak sym to the strong sym
+         /* Add reloc counts against the indirect sym to the direct sym
             list.  Merge any entries against the same section.  */
          for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
            {
@@ -918,7 +1026,7 @@ _bfd_sparc_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
       edir->tls_type = eind->tls_type;
       eind->tls_type = GOT_UNKNOWN;
     }
-  _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
 }
 
 static int
@@ -1012,7 +1120,12 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       if (r_symndx < symtab_hdr->sh_info)
        h = NULL;
       else
-       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+       {
+         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;
+       }
 
       /* Compatibility with old R_SPARC_REV32 reloc conflicting
         with R_SPARC_TLS_GD_HI22.  */
@@ -1325,7 +1438,7 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  if (name == NULL)
                    return FALSE;
 
-                 BFD_ASSERT (strncmp (name, ".rela", 5) == 0
+                 BFD_ASSERT (CONST_STRNEQ (name, ".rela")
                              && strcmp (bfd_get_section_name (abfd, sec),
                                         name + 5) == 0);
 
@@ -1338,13 +1451,14 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    {
                      flagword flags;
 
-                     sreloc = bfd_make_section (dynobj, name);
                      flags = (SEC_HAS_CONTENTS | SEC_READONLY
                               | SEC_IN_MEMORY | SEC_LINKER_CREATED);
                      if ((sec->flags & SEC_ALLOC) != 0)
                        flags |= SEC_ALLOC | SEC_LOAD;
+                     sreloc = bfd_make_section_with_flags (dynobj,
+                                                           name,
+                                                           flags);
                      if (sreloc == NULL
-                         || ! bfd_set_section_flags (dynobj, sreloc, flags)
                          || ! bfd_set_section_alignment (dynobj, sreloc,
                                                          htab->word_align_power))
                        return FALSE;
@@ -1363,13 +1477,15 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                     easily.  Oh well.  */
 
                  asection *s;
+                 void *vpp;
+
                  s = bfd_section_from_r_symndx (abfd, &htab->sym_sec,
                                                 sec, r_symndx);
                  if (s == NULL)
                    return FALSE;
 
-                 head = ((struct _bfd_sparc_elf_dyn_relocs **)
-                         &elf_section_data (s)->local_dynrel);
+                 vpp = &elf_section_data (s)->local_dynrel;
+                 head = (struct _bfd_sparc_elf_dyn_relocs **) vpp;
                }
 
              p = *head;
@@ -1400,7 +1516,9 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          break;
 
        case R_SPARC_GNU_VTENTRY:
-         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+         BFD_ASSERT (h != NULL);
+         if (h != NULL
+             && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
            return FALSE;
          break;
 
@@ -1424,35 +1542,14 @@ _bfd_sparc_elf_gc_mark_hook (asection *sec,
                             Elf_Internal_Sym *sym)
 {
   if (h != NULL)
-    {
-      struct _bfd_sparc_elf_link_hash_table *htab;
-
-      htab = _bfd_sparc_elf_hash_table (info);
-      switch (SPARC_ELF_R_TYPE (rel->r_info))
+    switch (SPARC_ELF_R_TYPE (rel->r_info))
       {
       case R_SPARC_GNU_VTINHERIT:
       case R_SPARC_GNU_VTENTRY:
-       break;
-
-      default:
-       switch (h->root.type)
-         {
-         case bfd_link_hash_defined:
-         case bfd_link_hash_defweak:
-           return h->root.u.def.section;
-
-         case bfd_link_hash_common:
-           return h->root.u.c.p->section;
-
-         default:
-           break;
-         }
+       return NULL;
       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
 
-  return NULL;
+  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
 
 /* Update the got entry reference counts for the section being removed.  */
@@ -1466,6 +1563,9 @@ _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
   bfd_signed_vma *local_got_refcounts;
   const Elf_Internal_Rela *rel, *relend;
 
+  if (info->relocatable)
+    return TRUE;
+
   elf_section_data (sec)->local_dynrel = NULL;
 
   htab = _bfd_sparc_elf_hash_table (info);
@@ -1608,7 +1708,6 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   struct _bfd_sparc_elf_link_hash_entry * eh;
   struct _bfd_sparc_elf_dyn_relocs *p;
   asection *s;
-  unsigned int power_of_two;
 
   htab = _bfd_sparc_elf_hash_table (info);
 
@@ -1698,6 +1797,13 @@ _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
@@ -1718,29 +1824,9 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       h->needs_copy = 1;
     }
 
-  /* We need to figure out the alignment required for this symbol.  I
-     have no idea how ELF linkers handle this.  */
-  power_of_two = bfd_log2 (h->size);
-  if (power_of_two > htab->align_power_max)
-    power_of_two = htab->align_power_max;
-
-  /* Apply the required alignment.  */
   s = htab->sdynbss;
-  s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two));
-  if (power_of_two > bfd_get_section_alignment (dynobj, s))
-    {
-      if (! bfd_set_section_alignment (dynobj, s, power_of_two))
-       return FALSE;
-    }
-
-  /* Define the symbol as being at this point in the section.  */
-  h->root.u.def.section = s;
-  h->root.u.def.value = s->size;
 
-  /* Increment the section size to make room for the symbol.  */
-  s->size += h->size;
-
-  return TRUE;
+  return _bfd_elf_adjust_dynamic_copy (h, s);
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
@@ -1782,15 +1868,20 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
        {
          asection *s = htab->splt;
 
-         /* The first four entries in .plt is reserved.  */
+         /* Allocate room for the header.  */
          if (s->size == 0)
-           s->size = (SPARC_ELF_WORD_BYTES(htab) == 8 ?
-                      PLT64_HEADER_SIZE : PLT32_HEADER_SIZE);
+           {
+             s->size = htab->plt_header_size;
+
+             /* Allocate space for the .rela.plt.unloaded relocations.  */
+             if (htab->is_vxworks && !info->shared)
+               htab->srelplt2->size = sizeof (Elf32_External_Rela) * 2;
+           }
 
          /* The procedure linkage table size is bounded by the magnitude
             of the offset we can describe in the entry.  */
          if (s->size >= (SPARC_ELF_WORD_BYTES(htab) == 8 ?
-                         (bfd_vma)1 << 32 : 0x400000))
+                         (((bfd_vma)1 << 31) << 1) : 0x400000))
            {
              bfd_set_error (bfd_error_bad_value);
              return FALSE;
@@ -1822,11 +1913,20 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
            }
 
          /* Make room for this entry.  */
-         s->size += (SPARC_ELF_WORD_BYTES(htab) == 8 ?
-                     PLT64_ENTRY_SIZE : PLT32_ENTRY_SIZE);
+         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 (htab->is_vxworks)
+           {
+             /* Allocate space for the .got.plt entry.  */
+             htab->sgotplt->size += 4;
+
+             /* ...and for the .rela.plt.unloaded relocations.  */
+             if (!info->shared)
+               htab->srelplt2->size += sizeof (Elf32_External_Rela) * 3;
+           }
        }
       else
        {
@@ -1911,6 +2011,24 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
                pp = &p->next;
            }
        }
+
+      /* Also discard relocs on undefined weak syms with non-default
+        visibility.  */
+      if (eh->dyn_relocs != NULL
+         && h->root.type == bfd_link_hash_undefweak)
+       {
+         if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+           eh->dyn_relocs = NULL;
+
+         /* Make sure undefined weak symbols are output as a dynamic
+            symbol in PIEs.  */
+         else if (h->dynindx == -1
+                  && !h->forced_local)
+           {
+             if (! bfd_elf_link_record_dynamic_symbol (info, h))
+               return FALSE;
+           }
+       }
     }
   else
     {
@@ -2046,10 +2164,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
        {
          struct _bfd_sparc_elf_dyn_relocs *p;
 
-         for (p = *((struct _bfd_sparc_elf_dyn_relocs **)
-                    &elf_section_data (s)->local_dynrel);
-              p != NULL;
-              p = p->next)
+         for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
            {
              if (!bfd_is_abs_section (p->sec)
                  && bfd_is_abs_section (p->sec->output_section))
@@ -2113,6 +2228,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
   elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) 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.  */
@@ -2134,55 +2250,55 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
      memory for them.  */
   for (s = dynobj->sections; s != NULL; s = s->next)
     {
-      const char *name;
-      bfd_boolean strip = FALSE;
-
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
 
-      /* It's OK to base decisions on the section name, because none
-        of the dynobj section names depend upon the input files.  */
-      name = bfd_get_section_name (dynobj, s);
-
-      if (strncmp (name, ".rela", 5) == 0)
+      if (s == htab->splt
+         || s == htab->sgot
+         || s == htab->sdynbss
+         || s == htab->sgotplt)
        {
-         if (s->size == 0)
-           {
-             /* If we don't need this section, strip it from the
-                output file.  This is to handle .rela.bss and
-                .rel.plt.  We must create it in
-                create_dynamic_sections, because it must be created
-                before the linker maps input sections to output
-                sections.  The linker does that before
-                adjust_dynamic_symbol is called, and it is that
-                function which decides whether anything needs to go
-                into these sections.  */
-             strip = TRUE;
-           }
-         else
+         /* Strip this section if we don't need it; see the
+            comment below.  */
+       }
+      else if (CONST_STRNEQ (s->name, ".rela"))
+       {
+         if (s->size != 0)
            {
              /* We use the reloc_count field as a counter if we need
                 to copy relocs into the output file.  */
              s->reloc_count = 0;
            }
        }
-      else if (s != htab->splt && s != htab->sgot)
+      else
        {
-         /* It's not one of our sections, so don't allocate space.  */
+         /* It's not one of our sections.  */
          continue;
        }
 
-      if (strip)
+      if (s->size == 0)
        {
-         _bfd_strip_section_from_output (info, s);
+         /* If we don't need this section, strip it from the
+            output file.  This is mostly to handle .rela.bss and
+            .rela.plt.  We must create both sections in
+            create_dynamic_sections, because they must be created
+            before the linker maps input sections to output
+            sections.  The linker does that before
+            adjust_dynamic_symbol is called, and it is that
+            function which decides whether anything needs to go
+            into these sections.  */
+         s->flags |= SEC_EXCLUDE;
          continue;
        }
 
+      if ((s->flags & SEC_HAS_CONTENTS) == 0)
+       continue;
+
       /* Allocate memory for the section contents.  Zero the memory
         for the benefit of .rela.plt, which has 4 unused entries
         at the beginning, and we don't want garbage.  */
       s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
-      if (s->contents == NULL && s->size != 0)
+      if (s->contents == NULL)
        return FALSE;
     }
 
@@ -2283,6 +2399,9 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
                eht->dynsymcount++;
              }
        }
+      if (htab->is_vxworks
+         && !elf_vxworks_add_dynamic_entries (output_bfd, info))
+       return FALSE;
     }
 #undef add_dynamic_entry
 
@@ -2292,13 +2411,16 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
 bfd_boolean
 _bfd_sparc_elf_new_section_hook (bfd *abfd, asection *sec)
 {
-  struct _bfd_sparc_elf_section_data *sdata;
-  bfd_size_type amt = sizeof (*sdata);
+  if (!sec->used_by_bfd)
+    {
+      struct _bfd_sparc_elf_section_data *sdata;
+      bfd_size_type amt = sizeof (*sdata);
 
-  sdata = (struct _bfd_sparc_elf_section_data *) bfd_zalloc (abfd, amt);
-  if (sdata == NULL)
-    return FALSE;
-  sec->used_by_bfd = (PTR) sdata;
+      sdata = bfd_zalloc (abfd, amt);
+      if (sdata == NULL)
+       return FALSE;
+      sec->used_by_bfd = sdata;
+    }
 
   return _bfd_elf_new_section_hook (abfd, sec);
 }
@@ -2344,10 +2466,14 @@ tpoff (struct bfd_link_info *info, bfd_vma address)
 /* Relocate a SPARC ELF section.  */
 
 bfd_boolean
-_bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
-                                bfd *input_bfd, asection *input_section,
-                                bfd_byte *contents, Elf_Internal_Rela *relocs,
-                                Elf_Internal_Sym *local_syms, asection **local_sections)
+_bfd_sparc_elf_relocate_section (bfd *output_bfd,
+                                struct bfd_link_info *info,
+                                bfd *input_bfd,
+                                asection *input_section,
+                                bfd_byte *contents,
+                                Elf_Internal_Rela *relocs,
+                                Elf_Internal_Sym *local_syms,
+                                asection **local_sections)
 {
   struct _bfd_sparc_elf_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
@@ -2359,9 +2485,6 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   Elf_Internal_Rela *relend;
   int num_relocs;
 
-  if (info->relocatable)
-    return TRUE;
-
   htab = _bfd_sparc_elf_hash_table (info);
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
@@ -2405,7 +2528,6 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
        }
       howto = _bfd_sparc_elf_howto_table + r_type;
 
-      /* This is a final link.  */
       r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info);
       h = NULL;
       sym = NULL;
@@ -2437,6 +2559,21 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
            }
        }
 
+      if (sec != NULL && elf_discarded_section (sec))
+       {
+         /* For relocs against symbols from removed linkonce
+            sections, or sections discarded by a linker script, we
+            just want the section contents zeroed.  Avoid any
+            special processing.  */
+         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
+         rel->r_info = 0;
+         rel->r_addend = 0;
+         continue;
+       }
+
+      if (info->relocatable)
+       continue;
+
       switch (r_type)
        {
        case R_SPARC_GOT10:
@@ -2518,7 +2655,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                                                        0, R_SPARC_RELATIVE);
                      outrel.r_addend = relocation;
                      relocation = 0;
-                     SPARC_ELF_APPEND_RELA (htab, output_bfd, s, &outrel);
+                     sparc_elf_append_rela (output_bfd, s, &outrel);
                    }
 
                  SPARC_ELF_PUT_WORD (htab, output_bfd, relocation,
@@ -2625,11 +2762,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
        case R_SPARC_L44:
        case R_SPARC_UA64:
        r_sparc_plt32:
-         /* r_symndx will be zero only for relocs against symbols
-            from removed linkonce sections, or sections discarded by
-            a linker script.  */
-         if (r_symndx == 0
-             || (input_section->flags & SEC_ALLOC) == 0)
+         if ((input_section->flags & SEC_ALLOC) == 0)
            break;
 
          if ((info->shared
@@ -2737,6 +2870,8 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                    {
                      long indx;
 
+                     outrel.r_addend = relocation + rel->r_addend;
+
                      if (is_plt)
                        sec = htab->splt;
 
@@ -2751,9 +2886,20 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                        {
                          asection *osec;
 
+                         /* We are turning this relocation into one
+                            against a section symbol.  It would be
+                            proper to subtract the symbol's value,
+                            osec->vma, from the emitted reloc addend,
+                            but ld.so expects buggy relocs.  */
                          osec = sec->output_section;
                          indx = elf_section_data (osec)->dynindx;
 
+                         if (indx == 0)
+                           {
+                             osec = htab->elf.text_index_section;
+                             indx = elf_section_data (osec)->dynindx;
+                           }
+
                          /* FIXME: we really should be able to link non-pic
                             shared libraries.  */
                          if (indx == 0)
@@ -2767,12 +2913,12 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                            }
                        }
 
-                     outrel.r_info = SPARC_ELF_R_INFO (htab, rel, indx, r_type);
-                     outrel.r_addend = relocation + rel->r_addend;
+                     outrel.r_info = SPARC_ELF_R_INFO (htab, rel, indx,
+                                                       r_type);
                    }
                }
 
-             SPARC_ELF_APPEND_RELA (htab, output_bfd, sreloc, &outrel);
+             sparc_elf_append_rela (output_bfd, sreloc, &outrel);
 
              /* This reloc will be computed at runtime, so there's no
                 need to do anything now.  */
@@ -2880,7 +3026,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
              else
                outrel.r_addend = 0;
              outrel.r_info = SPARC_ELF_R_INFO (htab, NULL, indx, dr_type);
-             SPARC_ELF_APPEND_RELA (htab, output_bfd, htab->srelgot, &outrel);
+             sparc_elf_append_rela (output_bfd, htab->srelgot, &outrel);
 
              if (r_type == R_SPARC_TLS_GD_HI22
                  || r_type == R_SPARC_TLS_GD_LO10)
@@ -2901,7 +3047,8 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                      outrel.r_info = SPARC_ELF_R_INFO (htab, NULL, indx,
                                                        SPARC_ELF_DTPOFF_RELOC (htab));
                      outrel.r_offset += SPARC_ELF_WORD_BYTES (htab);
-                     SPARC_ELF_APPEND_RELA (htab, output_bfd, htab->srelgot, &outrel);
+                     sparc_elf_append_rela (output_bfd, htab->srelgot,
+                                            &outrel);
                    }
                }
              else if (dr_type == SPARC_ELF_DTPMOD_RELOC (htab))
@@ -2970,7 +3117,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                                    + rel->r_addend;
                }
 
-             SPARC_ELF_APPEND_RELA (htab, output_bfd, sreloc, &outrel);
+             sparc_elf_append_rela (output_bfd, sreloc, &outrel);
              continue;
            }
          relocation = tpoff (info, relocation);
@@ -3114,10 +3261,11 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
          && !((input_section->flags & SEC_DEBUGGING) != 0
               && h->def_dynamic))
        (*_bfd_error_handler)
-         (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"),
+         (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
           input_bfd,
           input_section,
           (long) rel->r_offset,
+          howto->name,
           h->root.root.string);
 
       r = bfd_reloc_continue;
@@ -3336,8 +3484,34 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
              {
                const char *name;
 
+               /* 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)
+                   && (((input_section->flags & SEC_DEBUGGING) != 0
+                        && strcmp (bfd_section_name (input_bfd,
+                                                     input_section),
+                                   ".stab") == 0)
+                       || _bfd_elf_section_offset (output_bfd, info,
+                                                   input_section,
+                                                   rel->r_offset)
+                            == (bfd_vma)-1))
+                 break;
+
                if (h != NULL)
-                 name = NULL;
+                 {
+                   /* Assume this is a call protected by other code that
+                      detect the symbol is undefined.  If this is the case,
+                      we can safely ignore the overflow.  If not, the
+                      program is hosed anyway, and a little warning isn't
+                      going to help.  */
+                   if (h->root.type == bfd_link_hash_undefweak
+                       && howto->pc_relative)
+                     break;
+
+                   name = NULL;
+                 }
                else
                  {
                    name = bfd_elf_string_from_elf_section (input_bfd,
@@ -3362,6 +3536,97 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   return TRUE;
 }
 
+/* Build a VxWorks PLT entry.  PLT_INDEX is the index of the PLT entry
+   and PLT_OFFSET is the byte offset from the start of .plt.  GOT_OFFSET
+   is the offset of the associated .got.plt entry from
+   _GLOBAL_OFFSET_TABLE_.  */
+
+static void
+sparc_vxworks_build_plt_entry (bfd *output_bfd, struct bfd_link_info *info,
+                              bfd_vma plt_offset, bfd_vma plt_index,
+                              bfd_vma got_offset)
+{
+  bfd_vma got_base;
+  const bfd_vma *plt_entry;
+  struct _bfd_sparc_elf_link_hash_table *htab;
+  bfd_byte *loc;
+  Elf_Internal_Rela rela;
+
+  htab = _bfd_sparc_elf_hash_table (info);
+  if (info->shared)
+    {
+      plt_entry = sparc_vxworks_shared_plt_entry;
+      got_base = 0;
+    }
+  else
+    {
+      plt_entry = sparc_vxworks_exec_plt_entry;
+      got_base = (htab->elf.hgot->root.u.def.value
+                 + htab->elf.hgot->root.u.def.section->output_offset
+                 + htab->elf.hgot->root.u.def.section->output_section->vma);
+    }
+
+  /* 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);
+  bfd_put_32 (output_bfd, plt_entry[1] + ((got_base + got_offset) & 0x3ff),
+             htab->splt->contents + plt_offset + 4);
+  bfd_put_32 (output_bfd, plt_entry[2],
+             htab->splt->contents + plt_offset + 8);
+  bfd_put_32 (output_bfd, plt_entry[3],
+             htab->splt->contents + plt_offset + 12);
+  bfd_put_32 (output_bfd, plt_entry[4],
+             htab->splt->contents + plt_offset + 16);
+  bfd_put_32 (output_bfd, plt_entry[5] + (plt_index >> 10),
+             htab->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);
+  bfd_put_32 (output_bfd, plt_entry[7] + (plt_index & 0x3ff),
+             htab->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_put_32 (output_bfd,
+             htab->splt->output_section->vma
+             + htab->splt->output_offset
+             + plt_offset + 20,
+             htab->sgotplt->contents + got_offset);
+
+  /* Add relocations to .rela.plt.unloaded.  */
+  if (!info->shared)
+    {
+      loc = (htab->srelplt2->contents
+            + (2 + 3 * plt_index) * sizeof (Elf32_External_Rela));
+
+      /* Relocate the initial sethi.  */
+      rela.r_offset = (htab->splt->output_section->vma
+                      + htab->splt->output_offset
+                      + plt_offset);
+      rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_HI22);
+      rela.r_addend = got_offset;
+      bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+      loc += sizeof (Elf32_External_Rela);
+
+      /* Likewise the following or.  */
+      rela.r_offset += 4;
+      rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_LO10);
+      bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+      loc += sizeof (Elf32_External_Rela);
+
+      /* Relocate the .got.plt entry.  */
+      rela.r_offset = (htab->sgotplt->output_section->vma
+                      + htab->sgotplt->output_offset
+                      + got_offset);
+      rela.r_info = ELF32_R_INFO (htab->elf.hplt->indx, R_SPARC_32);
+      rela.r_addend = plt_offset + 20;
+      bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+    }
+}
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -3373,9 +3638,11 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 {
   bfd *dynobj;
   struct _bfd_sparc_elf_link_hash_table *htab;
+  const struct elf_backend_data *bed;
 
   htab = _bfd_sparc_elf_hash_table (info);
   dynobj = htab->elf.dynobj;
+  bed = get_elf_backend_data (output_bfd);
 
   if (h->plt.offset != (bfd_vma) -1)
     {
@@ -3383,7 +3650,7 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
       asection *srela;
       Elf_Internal_Rela rela;
       bfd_byte *loc;
-      bfd_vma r_offset;
+      bfd_vma r_offset, got_offset;
       int rela_index;
 
       /* This symbol has an entry in the PLT.  Set it up.  */
@@ -3394,23 +3661,48 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
       srela = htab->srelplt;
       BFD_ASSERT (splt != NULL && srela != NULL);
 
-      /* 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);
-
       /* Fill in the entry in the .rela.plt section.  */
-      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 (htab->is_vxworks)
        {
+         /* Work out the index of this PLT entry.  */
+         rela_index = ((h->plt.offset - htab->plt_header_size)
+                       / htab->plt_entry_size);
+
+         /* Calculate the offset of the associated .got.plt entry.
+            The first three entries are reserved.  */
+         got_offset = (rela_index + 3) * 4;
+
+         sparc_vxworks_build_plt_entry (output_bfd, info, h->plt.offset,
+                                        rela_index, got_offset);
+
+
+         /* 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
+                          + got_offset);
          rela.r_addend = 0;
        }
       else
        {
-         rela.r_addend = -(h->plt.offset + 4)
-                         -(splt->output_section->vma + splt->output_offset);
+         /* 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);
+
+         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))
+           {
+             rela.r_addend = 0;
+           }
+         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);
 
@@ -3420,16 +3712,8 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
         thus .plt[4] has corresponding .rela.plt[0] and so on.  */
 
       loc = srela->contents;
-      if (ABI_64_P (output_bfd))
-       {
-         loc += rela_index * sizeof (Elf64_External_Rela);
-         bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
-       }
-      else
-       {
-         loc += rela_index * sizeof (Elf32_External_Rela);
-         bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
-       }
+      loc += rela_index * bed->s->sizeof_rela;
+      bed->s->swap_reloca_out (output_bfd, &rela, loc);
 
       if (!h->def_regular)
        {
@@ -3486,7 +3770,7 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
       SPARC_ELF_PUT_WORD (htab, output_bfd, 0,
                          sgot->contents + (h->got.offset & ~(bfd_vma) 1));
-      SPARC_ELF_APPEND_RELA (htab, output_bfd, srela, &rela);
+      sparc_elf_append_rela (output_bfd, srela, &rela);
     }
 
   if (h->needs_copy)
@@ -3506,13 +3790,15 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
                       + h->root.u.def.section->output_offset);
       rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, R_SPARC_COPY);
       rela.r_addend = 0;
-      SPARC_ELF_APPEND_RELA (htab, output_bfd, s, &rela);
+      sparc_elf_append_rela (output_bfd, s, &rela);
     }
 
-  /* Mark some specially defined symbols as absolute.  */
+  /* 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
-      || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0
-      || strcmp (h->root.root.string, "_PROCEDURE_LINKAGE_TABLE_") == 0)
+      || (!htab->is_vxworks
+         && (h == htab->elf.hgot || h == htab->elf.hplt)))
     sym->st_shndx = SHN_ABS;
 
   return TRUE;
@@ -3521,29 +3807,56 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
 /* Finish up the dynamic sections.  */
 
 static bfd_boolean
-sparc64_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
-                   bfd *dynobj, asection *sdyn,
-                   asection *splt ATTRIBUTE_UNUSED)
+sparc_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
+                 bfd *dynobj, asection *sdyn,
+                 asection *splt ATTRIBUTE_UNUSED)
 {
-  Elf64_External_Dyn *dyncon, *dynconend;
+  struct _bfd_sparc_elf_link_hash_table *htab;
+  const struct elf_backend_data *bed;
+  bfd_byte *dyncon, *dynconend;
+  size_t dynsize;
   int stt_regidx = -1;
+  bfd_boolean abi_64_p;
 
-  dyncon = (Elf64_External_Dyn *) sdyn->contents;
-  dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->size);
-  for (; dyncon < dynconend; dyncon++)
+  htab = _bfd_sparc_elf_hash_table (info);
+  bed = get_elf_backend_data (output_bfd);
+  dynsize = bed->s->sizeof_dyn;
+  dynconend = sdyn->contents + sdyn->size;
+  abi_64_p = ABI_64_P (output_bfd);
+  for (dyncon = sdyn->contents; dyncon < dynconend; dyncon += dynsize)
     {
       Elf_Internal_Dyn dyn;
       const char *name;
       bfd_boolean size;
 
-      bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn);
+      bed->s->swap_dyn_in (dynobj, dyncon, &dyn);
 
-      switch (dyn.d_tag)
+      if (htab->is_vxworks && dyn.d_tag == DT_RELASZ)
+       {
+         /* On VxWorks, DT_RELASZ should not include the relocations
+            in .rela.plt.  */
+         if (htab->srelplt)
+           {
+             dyn.d_un.d_val -= htab->srelplt->size;
+             bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
+           }
+       }
+      else if (htab->is_vxworks && dyn.d_tag == DT_PLTGOT)
+       {
+         /* On VxWorks, DT_PLTGOT should point to the start of the GOT,
+            not to the start of the PLT.  */
+         if (htab->sgotplt)
+           {
+             dyn.d_un.d_val = (htab->sgotplt->output_section->vma
+                               + htab->sgotplt->output_offset);
+             bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
+           }
+       }
+      else if (htab->is_vxworks
+              && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
+       bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
+      else if (abi_64_p && dyn.d_tag == DT_SPARC_REGISTER)
        {
-       case DT_PLTGOT:   name = ".plt"; size = FALSE; break;
-       case DT_PLTRELSZ: name = ".rela.plt"; size = TRUE; break;
-       case DT_JMPREL:   name = ".rela.plt"; size = FALSE; break;
-       case DT_SPARC_REGISTER:
          if (stt_regidx == -1)
            {
              stt_regidx =
@@ -3552,75 +3865,129 @@ sparc64_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
                return FALSE;
            }
          dyn.d_un.d_val = stt_regidx++;
-         bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
-         /* fallthrough */
-       default:          name = NULL; size = FALSE; break;
+         bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
        }
-
-      if (name != NULL)
+      else
        {
-         asection *s;
+         switch (dyn.d_tag)
+           {
+           case DT_PLTGOT:   name = ".plt"; size = FALSE; break;
+           case DT_PLTRELSZ: name = ".rela.plt"; size = TRUE; break;
+           case DT_JMPREL:   name = ".rela.plt"; size = FALSE; break;
+           default:          name = NULL; size = FALSE; break;
+           }
 
-         s = bfd_get_section_by_name (output_bfd, name);
-         if (s == NULL)
-           dyn.d_un.d_val = 0;
-         else
+         if (name != NULL)
            {
-             if (! size)
-               dyn.d_un.d_ptr = s->vma;
+             asection *s;
+
+             s = bfd_get_section_by_name (output_bfd, name);
+             if (s == NULL)
+               dyn.d_un.d_val = 0;
              else
-               dyn.d_un.d_val = s->size;
+               {
+                 if (! size)
+                   dyn.d_un.d_ptr = s->vma;
+                 else
+                   dyn.d_un.d_val = s->size;
+               }
+             bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
            }
-         bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
        }
     }
   return TRUE;
 }
 
-static bfd_boolean
-sparc32_finish_dyn (bfd *output_bfd,
-                   struct bfd_link_info *info ATTRIBUTE_UNUSED,
-                   bfd *dynobj, asection *sdyn,
-                   asection *splt ATTRIBUTE_UNUSED)
+/* Install the first PLT entry in a VxWorks executable and make sure that
+   .rela.plt.unloaded relocations have the correct symbol indexes.  */
+
+static void
+sparc_vxworks_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
 {
-  Elf32_External_Dyn *dyncon, *dynconend;
+  struct _bfd_sparc_elf_link_hash_table *htab;
+  Elf_Internal_Rela rela;
+  bfd_vma got_base;
+  bfd_byte *loc;
 
-  dyncon = (Elf32_External_Dyn *) sdyn->contents;
-  dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size);
-  for (; dyncon < dynconend; dyncon++)
-    {
-      Elf_Internal_Dyn dyn;
-      const char *name;
-      bfd_boolean size;
+  htab = _bfd_sparc_elf_hash_table (info);
 
-      bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
+  /* Calculate the absolute value of _GLOBAL_OFFSET_TABLE_.  */
+  got_base = (htab->elf.hgot->root.u.def.section->output_section->vma
+             + htab->elf.hgot->root.u.def.section->output_offset
+             + htab->elf.hgot->root.u.def.value);
+
+  /* Install the initial PLT entry.  */
+  bfd_put_32 (output_bfd,
+             sparc_vxworks_exec_plt0_entry[0] + ((got_base + 8) >> 10),
+             htab->splt->contents);
+  bfd_put_32 (output_bfd,
+             sparc_vxworks_exec_plt0_entry[1] + ((got_base + 8) & 0x3ff),
+             htab->splt->contents + 4);
+  bfd_put_32 (output_bfd,
+             sparc_vxworks_exec_plt0_entry[2],
+             htab->splt->contents + 8);
+  bfd_put_32 (output_bfd,
+             sparc_vxworks_exec_plt0_entry[3],
+             htab->splt->contents + 12);
+  bfd_put_32 (output_bfd,
+             sparc_vxworks_exec_plt0_entry[4],
+             htab->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_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_HI22);
+  rela.r_addend = 8;
+  bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+  loc += sizeof (Elf32_External_Rela);
+
+  /* Likewise the following "or".  */
+  rela.r_offset += 4;
+  rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_LO10);
+  bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+  loc += sizeof (Elf32_External_Rela);
+
+  /* Fix up the remaining .rela.plt.unloaded relocations.  They may have
+     the wrong symbol index for _G_O_T_ or _P_L_T_ depending on the order
+     in which symbols were output.  */
+  while (loc < htab->srelplt2->contents + htab->srelplt2->size)
+    {
+      Elf_Internal_Rela rel;
+
+      /* The entry's initial "sethi" (against _G_O_T_).  */
+      bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
+      rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_HI22);
+      bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+      loc += sizeof (Elf32_External_Rela);
+
+      /* The following "or" (also against _G_O_T_).  */
+      bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
+      rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_SPARC_LO10);
+      bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+      loc += sizeof (Elf32_External_Rela);
+
+      /* The .got.plt entry (against _P_L_T_).  */
+      bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
+      rel.r_info = ELF32_R_INFO (htab->elf.hplt->indx, R_SPARC_32);
+      bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+      loc += sizeof (Elf32_External_Rela);
+    }
+}
 
-      switch (dyn.d_tag)
-       {
-       case DT_PLTGOT:   name = ".plt"; size = FALSE; break;
-       case DT_PLTRELSZ: name = ".rela.plt"; size = TRUE; break;
-       case DT_JMPREL:   name = ".rela.plt"; size = FALSE; break;
-       default:          name = NULL; size = FALSE; break;
-       }
+/* Install the first PLT entry in a VxWorks shared object.  */
 
-      if (name != NULL)
-       {
-         asection *s;
+static void
+sparc_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info)
+{
+  struct _bfd_sparc_elf_link_hash_table *htab;
+  unsigned int i;
 
-         s = bfd_get_section_by_name (output_bfd, name);
-         if (s == NULL)
-           dyn.d_un.d_val = 0;
-         else
-           {
-             if (! size)
-               dyn.d_un.d_ptr = s->vma;
-             else
-               dyn.d_un.d_val = s->size;
-           }
-         bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
-       }
-    }
-  return TRUE;
+  htab = _bfd_sparc_elf_hash_table (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);
 }
 
 bfd_boolean
@@ -3638,34 +4005,35 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       asection *splt;
-      bfd_boolean ret;
 
       splt = bfd_get_section_by_name (dynobj, ".plt");
       BFD_ASSERT (splt != NULL && sdyn != NULL);
 
-      if (ABI_64_P (output_bfd))
-       ret = sparc64_finish_dyn (output_bfd, info, dynobj, sdyn, splt);
-      else
-       ret = sparc32_finish_dyn (output_bfd, info, dynobj, sdyn, splt);
-
-      if (ret != TRUE)
-       return ret;
+      if (!sparc_finish_dyn (output_bfd, info, dynobj, sdyn, splt))
+       return FALSE;
 
       /* Initialize the contents of the .plt section.  */
       if (splt->size > 0)
        {
-         if (ABI_64_P (output_bfd))
-           memset (splt->contents, 0, 4 * PLT64_ENTRY_SIZE);
+         if (htab->is_vxworks)
+           {
+             if (info->shared)
+               sparc_vxworks_finish_shared_plt (output_bfd, info);
+             else
+               sparc_vxworks_finish_exec_plt (output_bfd, info);
+           }
          else
            {
-             memset (splt->contents, 0, 4 * PLT32_ENTRY_SIZE);
-             bfd_put_32 (output_bfd, (bfd_vma) SPARC_NOP,
-                         splt->contents + splt->size - 4);
+             memset (splt->contents, 0, htab->plt_header_size);
+             if (!ABI_64_P (output_bfd))
+               bfd_put_32 (output_bfd, (bfd_vma) SPARC_NOP,
+                           splt->contents + splt->size - 4);
            }
        }
 
-      elf_section_data (splt->output_section)->this_hdr.sh_entsize =
-       (ABI_64_P (output_bfd) ? PLT64_ENTRY_SIZE : PLT32_ENTRY_SIZE);
+      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