Updates.
[deliverable/binutils-gdb.git] / bfd / elf64-sparc.c
index 255713e3d70a2085d9fdd1ddcdf1e7906d75c672..b2b8cbf9ec0b4614be53174e4592292ee6c719a9 100644 (file)
@@ -1,5 +1,6 @@
 /* SPARC-specific support for 64-bit ELF
-   Copyright (C) 1993, 95, 96, 97, 98, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1993, 95, 96, 97, 98, 99, 2000
+   Free Software Foundation, Inc.
 
 This file is part of BFD, the Binary File Descriptor library.
 
@@ -21,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "sysdep.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
+#include "opcode/sparc.h"
 
 /* This is defined if one wants to build upward compatible binaries
    with the original sparc64-elf toolchain.  The support is kept in for
@@ -32,6 +34,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 /* In case we're on a 32-bit machine, construct a 64-bit "-1" value.  */
 #define MINUS_ONE (~ (bfd_vma) 0)
 
+static struct bfd_link_hash_table * sparc64_elf_bfd_link_hash_table_create
+  PARAMS((bfd *));
 static reloc_howto_type *sparc64_elf_reloc_type_lookup
   PARAMS ((bfd *, bfd_reloc_code_real_type));
 static void sparc64_elf_info_to_howto
@@ -51,10 +55,19 @@ static boolean sparc64_elf_adjust_dynamic_symbol
   PARAMS((struct bfd_link_info *, struct elf_link_hash_entry *));
 static boolean sparc64_elf_size_dynamic_sections
   PARAMS((bfd *, struct bfd_link_info *));
+static int sparc64_elf_get_symbol_type
+  PARAMS (( Elf_Internal_Sym *, int));
+static boolean sparc64_elf_add_symbol_hook
+  PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *,
+        const char **, flagword *, asection **, bfd_vma *));
+static void sparc64_elf_symbol_processing
+  PARAMS ((bfd *, asymbol *));
 
 static boolean sparc64_elf_merge_private_bfd_data
   PARAMS ((bfd *, bfd *));
 
+static boolean sparc64_elf_relax_section
+  PARAMS ((bfd *, asection *, struct bfd_link_info *, boolean *));
 static boolean sparc64_elf_relocate_section
   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
           Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
@@ -202,7 +215,7 @@ static CONST struct elf_reloc_map sparc_reloc_map[] =
 
 static reloc_howto_type *
 sparc64_elf_reloc_type_lookup (abfd, code)
-     bfd *abfd;
+     bfd *abfd ATTRIBUTE_UNUSED;
      bfd_reloc_code_real_type code;
 {
   unsigned int i;
@@ -216,7 +229,7 @@ sparc64_elf_reloc_type_lookup (abfd, code)
 
 static void
 sparc64_elf_info_to_howto (abfd, cache_ptr, dst)
-     bfd *abfd;
+     bfd *abfd ATTRIBUTE_UNUSED;
      arelent *cache_ptr;
      Elf64_Internal_Rela *dst;
 {
@@ -230,7 +243,7 @@ sparc64_elf_info_to_howto (abfd, cache_ptr, dst)
    
 static long
 sparc64_elf_get_reloc_upper_bound (abfd, sec)
-     bfd *abfd;
+     bfd *abfd ATTRIBUTE_UNUSED;
      asection *sec;
 {
   return (sec->reloc_count * 2 + 1) * sizeof (arelent *);
@@ -256,7 +269,6 @@ sparc64_elf_slurp_one_reloc_table (abfd, asect, rel_hdr, symbols, dynamic)
      asymbol **symbols;
      boolean dynamic;
 {
-  struct elf_backend_data * const ebd = get_elf_backend_data (abfd);
   PTR allocated = NULL;
   bfd_byte *native_relocs;
   arelent *relent;
@@ -465,7 +477,7 @@ sparc64_elf_write_relocs (abfd, sec, data)
 {
   boolean *failedp = (boolean *) data;
   Elf_Internal_Shdr *rela_hdr;
-  Elf64_External_Rela *outbound_relocas;
+  Elf64_External_Rela *outbound_relocas, *src_rela;
   unsigned int idx, count;
   asymbol *last_sym = 0;
   int last_sym_idx = 0;
@@ -491,7 +503,6 @@ sparc64_elf_write_relocs (abfd, sec, data)
   for (idx = 0; idx < sec->reloc_count; idx++)
     {
       bfd_vma addr;
-      unsigned int i;
 
       ++count;
 
@@ -525,17 +536,16 @@ sparc64_elf_write_relocs (abfd, sec, data)
 
   /* orelocation has the data, reloc_count has the count... */
   outbound_relocas = (Elf64_External_Rela *) rela_hdr->contents;
+  src_rela = outbound_relocas;
 
   for (idx = 0; idx < sec->reloc_count; idx++)
     {
       Elf_Internal_Rela dst_rela;
-      Elf64_External_Rela *src_rela;
       arelent *ptr;
       asymbol *sym;
       int n;
 
       ptr = sec->orelocation[idx];
-      src_rela = outbound_relocas + idx;
 
       /* The address of an ELF reloc is section relative for an object
         file, and absolute for an executable file or shared library.
@@ -593,8 +603,55 @@ sparc64_elf_write_relocs (abfd, sec, data)
 
       dst_rela.r_addend = ptr->addend;
       bfd_elf64_swap_reloca_out (abfd, &dst_rela, src_rela);
+      ++src_rela;
     }
 }
+\f
+/* Sparc64 ELF linker hash table.  */
+
+struct sparc64_elf_app_reg
+{
+  unsigned char bind;
+  unsigned short shndx;
+  bfd *abfd;
+  char *name;
+};
+
+struct sparc64_elf_link_hash_table
+{
+  struct elf_link_hash_table root;
+
+  struct sparc64_elf_app_reg app_regs [4];
+};
+
+/* Get the Sparc64 ELF linker hash table from a link_info structure.  */
+
+#define sparc64_elf_hash_table(p) \
+  ((struct sparc64_elf_link_hash_table *) ((p)->hash))
+  
+/* Create a Sparc64 ELF linker hash table.  */
+
+static struct bfd_link_hash_table *
+sparc64_elf_bfd_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct sparc64_elf_link_hash_table *ret;
+
+  ret = ((struct sparc64_elf_link_hash_table *)
+        bfd_zalloc (abfd, sizeof (struct sparc64_elf_link_hash_table)));
+  if (ret == (struct sparc64_elf_link_hash_table *) NULL)
+    return NULL;
+
+  if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
+                                      _bfd_elf_link_hash_newfunc))
+    {
+      bfd_release (abfd, ret);
+      return NULL;
+    }
+
+  return &ret->root.root;
+}
+
 \f
 /* Utility for performing the standard initial work of an instruction
    relocation.
@@ -667,13 +724,13 @@ sparc_elf_notsup_reloc (abfd,
                        input_section,
                        output_bfd,
                        error_message)
-     bfd *abfd;
-     arelent *reloc_entry;
-     asymbol *symbol;
-     PTR data;
-     asection *input_section;
-     bfd *output_bfd;
-     char **error_message;
+     bfd *abfd ATTRIBUTE_UNUSED;
+     arelent *reloc_entry ATTRIBUTE_UNUSED;
+     asymbol *symbol ATTRIBUTE_UNUSED;
+     PTR data ATTRIBUTE_UNUSED;
+     asection *input_section ATTRIBUTE_UNUSED;
+     bfd *output_bfd ATTRIBUTE_UNUSED;
+     char **error_message ATTRIBUTE_UNUSED;
 {
   return bfd_reloc_notsupported;
 }
@@ -689,7 +746,7 @@ sparc_elf_wdisp16_reloc (abfd, reloc_entry, symbol, data, input_section,
      PTR data;
      asection *input_section;
      bfd *output_bfd;
-     char **error_message;
+     char **error_message ATTRIBUTE_UNUSED;
 {
   bfd_vma relocation;
   bfd_vma insn;
@@ -727,7 +784,7 @@ sparc_elf_hix22_reloc (abfd,
      PTR data;
      asection *input_section;
      bfd *output_bfd;
-     char **error_message;
+     char **error_message ATTRIBUTE_UNUSED;
 {
   bfd_vma relocation;
   bfd_vma insn;
@@ -764,7 +821,7 @@ sparc_elf_lox10_reloc (abfd,
      PTR data;
      asection *input_section;
      bfd *output_bfd;
-     char **error_message;
+     char **error_message ATTRIBUTE_UNUSED;
 {
   bfd_vma relocation;
   bfd_vma insn;
@@ -820,8 +877,8 @@ sparc64_elf_build_plt (output_bfd, contents, nentries)
       /* sethi (. - plt0), %g1 */
       sethi = 0x03000000 | (i * PLT_ENTRY_SIZE);
 
-      /* ba,a,pt %icc, plt1 */
-      ba = 0x30480000 | (((contents+PLT_ENTRY_SIZE) - (entry+4)) / 4 & 0x7ffff);
+      /* ba,a,pt %xcc, plt1 */
+      ba = 0x30680000 | (((contents+PLT_ENTRY_SIZE) - (entry+4)) / 4 & 0x7ffff);
 
       bfd_put_32 (output_bfd, sethi, entry);
       bfd_put_32 (output_bfd, ba, entry+4);
@@ -858,7 +915,7 @@ sparc64_elf_build_plt (output_bfd, contents, nentries)
          bfd_put_32 (output_bfd, 0x83c3c001, entry+16); /* jmpl %o7+%g1,%g1 */
          bfd_put_32 (output_bfd, 0x9e100005, entry+20); /* mov %g5,%o7 */
 
-         bfd_put_64 (output_bfd, contents - entry+4, ptr);
+         bfd_put_64 (output_bfd, contents - (entry+4), ptr);
        }
     }
 }
@@ -893,11 +950,15 @@ sparc64_elf_plt_ptr_offset (index, max)
 
   /* See above for details.  */
 
-  block = (index - LARGE_PLT_THRESHOLD) / 160;
-  ofs = (index - LARGE_PLT_THRESHOLD) % 160;
-  last = (max - LARGE_PLT_THRESHOLD) % 160;
+  block = (((index - LARGE_PLT_THRESHOLD) / 160) * 160)
+         + LARGE_PLT_THRESHOLD;
+  ofs = index - block;
+  if (block + 160 > max)
+    last = (max - LARGE_PLT_THRESHOLD) % 160;
+  else
+    last = 160;
 
-  return ((LARGE_PLT_THRESHOLD + block*160) * PLT_ENTRY_SIZE
+  return (block * PLT_ENTRY_SIZE
          + last * 6*4
          + ofs * 8);
 }
@@ -1196,6 +1257,223 @@ sparc64_elf_check_relocs (abfd, info, sec, relocs)
   return true;
 }
 
+/* Hook called by the linker routine which adds symbols from an object
+   file.  We use it for STT_REGISTER symbols.  */
+
+static boolean
+sparc64_elf_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     const Elf_Internal_Sym *sym;
+     const char **namep;
+     flagword *flagsp ATTRIBUTE_UNUSED;
+     asection **secp ATTRIBUTE_UNUSED;
+     bfd_vma *valp ATTRIBUTE_UNUSED;
+{
+  static char *stt_types[] = { "NOTYPE", "OBJECT", "FUNCTION" };
+
+  if (ELF_ST_TYPE (sym->st_info) == STT_REGISTER)
+    {
+      int reg;
+      struct sparc64_elf_app_reg *p;
+      
+      reg = (int)sym->st_value;
+      switch (reg & ~1)
+       {
+       case 2: reg -= 2; break;
+       case 6: reg -= 4; break;
+       default:
+          (*_bfd_error_handler)
+            (_("%s: Only registers %%g[2367] can be declared using STT_REGISTER"),
+             bfd_get_filename (abfd));
+         return false;
+       }
+
+      if (info->hash->creator != abfd->xvec
+         || (abfd->flags & DYNAMIC) != 0)
+        {
+         /* STT_REGISTER only works when linking an elf64_sparc object.
+            If STT_REGISTER comes from a dynamic object, don't put it into
+            the output bfd.  The dynamic linker will recheck it.  */
+         *namep = NULL;
+         return true;
+        }
+
+      p = sparc64_elf_hash_table(info)->app_regs + reg;
+
+      if (p->name != NULL && strcmp (p->name, *namep))
+       {
+          (*_bfd_error_handler)
+            (_("Register %%g%d used incompatibly: "
+               "previously declared in %s to %s, in %s redefined to %s"),
+             (int)sym->st_value,
+             bfd_get_filename (p->abfd), *p->name ? p->name : "#scratch",
+             bfd_get_filename (abfd), **namep ? *namep : "#scratch");
+         return false;
+       }
+
+      if (p->name == NULL)
+       {
+         if (**namep)
+           {
+             struct elf_link_hash_entry *h;
+             
+             h = (struct elf_link_hash_entry *)
+               bfd_link_hash_lookup (info->hash, *namep, false, false, false);
+
+             if (h != NULL)
+               {
+                 unsigned char type = h->type;
+
+                 if (type > STT_FUNC) type = 0;
+                 (*_bfd_error_handler)
+                   (_("Symbol `%s' has differing types: "
+                      "previously %s, REGISTER in %s"),
+                    *namep, stt_types [type], bfd_get_filename (abfd));
+                 return false;
+               }
+
+             p->name = bfd_hash_allocate (&info->hash->table,
+                                          strlen (*namep) + 1);
+             if (!p->name)
+               return false;
+
+             strcpy (p->name, *namep);
+           }
+         else
+           p->name = "";
+         p->bind = ELF_ST_BIND (sym->st_info);
+         p->abfd = abfd;
+         p->shndx = sym->st_shndx;
+       }
+      else
+       {
+         if (p->bind == STB_WEAK
+             && ELF_ST_BIND (sym->st_info) == STB_GLOBAL)
+           {
+             p->bind = STB_GLOBAL;
+             p->abfd = abfd;
+           }
+       }
+      *namep = NULL;
+      return true;
+    }
+  else if (! *namep || ! **namep)
+    return true;
+  else
+    {
+      int i;
+      struct sparc64_elf_app_reg *p;
+
+      p = sparc64_elf_hash_table(info)->app_regs;
+      for (i = 0; i < 4; i++, p++)
+       if (p->name != NULL && ! strcmp (p->name, *namep))
+         {
+           unsigned char type = ELF_ST_TYPE (sym->st_info);
+
+           if (type > STT_FUNC) type = 0;
+           (*_bfd_error_handler)
+             (_("Symbol `%s' has differing types: "
+                "REGISTER in %s, %s in %s"),
+              *namep, bfd_get_filename (p->abfd), stt_types [type],
+              bfd_get_filename (abfd));
+           return false;
+         }
+    }
+  return true;
+}
+
+/* This function takes care of emiting STT_REGISTER symbols
+   which we cannot easily keep in the symbol hash table.  */
+
+static boolean
+sparc64_elf_output_arch_syms (output_bfd, info, finfo, func)
+     bfd *output_bfd ATTRIBUTE_UNUSED;
+     struct bfd_link_info *info;
+     PTR finfo;
+     boolean (*func) PARAMS ((PTR, const char *,
+                             Elf_Internal_Sym *, asection *));
+{
+  int reg;
+  struct sparc64_elf_app_reg *app_regs =
+    sparc64_elf_hash_table(info)->app_regs;
+  Elf_Internal_Sym sym;
+
+  /* We arranged in size_dynamic_sections to put the STT_REGISTER entries
+     at the end of the dynlocal list, so they came at the end of the local
+     symbols in the symtab.  Except that they aren't STB_LOCAL, so we need
+     to back up symtab->sh_info.  */
+  if (elf_hash_table (info)->dynlocal)
+    {
+      bfd * dynobj = elf_hash_table (info)->dynobj;
+      asection *dynsymsec = bfd_get_section_by_name (dynobj, ".dynsym");
+      struct elf_link_local_dynamic_entry *e;
+
+      for (e = elf_hash_table (info)->dynlocal; e ; e = e->next)
+       if (e->input_indx == -1)
+         break;
+      if (e)
+       {
+         elf_section_data (dynsymsec->output_section)->this_hdr.sh_info
+           = e->dynindx;
+       }
+    }
+
+  if (info->strip == strip_all)
+    return true;
+
+  for (reg = 0; reg < 4; reg++)
+    if (app_regs [reg].name != NULL)
+      {
+       if (info->strip == strip_some
+           && bfd_hash_lookup (info->keep_hash,
+                               app_regs [reg].name,
+                               false, false) == NULL)
+         continue;
+
+       sym.st_value = reg < 2 ? reg + 2 : reg + 4;
+       sym.st_size = 0;
+       sym.st_other = 0;
+       sym.st_info = ELF_ST_INFO (app_regs [reg].bind, STT_REGISTER);
+       sym.st_shndx = app_regs [reg].shndx;
+       if (! (*func) (finfo, app_regs [reg].name, &sym,
+                      sym.st_shndx == SHN_ABS
+                        ? bfd_abs_section_ptr : bfd_und_section_ptr))
+         return false;
+      }
+
+  return true;
+}
+
+static int
+sparc64_elf_get_symbol_type (elf_sym, type)
+     Elf_Internal_Sym * elf_sym;
+     int type;
+{
+  if (ELF_ST_TYPE (elf_sym->st_info) == STT_REGISTER)
+    return STT_REGISTER;
+  else
+    return type;
+}
+
+/* A STB_GLOBAL,STT_REGISTER symbol should be BSF_GLOBAL
+   even in SHN_UNDEF section.  */
+
+static void
+sparc64_elf_symbol_processing (abfd, asym)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asymbol *asym;
+{
+  elf_symbol_type *elfsym;
+
+  elfsym = (elf_symbol_type *) asym;
+  if (elfsym->internal_elf_sym.st_info
+      == ELF_ST_INFO (STB_GLOBAL, STT_REGISTER))
+    {
+      asym->flags |= BSF_GLOBAL;
+    }
+}
+
 /* Adjust a symbol defined by a dynamic object and referenced by a
    regular object.  The current definition is in some section of the
    dynamic object, but we're not including those sections.  We have to
@@ -1476,7 +1754,7 @@ sparc64_elf_size_dynamic_sections (output_bfd, info)
 
       if (strip)
        {
-         _bfd_strip_section_from_output (s);
+         _bfd_strip_section_from_output (info, s);
          continue;
        }
 
@@ -1495,6 +1773,11 @@ sparc64_elf_size_dynamic_sections (output_bfd, info)
         must add the entries now so that we get the correct size for
         the .dynamic section.  The DT_DEBUG entry is filled in by the
         dynamic linker and used by the debugger.  */
+      int reg;
+      struct sparc64_elf_app_reg * app_regs;
+      struct bfd_strtab_hash *dynstr;
+      struct elf_link_hash_table *eht = elf_hash_table (info);
+
       if (! info->shared)
        {
          if (! bfd_elf64_add_dynamic_entry (info, DT_DEBUG, 0))
@@ -1520,12 +1803,76 @@ sparc64_elf_size_dynamic_sections (output_bfd, info)
        {
          if (! bfd_elf64_add_dynamic_entry (info, DT_TEXTREL, 0))
            return false;
+         info->flags |= DF_TEXTREL;
        }
+
+      /* Add dynamic STT_REGISTER symbols and corresponding DT_SPARC_REGISTER
+        entries if needed.  */
+      app_regs = sparc64_elf_hash_table (info)->app_regs;
+      dynstr = eht->dynstr;
+
+      for (reg = 0; reg < 4; reg++)
+       if (app_regs [reg].name != NULL)
+         {
+           struct elf_link_local_dynamic_entry *entry, *e;
+             
+           if (! bfd_elf64_add_dynamic_entry (info, DT_SPARC_REGISTER, 0))
+             return false;
+
+           entry = (struct elf_link_local_dynamic_entry *)
+             bfd_hash_allocate (&info->hash->table, sizeof (*entry));
+           if (entry == NULL)
+             return false;
+
+           /* We cheat here a little bit: the symbol will not be local, so we
+              put it at the end of the dynlocal linked list.  We will fix it
+              later on, as we have to fix other fields anyway.  */
+           entry->isym.st_value = reg < 2 ? reg + 2 : reg + 4;
+           entry->isym.st_size = 0;
+           if (*app_regs [reg].name != '\0')
+             entry->isym.st_name
+               = _bfd_stringtab_add (dynstr, app_regs[reg].name, true, false);
+           else
+             entry->isym.st_name = 0;
+           entry->isym.st_other = 0;
+           entry->isym.st_info = ELF_ST_INFO (app_regs [reg].bind,
+                                              STT_REGISTER);
+           entry->isym.st_shndx = app_regs [reg].shndx;
+           entry->next = NULL;
+           entry->input_bfd = output_bfd;
+           entry->input_indx = -1;
+
+           if (eht->dynlocal == NULL)
+             eht->dynlocal = entry;
+           else
+             {
+               for (e = eht->dynlocal; e->next; e = e->next)
+                 ;
+               e->next = entry;
+             }
+           eht->dynsymcount++;
+         }
     }
 
   return true;
 }
 \f
+#define SET_SEC_DO_RELAX(section) do { elf_section_data(section)->tdata = (void *)1; } while (0)
+#define SEC_DO_RELAX(section) (elf_section_data(section)->tdata == (void *)1)
+
+/*ARGSUSED*/
+static boolean
+sparc64_elf_relax_section (abfd, section, link_info, again)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *section ATTRIBUTE_UNUSED;
+     struct bfd_link_info *link_info ATTRIBUTE_UNUSED;
+     boolean *again;
+{
+  *again = false;
+  SET_SEC_DO_RELAX (section);
+  return true;
+}
+\f
 /* Relocate a SPARC64 ELF section.  */
 
 static boolean
@@ -1569,7 +1916,7 @@ sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
     {
       int r_type;
       reloc_howto_type *howto;
-      long r_symndx;
+      unsigned long r_symndx;
       struct elf_link_hash_entry *h;
       Elf_Internal_Sym *sym;
       asection *sec;
@@ -1720,13 +2067,17 @@ sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
            }
          else if (h->root.type == bfd_link_hash_undefweak)
            relocation = 0;
-         else if (info->shared && !info->symbolic && !info->no_undefined)
+         else if (info->shared && !info->symbolic
+                  && !info->no_undefined
+                  && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
            relocation = 0;
          else
            {
              if (! ((*info->callbacks->undefined_symbol)
                     (info, h->root.root.string, input_bfd,
-                     input_section, rel->r_offset)))
+                     input_section, rel->r_offset,
+                     (!info->shared || info->no_undefined
+                      || ELF_ST_VISIBILITY (h->other)))))
                return false;
              relocation = 0;
            }
@@ -1921,15 +2272,7 @@ sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                                          ELF64_R_TYPE_INFO (
                                            ELF64_R_TYPE_DATA (rel->r_info),
                                                               r_type));
-
-                       /* For non-RELATIVE dynamic relocations, we keep the
-                          same symbol, and so generally the same addend.  But
-                          we do need to adjust those relocations referencing
-                          sections.  */
-                       outrel.r_addend = rel->r_addend;
-                       if (r_symndx < symtab_hdr->sh_info
-                           && ELF_ST_TYPE (sym->st_info) == STT_SECTION)
-                         outrel.r_addend += sec->output_offset+sym->st_value;
+                       outrel.r_addend = relocation + rel->r_addend;
                      }
                  }
 
@@ -2071,6 +2414,8 @@ sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          relocation = (splt->output_section->vma
                        + splt->output_offset
                        + sparc64_elf_plt_entry_offset (h->plt.offset));
+         if (r_type == R_SPARC_WPLT30)
+           goto do_wplt30;
          goto do_default;
 
        case R_SPARC_OLO10:
@@ -2146,6 +2491,97 @@ sparc64_elf_relocate_section (output_bfd, info, input_bfd, input_section,
          }
          break;
 
+       case R_SPARC_WDISP30:
+       do_wplt30:
+         if (SEC_DO_RELAX (input_section)
+             && rel->r_offset + 4 < input_section->_raw_size)
+           {
+#define G0             0
+#define O7             15
+#define XCC            (2 << 20)
+#define COND(x)                (((x)&0xf)<<25)
+#define CONDA          COND(0x8)
+#define INSN_BPA       (F2(0,1) | CONDA | BPRED | XCC)
+#define INSN_BA                (F2(0,2) | CONDA)
+#define INSN_OR                F3(2, 0x2, 0)
+#define INSN_NOP       F2(0,4)
+
+             bfd_vma x, y;
+
+             /* If the instruction is a call with either:
+                restore
+                arithmetic instruction with rd == %o7
+                where rs1 != %o7 and rs2 if it is register != %o7
+                then we can optimize if the call destination is near
+                by changing the call into a branch always.  */
+             x = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             y = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+             if ((x & OP(~0)) == OP(1) && (y & OP(~0)) == OP(2))
+               {
+                 if (((y & OP3(~0)) == OP3(0x3d) /* restore */
+                      || ((y & OP3(0x28)) == 0 /* arithmetic */
+                          && (y & RD(~0)) == RD(O7)))
+                     && (y & RS1(~0)) != RS1(O7)
+                     && ((y & F3I(~0))
+                         || (y & RS2(~0)) != RS2(O7)))
+                   {
+                     bfd_vma reloc;
+
+                     reloc = relocation + rel->r_addend - rel->r_offset;
+                     reloc -= (input_section->output_section->vma
+                               + input_section->output_offset);
+                     if (reloc & 3)
+                       goto do_default;
+
+                     /* Ensure the branch fits into simm22.  */
+                     if ((reloc & ~(bfd_vma)0x7fffff)
+                          && ((reloc | 0x7fffff) != MINUS_ONE))
+                       goto do_default;
+                     reloc >>= 2;
+
+                     /* Check whether it fits into simm19.  */
+                     if ((reloc & 0x3c0000) == 0
+                         || (reloc & 0x3c0000) == 0x3c0000)
+                       x = INSN_BPA | (reloc & 0x7ffff); /* ba,pt %xcc */
+                     else
+                       x = INSN_BA | (reloc & 0x3fffff); /* ba */
+                     bfd_put_32 (input_bfd, x, contents + rel->r_offset);
+                     r = bfd_reloc_ok;
+                     if (rel->r_offset >= 4
+                         && (y & (0xffffffff ^ RS1(~0)))
+                            == (INSN_OR | RD(O7) | RS2(G0)))
+                       {
+                         bfd_vma z;
+                         unsigned int reg;
+
+                         z = bfd_get_32 (input_bfd,
+                                         contents + rel->r_offset - 4);
+                         if ((z & (0xffffffff ^ RD(~0)))
+                             != (INSN_OR | RS1(O7) | RS2(G0)))
+                           break;
+
+                         /* The sequence was
+                            or %o7, %g0, %rN
+                            call foo
+                            or %rN, %g0, %o7
+
+                            If call foo was replaced with ba, replace
+                            or %rN, %g0, %o7 with nop.  */
+
+                         reg = (y & RS1(~0)) >> 14;
+                         if (reg != ((z & RD(~0)) >> 25)
+                             || reg == G0 || reg == O7)
+                           break;
+
+                         bfd_put_32 (input_bfd, INSN_NOP,
+                                     contents + rel->r_offset + 4);
+                       }
+                     break;
+                   }
+               }
+           }
+         /* FALLTHROUGH */
+
        default:
        do_default:
          r = _bfd_final_link_relocate (howto, input_bfd, input_section,
@@ -2244,7 +2680,8 @@ sparc64_elf_finish_dynamic_symbol (output_bfd, info, h, sym)
        {
          int max = splt->_raw_size / PLT_ENTRY_SIZE;
          rela.r_offset = sparc64_elf_plt_ptr_offset (h->plt.offset, max);
-         rela.r_addend = -(sparc64_elf_plt_entry_offset (h->plt.offset) + 4);
+         rela.r_addend = -(sparc64_elf_plt_entry_offset (h->plt.offset) + 4)
+                         -(splt->output_section->vma + splt->output_offset);
        }
       rela.r_offset += (splt->output_section->vma + splt->output_offset);
       rela.r_info = ELF64_R_INFO (h->dynindx, R_SPARC_JMP_SLOT);
@@ -2346,6 +2783,7 @@ sparc64_elf_finish_dynamic_sections (output_bfd, info)
      struct bfd_link_info *info;
 {
   bfd *dynobj;
+  int stt_regidx = -1;
   asection *sdyn;
   asection *sgot;
 
@@ -2376,6 +2814,17 @@ sparc64_elf_finish_dynamic_sections (output_bfd, info)
            case DT_PLTGOT:   name = ".plt"; size = false; break;
            case DT_PLTRELSZ: name = ".rela.plt"; size = true; break;
            case DT_JMPREL:   name = ".rela.plt"; size = false; break;
+           case DT_SPARC_REGISTER:
+             if (stt_regidx == -1)
+               {
+                 stt_regidx =
+                   _bfd_elf_link_lookup_local_dynindx (info, output_bfd, -1);
+                 if (stt_regidx == -1)
+                   return false;
+               }
+             dyn.d_un.d_val = stt_regidx++;
+             bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
+             /* fallthrough */
            default:          name = NULL; size = false; break;
            }
 
@@ -2466,25 +2915,40 @@ sparc64_elf_merge_private_bfd_data (ibfd, obfd)
     {
       error = false;
   
-      old_flags |= (new_flags & (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1));
-      new_flags |= (old_flags & (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1));
-      if ((old_flags & (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1)) ==
-           (EF_SPARC_SUN_US1|EF_SPARC_HAL_R1))
-        {
-          error = true;
-          (*_bfd_error_handler)
-            (_("%s: linking UltraSPARC specific with HAL specific code"),
-             bfd_get_filename (ibfd));
-        }
-        
-      /* Choose the most restrictive memory ordering */
-      old_mm = (old_flags & EF_SPARCV9_MM);
-      new_mm = (new_flags & EF_SPARCV9_MM);
-      old_flags &= ~EF_SPARCV9_MM;
-      new_flags &= ~EF_SPARCV9_MM;
-      if (new_mm < old_mm) old_mm = new_mm;
-      old_flags |= old_mm;
-      new_flags |= old_mm;
+      if ((ibfd->flags & DYNAMIC) != 0)
+       {
+         /* We don't want dynamic objects memory ordering and
+            architecture to have any role. That's what dynamic linker
+            should do.  */
+         new_flags &= ~(EF_SPARCV9_MM | EF_SPARC_SUN_US1 | EF_SPARC_HAL_R1);
+         new_flags |= (old_flags
+                       & (EF_SPARCV9_MM
+                          | EF_SPARC_SUN_US1
+                          | EF_SPARC_HAL_R1));
+       }
+      else
+       {
+         /* Choose the highest architecture requirements.  */
+         old_flags |= (new_flags & (EF_SPARC_SUN_US1 | EF_SPARC_HAL_R1));
+         new_flags |= (old_flags & (EF_SPARC_SUN_US1 | EF_SPARC_HAL_R1));
+         if ((old_flags & (EF_SPARC_SUN_US1 | EF_SPARC_HAL_R1))
+             == (EF_SPARC_SUN_US1 | EF_SPARC_HAL_R1))
+           {
+             error = true;
+             (*_bfd_error_handler)
+               (_("%s: linking UltraSPARC specific with HAL specific code"),
+                bfd_get_filename (ibfd));
+           }
+         /* Choose the most restrictive memory ordering.  */
+         old_mm = (old_flags & EF_SPARCV9_MM);
+         new_mm = (new_flags & EF_SPARCV9_MM);
+         old_flags &= ~EF_SPARCV9_MM;
+         new_flags &= ~EF_SPARCV9_MM;
+         if (new_mm < old_mm)
+           old_mm = new_mm;
+         old_flags |= old_mm;
+         new_flags |= old_mm;
+       }
 
       /* Warn about any other mismatches */
       if (new_flags != old_flags)
@@ -2505,7 +2969,34 @@ sparc64_elf_merge_private_bfd_data (ibfd, obfd)
     }
   return true;
 }
+\f
+/* Print a STT_REGISTER symbol to file FILE.  */
 
+static const char *
+sparc64_elf_print_symbol_all (abfd, filep, symbol)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     PTR filep;
+     asymbol *symbol;
+{
+  FILE *file = (FILE *) filep;
+  int reg, type;
+  
+  if (ELF_ST_TYPE (((elf_symbol_type *) symbol)->internal_elf_sym.st_info)
+      != STT_REGISTER)
+    return NULL;
+
+  reg = ((elf_symbol_type *) symbol)->internal_elf_sym.st_value;
+  type = symbol->flags;
+  fprintf (file, "REG_%c%c%11s%c%c    R", "GOLI" [reg / 8], '0' + (reg & 7), "",
+                ((type & BSF_LOCAL)
+                 ? (type & BSF_GLOBAL) ? '!' : 'l'
+                 : (type & BSF_GLOBAL) ? 'g' : ' '),
+                (type & BSF_WEAK) ? 'w' : ' ');
+  if (symbol->name == NULL || symbol->name [0] == '\0')
+    return "#scratch";
+  else
+    return symbol->name;
+}
 \f
 /* Set the right machine number for a SPARC64 ELF file.  */
 
@@ -2535,7 +3026,7 @@ const struct elf_size_info sparc64_elf_size_info =
   sizeof (Elf64_External_Sym),
   sizeof (Elf64_External_Dyn),
   sizeof (Elf_External_Note),
-  8,           /* hash-table entry size */
+  4,           /* hash-table entry size */
   /* internal relocations per external relocations.
      For link purposes we use just 1 internal per
      1 external, for assembly and slurp symbol table
@@ -2570,6 +3061,9 @@ const struct elf_size_info sparc64_elf_size_info =
 /* This is the value that we used before the ABI was released.  */
 #define ELF_MACHINE_ALT1 EM_OLD_SPARCV9
 
+#define bfd_elf64_bfd_link_hash_table_create \
+  sparc64_elf_bfd_link_hash_table_create
+  
 #define elf_info_to_howto \
   sparc64_elf_info_to_howto
 #define bfd_elf64_get_reloc_upper_bound \
@@ -2580,9 +3074,17 @@ const struct elf_size_info sparc64_elf_size_info =
   sparc64_elf_canonicalize_dynamic_reloc
 #define bfd_elf64_bfd_reloc_type_lookup \
   sparc64_elf_reloc_type_lookup
+#define bfd_elf64_bfd_relax_section \
+  sparc64_elf_relax_section
 
 #define elf_backend_create_dynamic_sections \
   _bfd_elf_create_dynamic_sections
+#define elf_backend_add_symbol_hook \
+  sparc64_elf_add_symbol_hook
+#define elf_backend_get_symbol_type \
+  sparc64_elf_get_symbol_type
+#define elf_backend_symbol_processing \
+  sparc64_elf_symbol_processing
 #define elf_backend_check_relocs \
   sparc64_elf_check_relocs
 #define elf_backend_adjust_dynamic_symbol \
@@ -2595,6 +3097,10 @@ const struct elf_size_info sparc64_elf_size_info =
   sparc64_elf_finish_dynamic_symbol
 #define elf_backend_finish_dynamic_sections \
   sparc64_elf_finish_dynamic_sections
+#define elf_backend_print_symbol_all \
+  sparc64_elf_print_symbol_all
+#define elf_backend_output_arch_syms \
+  sparc64_elf_output_arch_syms
 
 #define bfd_elf64_bfd_merge_private_bfd_data \
   sparc64_elf_merge_private_bfd_data
This page took 0.033127 seconds and 4 git commands to generate.