gas: Extend .symver directive
[deliverable/binutils-gdb.git] / gas / config / obj-elf.c
index d38721bf05080318344398e86fddca9cd72434f4..409ea4d6bed5ef4542bc772e9b7b372f9026bc1c 100644 (file)
@@ -1,7 +1,5 @@
 /* ELF object file format
 /* ELF object file format
-   Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 1992-2020 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
 
    This file is part of GAS, the GNU Assembler.
 
@@ -25,7 +23,6 @@
 #include "safe-ctype.h"
 #include "subsegs.h"
 #include "obstack.h"
 #include "safe-ctype.h"
 #include "subsegs.h"
 #include "obstack.h"
-#include "struc-symbol.h"
 #include "dwarf2dbg.h"
 
 #ifndef ECOFF_DEBUGGING
 #include "dwarf2dbg.h"
 
 #ifndef ECOFF_DEBUGGING
@@ -36,6 +33,7 @@
 
 #ifdef NEED_ECOFF_DEBUG
 #include "ecoff.h"
 
 #ifdef NEED_ECOFF_DEBUG
 #include "ecoff.h"
+#include "bfd/ecoff-bfd.h"
 #endif
 
 #ifdef TC_ALPHA
 #endif
 
 #ifdef TC_ALPHA
 #include "elf/ppc.h"
 #endif
 
 #include "elf/ppc.h"
 #endif
 
-#ifdef TC_I370
-#include "elf/i370.h"
-#endif
-
 #ifdef TC_I386
 #include "elf/x86-64.h"
 #endif
 #ifdef TC_I386
 #include "elf/x86-64.h"
 #endif
 #include "elf/mep.h"
 #endif
 
 #include "elf/mep.h"
 #endif
 
+#ifdef TC_NIOS2
+#include "elf/nios2.h"
+#endif
+
+#ifdef TC_PRU
+#include "elf/pru.h"
+#endif
+
 static void obj_elf_line (int);
 static void obj_elf_size (int);
 static void obj_elf_type (int);
 static void obj_elf_line (int);
 static void obj_elf_size (int);
 static void obj_elf_type (int);
@@ -72,6 +74,7 @@ static void obj_elf_visibility (int);
 static void obj_elf_symver (int);
 static void obj_elf_subsection (int);
 static void obj_elf_popsection (int);
 static void obj_elf_symver (int);
 static void obj_elf_subsection (int);
 static void obj_elf_popsection (int);
+static void obj_elf_gnu_attribute (int);
 static void obj_elf_tls_common (int);
 static void obj_elf_lcomm (int);
 static void obj_elf_struct (int);
 static void obj_elf_tls_common (int);
 static void obj_elf_lcomm (int);
 static void obj_elf_struct (int);
@@ -110,15 +113,18 @@ static const pseudo_typeS elf_pseudo_table[] =
   {"subsection", obj_elf_subsection, 0},
 
   /* These are GNU extensions to aid in garbage collecting C++ vtables.  */
   {"subsection", obj_elf_subsection, 0},
 
   /* These are GNU extensions to aid in garbage collecting C++ vtables.  */
-  {"vtable_inherit", (void (*) (int)) &obj_elf_vtable_inherit, 0},
-  {"vtable_entry", (void (*) (int)) &obj_elf_vtable_entry, 0},
+  {"vtable_inherit", obj_elf_vtable_inherit, 0},
+  {"vtable_entry", obj_elf_vtable_entry, 0},
+
+  /* A GNU extension for object attributes.  */
+  {"gnu_attribute", obj_elf_gnu_attribute, 0},
 
   /* These are used for dwarf.  */
   {"2byte", cons, 2},
   {"4byte", cons, 4},
   {"8byte", cons, 8},
   /* These are used for dwarf2.  */
 
   /* These are used for dwarf.  */
   {"2byte", cons, 2},
   {"4byte", cons, 4},
   {"8byte", cons, 8},
   /* These are used for dwarf2.  */
-  { "file", (void (*) (int)) dwarf2_directive_file, 0 },
+  { "file", dwarf2_directive_file, 0 },
   { "loc",  dwarf2_directive_loc,  0 },
   { "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
 
   { "loc",  dwarf2_directive_loc,  0 },
   { "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
 
@@ -154,6 +160,7 @@ static const pseudo_typeS ecoff_debug_pseudo_table[] =
   { "etype",   ecoff_directive_type,   0 },
 
   /* ECOFF specific debugging information.  */
   { "etype",   ecoff_directive_type,   0 },
 
   /* ECOFF specific debugging information.  */
+  { "aent",    ecoff_directive_ent,    1 },
   { "begin",   ecoff_directive_begin,  0 },
   { "bend",    ecoff_directive_bend,   0 },
   { "end",     ecoff_directive_end,    0 },
   { "begin",   ecoff_directive_begin,  0 },
   { "bend",    ecoff_directive_bend,   0 },
   { "end",     ecoff_directive_end,    0 },
@@ -254,13 +261,15 @@ elf_sec_sym_ok_for_reloc (asection *sec)
 void
 elf_file_symbol (const char *s, int appfile)
 {
 void
 elf_file_symbol (const char *s, int appfile)
 {
+  asymbol *bsym;
+
   if (!appfile
       || symbol_rootP == NULL
   if (!appfile
       || symbol_rootP == NULL
-      || symbol_rootP->bsym == NULL
-      || (symbol_rootP->bsym->flags & BSF_FILE) == 0)
+      || (bsym = symbol_get_bfdsym (symbol_rootP)) == NULL
+      || (bsym->flags & BSF_FILE) == 0)
     {
       symbolS *sym;
     {
       symbolS *sym;
-      unsigned int name_length;
+      size_t name_length;
 
       sym = symbol_new (s, absolute_section, 0, NULL);
       symbol_set_frag (sym, &zero_address_frag);
 
       sym = symbol_new (s, absolute_section, 0, NULL);
       symbol_set_frag (sym, &zero_address_frag);
@@ -276,14 +285,17 @@ elf_file_symbol (const char *s, int appfile)
 
       symbol_get_bfdsym (sym)->flags |= BSF_FILE;
 
 
       symbol_get_bfdsym (sym)->flags |= BSF_FILE;
 
-      if (symbol_rootP != sym)
+      if (symbol_rootP != sym
+         && ((bsym = symbol_get_bfdsym (symbol_rootP)) == NULL
+             || (bsym->flags & BSF_FILE) == 0))
        {
          symbol_remove (sym, &symbol_rootP, &symbol_lastP);
          symbol_insert (sym, symbol_rootP, &symbol_rootP, &symbol_lastP);
        {
          symbol_remove (sym, &symbol_rootP, &symbol_lastP);
          symbol_insert (sym, symbol_rootP, &symbol_rootP, &symbol_lastP);
+       }
+
 #ifdef DEBUG
 #ifdef DEBUG
-         verify_symbol_chain (symbol_rootP, symbol_lastP);
+      verify_symbol_chain (symbol_rootP, symbol_lastP);
 #endif
 #endif
-       }
     }
 
 #ifdef NEED_ECOFF_DEBUG
     }
 
 #ifdef NEED_ECOFF_DEBUG
@@ -400,11 +412,10 @@ get_sym_from_input_line_and_check (void)
   char c;
   symbolS *sym;
 
   char c;
   symbolS *sym;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (& name);
   sym = symbol_find_or_make (name);
   *input_line_pointer = c;
   sym = symbol_find_or_make (name);
   *input_line_pointer = c;
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
 
   /* There is no symbol name if input_line_pointer has not moved.  */
   if (name == input_line_pointer)
 
   /* There is no symbol name if input_line_pointer has not moved.  */
   if (name == input_line_pointer)
@@ -447,7 +458,6 @@ obj_elf_weak (int ignore ATTRIBUTE_UNUSED)
       symbolP = get_sym_from_input_line_and_check ();
       c = *input_line_pointer;
       S_SET_WEAK (symbolP);
       symbolP = get_sym_from_input_line_and_check ();
       c = *input_line_pointer;
       S_SET_WEAK (symbolP);
-      symbol_get_obj (symbolP)->local = 1;
       if (c == ',')
        {
          input_line_pointer++;
       if (c == ',')
        {
          input_line_pointer++;
@@ -511,13 +521,26 @@ static struct section_stack *section_stack;
 static bfd_boolean
 get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 {
 static bfd_boolean
 get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 {
-  const char *gname = (const char *) inf;
+  struct elf_section_match *match = (struct elf_section_match *) inf;
+  const char *gname = match->group_name;
   const char *group_name = elf_group_name (sec);
   const char *group_name = elf_group_name (sec);
-
-  return (group_name == gname
-         || (group_name != NULL
-             && gname != NULL
-             && strcmp (group_name, gname) == 0));
+  const char *linked_to_symbol_name
+    = sec->map_head.linked_to_symbol_name;
+  unsigned int info = elf_section_data (sec)->this_hdr.sh_info;
+
+  return (info == match->info
+         && ((bfd_section_flags (sec) & SEC_ASSEMBLER_SECTION_ID)
+              == (match->flags & SEC_ASSEMBLER_SECTION_ID))
+         && sec->section_id == match->section_id
+         && (group_name == gname
+             || (group_name != NULL
+                 && gname != NULL
+                 && strcmp (group_name, gname) == 0))
+         && (linked_to_symbol_name == match->linked_to_symbol_name
+             || (linked_to_symbol_name != NULL
+                 && match->linked_to_symbol_name != NULL
+                 && strcmp (linked_to_symbol_name,
+                            match->linked_to_symbol_name) == 0)));
 }
 
 /* Handle the .section pseudo-op.  This code supports two different
 }
 
 /* Handle the .section pseudo-op.  This code supports two different
@@ -540,10 +563,10 @@ get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 
 void
 obj_elf_change_section (const char *name,
 
 void
 obj_elf_change_section (const char *name,
-                       int type,
+                       unsigned int type,
                        bfd_vma attr,
                        int entsize,
                        bfd_vma attr,
                        int entsize,
-                       const char *group_name,
+                       struct elf_section_match *match_p,
                        int linkonce,
                        int push)
 {
                        int linkonce,
                        int push)
 {
@@ -553,6 +576,12 @@ obj_elf_change_section (const char *name,
   const struct elf_backend_data *bed;
   const struct bfd_elf_special_section *ssect;
 
   const struct elf_backend_data *bed;
   const struct bfd_elf_special_section *ssect;
 
+  if (match_p == NULL)
+    {
+      static struct elf_section_match unused_match;
+      match_p = &unused_match;
+    }
+
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
 #endif
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
 #endif
@@ -561,7 +590,7 @@ obj_elf_change_section (const char *name,
   if (push)
     {
       struct section_stack *elt;
   if (push)
     {
       struct section_stack *elt;
-      elt = (struct section_stack *) xmalloc (sizeof (struct section_stack));
+      elt = XNEW (struct section_stack);
       elt->next = section_stack;
       elt->seg = now_seg;
       elt->prev_seg = previous_section;
       elt->next = section_stack;
       elt->seg = now_seg;
       elt->prev_seg = previous_section;
@@ -573,7 +602,7 @@ obj_elf_change_section (const char *name,
   previous_subsection = now_subseg;
 
   old_sec = bfd_get_section_by_name_if (stdoutput, name, get_section,
   previous_subsection = now_subseg;
 
   old_sec = bfd_get_section_by_name_if (stdoutput, name, get_section,
-                                       (void *) group_name);
+                                       (void *) match_p);
   if (old_sec)
     {
       sec = old_sec;
   if (old_sec)
     {
       sec = old_sec;
@@ -614,7 +643,9 @@ obj_elf_change_section (const char *name,
              && ssect->type != SHT_PREINIT_ARRAY)
            {
              /* We allow to specify any type for a .note section.  */
              && ssect->type != SHT_PREINIT_ARRAY)
            {
              /* We allow to specify any type for a .note section.  */
-             if (ssect->type != SHT_NOTE)
+             if (ssect->type != SHT_NOTE
+                 /* Processor and application defined types are allowed too.  */
+                 && type < SHT_LOPROC)
                as_warn (_("setting incorrect section type for %s"),
                         name);
            }
                as_warn (_("setting incorrect section type for %s"),
                         name);
            }
@@ -626,7 +657,8 @@ obj_elf_change_section (const char *name,
            }
        }
 
            }
        }
 
-      if (old_sec == NULL && (attr & ~ssect->attr) != 0)
+      if (old_sec == NULL && ((attr & ~(SHF_MASKOS | SHF_MASKPROC))
+                             & ~ssect->attr) != 0)
        {
          /* As a GNU extension, we permit a .note section to be
             allocatable.  If the linker sees an allocatable .note
        {
          /* As a GNU extension, we permit a .note section to be
             allocatable.  If the linker sees an allocatable .note
@@ -658,15 +690,24 @@ obj_elf_change_section (const char *name,
          /* A section on Alpha may have SHF_ALPHA_GPREL.  */
          else if ((attr & ~ssect->attr) == SHF_ALPHA_GPREL)
            override = TRUE;
          /* A section on Alpha may have SHF_ALPHA_GPREL.  */
          else if ((attr & ~ssect->attr) == SHF_ALPHA_GPREL)
            override = TRUE;
+#endif
+#ifdef TC_RX
+         else if (attr == (SHF_EXECINSTR | SHF_WRITE | SHF_ALLOC)
+                  && (ssect->type == SHT_INIT_ARRAY
+                      || ssect->type == SHT_FINI_ARRAY
+                      || ssect->type == SHT_PREINIT_ARRAY))
+           /* RX init/fini arrays can and should have the "awx" attributes set.  */
+           ;
 #endif
          else
            {
 #endif
          else
            {
-             if (group_name == NULL)
+             if (match_p->group_name == NULL)
                as_warn (_("setting incorrect section attributes for %s"),
                         name);
              override = TRUE;
            }
        }
                as_warn (_("setting incorrect section attributes for %s"),
                         name);
              override = TRUE;
            }
        }
+
       if (!override && old_sec == NULL)
        attr |= ssect->attr;
     }
       if (!override && old_sec == NULL)
        attr |= ssect->attr;
     }
@@ -696,15 +737,24 @@ obj_elf_change_section (const char *name,
        type = bfd_elf_get_default_section_type (flags);
       elf_section_type (sec) = type;
       elf_section_flags (sec) = attr;
        type = bfd_elf_get_default_section_type (flags);
       elf_section_type (sec) = type;
       elf_section_flags (sec) = attr;
+      elf_section_data (sec)->this_hdr.sh_info = match_p->info;
 
       /* Prevent SEC_HAS_CONTENTS from being inadvertently set.  */
       if (type == SHT_NOBITS)
        seg_info (sec)->bss = 1;
 
 
       /* Prevent SEC_HAS_CONTENTS from being inadvertently set.  */
       if (type == SHT_NOBITS)
        seg_info (sec)->bss = 1;
 
-      bfd_set_section_flags (stdoutput, sec, flags);
+      /* Set the section ID and flags.  */
+      sec->section_id = match_p->section_id;
+      flags |= match_p->flags;
+
+      /* Set the linked-to symbol name.  */
+      sec->map_head.linked_to_symbol_name
+       = match_p->linked_to_symbol_name;
+
+      bfd_set_section_flags (sec, flags);
       if (flags & SEC_MERGE)
        sec->entsize = entsize;
       if (flags & SEC_MERGE)
        sec->entsize = entsize;
-      elf_group_name (sec) = group_name;
+      elf_group_name (sec) = match_p->group_name;
 
       /* Add a symbol for this section to the symbol table.  */
       secsym = symbol_find (name);
 
       /* Add a symbol for this section to the symbol table.  */
       secsym = symbol_find (name);
@@ -717,7 +767,17 @@ obj_elf_change_section (const char *name,
     {
       if (type != SHT_NULL
          && (unsigned) type != elf_section_type (old_sec))
     {
       if (type != SHT_NULL
          && (unsigned) type != elf_section_type (old_sec))
-       as_warn (_("ignoring changed section type for %s"), name);
+       {
+         if (ssect != NULL)
+           /* This is a special section with known type.  User
+              assembly might get the section type wrong; Even high
+              profile projects like glibc have done so in the past.
+              So don't error in this case.  */
+           as_warn (_("ignoring changed section type for %s"), name);
+         else
+           /* Do error when assembly isn't self-consistent.  */
+           as_bad (_("changed section type for %s"), name);
+       }
 
       if (attr != 0)
        {
 
       if (attr != 0)
        {
@@ -729,9 +789,19 @@ obj_elf_change_section (const char *name,
                  | SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS
                  | SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
                  | SEC_THREAD_LOCAL)))
                  | SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS
                  | SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
                  | SEC_THREAD_LOCAL)))
-           as_warn (_("ignoring changed section attributes for %s"), name);
+           {
+             if (ssect != NULL)
+               as_warn (_("ignoring changed section attributes for %s"), name);
+             else
+               as_bad (_("changed section attributes for %s"), name);
+           }
+         else
+           /* FIXME: Maybe we should consider removing a previously set
+              processor or application specific attribute as suspicious ?  */
+           elf_section_flags (sec) = attr;
+
          if ((flags & SEC_MERGE) && old_sec->entsize != (unsigned) entsize)
          if ((flags & SEC_MERGE) && old_sec->entsize != (unsigned) entsize)
-           as_warn (_("ignoring changed section entity size for %s"), name);
+           as_bad (_("changed section entity size for %s"), name);
        }
     }
 
        }
     }
 
@@ -741,10 +811,11 @@ obj_elf_change_section (const char *name,
 }
 
 static bfd_vma
 }
 
 static bfd_vma
-obj_elf_parse_section_letters (char *str, size_t len, bfd_boolean *clone)
+obj_elf_parse_section_letters (char *str, size_t len,
+                              bfd_boolean *is_clone, bfd_vma *gnu_attr)
 {
   bfd_vma attr = 0;
 {
   bfd_vma attr = 0;
-  *clone = FALSE;
+  *is_clone = FALSE;
 
   while (len > 0)
     {
 
   while (len > 0)
     {
@@ -756,6 +827,9 @@ obj_elf_parse_section_letters (char *str, size_t len, bfd_boolean *clone)
        case 'e':
          attr |= SHF_EXCLUDE;
          break;
        case 'e':
          attr |= SHF_EXCLUDE;
          break;
+       case 'o':
+         attr |= SHF_LINK_ORDER;
+         break;
        case 'w':
          attr |= SHF_WRITE;
          break;
        case 'w':
          attr |= SHF_WRITE;
          break;
@@ -774,8 +848,11 @@ obj_elf_parse_section_letters (char *str, size_t len, bfd_boolean *clone)
        case 'T':
          attr |= SHF_TLS;
          break;
        case 'T':
          attr |= SHF_TLS;
          break;
+       case 'd':
+         *gnu_attr |= SHF_GNU_MBIND;
+         break;
        case '?':
        case '?':
-         *clone = TRUE;
+         *is_clone = TRUE;
          break;
        /* Compatibility.  */
        case 'm':
          break;
        /* Compatibility.  */
        case 'm':
@@ -789,16 +866,30 @@ obj_elf_parse_section_letters (char *str, size_t len, bfd_boolean *clone)
                }
              break;
            }
                }
              break;
            }
+         /* Fall through.  */
        default:
          {
        default:
          {
-           char *bad_msg = _("unrecognized .section attribute: want a,e,w,x,M,S,G,T");
+           const char *bad_msg = _("unrecognized .section attribute:"
+                                   " want a,e,o,w,x,M,S,G,T or number");
 #ifdef md_elf_section_letter
            bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);
            if (md_attr != (bfd_vma) -1)
              attr |= md_attr;
            else
 #endif
 #ifdef md_elf_section_letter
            bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);
            if (md_attr != (bfd_vma) -1)
              attr |= md_attr;
            else
 #endif
-             as_fatal ("%s", bad_msg);
+             if (ISDIGIT (*str))
+               {
+                 char * end;
+
+                 attr |= strtoul (str, & end, 0);
+                 /* Update str and len, allowing for the fact that
+                    we will execute str++ and len-- below.  */
+                 end --;
+                 len -= (end - str);
+                 str = end;
+               }
+             else
+               as_fatal ("%s", bad_msg);
          }
          break;
        }
          }
          break;
        }
@@ -832,6 +923,17 @@ obj_elf_section_type (char *str, size_t len, bfd_boolean warn)
   }
 #endif
 
   }
 #endif
 
+  if (ISDIGIT (*str))
+    {
+      char * end;
+      int type = strtoul (str, & end, 0);
+
+      if (warn && (size_t) (end - str) != len)
+       as_warn (_("extraneous characters at end of numeric section type"));
+
+      return type;
+    }
+
   if (warn)
     as_warn (_("unrecognized section type"));
   return 0;
   if (warn)
     as_warn (_("unrecognized section type"));
   return 0;
@@ -871,7 +973,7 @@ obj_elf_section_word (char *str, size_t len, int *type)
 }
 
 /* Get name of section.  */
 }
 
 /* Get name of section.  */
-char *
+const char *
 obj_elf_section_name (void)
 {
   char *name;
 obj_elf_section_name (void)
 {
   char *name;
@@ -901,9 +1003,28 @@ obj_elf_section_name (void)
          return NULL;
        }
 
          return NULL;
        }
 
-      name = (char *) xmalloc (end - input_line_pointer + 1);
-      memcpy (name, input_line_pointer, end - input_line_pointer);
-      name[end - input_line_pointer] = '\0';
+      name = xmemdup0 (input_line_pointer, end - input_line_pointer);
+
+      while (flag_sectname_subst)
+        {
+         char *subst = strchr (name, '%');
+         if (subst && subst[1] == 'S')
+           {
+             int oldlen = strlen (name);
+             int substlen = strlen (now_seg->name);
+             int newlen = oldlen - 2 + substlen;
+             char *newname = XNEWVEC (char, newlen + 1);
+             int headlen = subst - name;
+             memcpy (newname, name, headlen);
+             strcpy (newname + headlen, now_seg->name);
+             strcat (newname + headlen, subst + 2);
+             xfree (name);
+             name = newname;
+           }
+         else
+           break;
+       }
+
 #ifdef tc_canonicalize_section_name
       name = tc_canonicalize_section_name (name);
 #endif
 #ifdef tc_canonicalize_section_name
       name = tc_canonicalize_section_name (name);
 #endif
@@ -916,14 +1037,16 @@ obj_elf_section_name (void)
 void
 obj_elf_section (int push)
 {
 void
 obj_elf_section (int push)
 {
-  char *name, *group_name, *beg;
+  const char *name;
+  char *beg;
   int type, dummy;
   bfd_vma attr;
   int type, dummy;
   bfd_vma attr;
+  bfd_vma gnu_attr;
   int entsize;
   int linkonce;
   subsegT new_subsection = -1;
   int entsize;
   int linkonce;
   subsegT new_subsection = -1;
+  struct elf_section_match match;
 
 
-#ifndef TC_I370
   if (flag_mri)
     {
       char mri_type;
   if (flag_mri)
     {
       char mri_type;
@@ -943,14 +1066,27 @@ obj_elf_section (int push)
 
       return;
     }
 
       return;
     }
-#endif /* ! defined (TC_I370) */
 
   name = obj_elf_section_name ();
   if (name == NULL)
     return;
 
   name = obj_elf_section_name ();
   if (name == NULL)
     return;
+
+  memset (&match, 0, sizeof (match));
+
+  symbolS * sym;
+  if ((sym = symbol_find (name)) != NULL
+      && ! symbol_section_p (sym)
+      && S_IS_DEFINED (sym)
+      && ! S_IS_VOLATILE (sym)
+      && ! S_CAN_BE_REDEFINED (sym))
+    {
+      as_bad (_("section name '%s' already defined as another symbol"), name);
+      ignore_rest_of_line ();
+      return;
+    }
   type = SHT_NULL;
   attr = 0;
   type = SHT_NULL;
   attr = 0;
-  group_name = NULL;
+  gnu_attr = 0;
   entsize = 0;
   linkonce = 0;
 
   entsize = 0;
   linkonce = 0;
 
@@ -978,7 +1114,7 @@ obj_elf_section (int push)
 
       if (*input_line_pointer == '"')
        {
 
       if (*input_line_pointer == '"')
        {
-         bfd_boolean clone;
+         bfd_boolean is_clone;
 
          beg = demand_copy_C_string (&dummy);
          if (beg == NULL)
 
          beg = demand_copy_C_string (&dummy);
          if (beg == NULL)
@@ -986,7 +1122,8 @@ obj_elf_section (int push)
              ignore_rest_of_line ();
              return;
            }
              ignore_rest_of_line ();
              return;
            }
-         attr |= obj_elf_parse_section_letters (beg, strlen (beg), &clone);
+         attr |= obj_elf_parse_section_letters (beg, strlen (beg),
+                                                &is_clone, &gnu_attr);
 
          SKIP_WHITESPACE ();
          if (*input_line_pointer == ',')
 
          SKIP_WHITESPACE ();
          if (*input_line_pointer == ',')
@@ -1009,10 +1146,18 @@ obj_elf_section (int push)
                }
              else if (c == '@' || c == '%')
                {
                }
              else if (c == '@' || c == '%')
                {
-                 beg = ++input_line_pointer;
-                 c = get_symbol_end ();
-                 *input_line_pointer = c;
-                 type = obj_elf_section_type (beg, input_line_pointer - beg, TRUE);
+                 ++input_line_pointer;
+
+                 if (ISDIGIT (* input_line_pointer))
+                   type = strtoul (input_line_pointer, &input_line_pointer, 0);
+                 else
+                   {
+                     c = get_symbol_name (& beg);
+                     (void) restore_line_pointer (c);
+                     type = obj_elf_section_type (beg,
+                                                  input_line_pointer - beg,
+                                                  TRUE);
+                   }
                }
              else
                input_line_pointer = save;
                }
              else
                input_line_pointer = save;
@@ -1038,21 +1183,39 @@ obj_elf_section (int push)
              attr &= ~SHF_MERGE;
            }
 
              attr &= ~SHF_MERGE;
            }
 
-         if ((attr & SHF_GROUP) != 0 && clone)
+         if ((attr & SHF_LINK_ORDER) != 0 && *input_line_pointer == ',')
+           {
+             char c;
+             unsigned int length;
+             ++input_line_pointer;
+             SKIP_WHITESPACE ();
+             c = get_symbol_name (& beg);
+             (void) restore_line_pointer (c);
+             length = input_line_pointer - beg;
+             if (length)
+               match.linked_to_symbol_name = xmemdup0 (beg, length);
+           }
+
+         if ((attr & SHF_GROUP) != 0 && is_clone)
            {
              as_warn (_("? section flag ignored with G present"));
            {
              as_warn (_("? section flag ignored with G present"));
-             clone = FALSE;
+             is_clone = FALSE;
            }
          if ((attr & SHF_GROUP) != 0 && *input_line_pointer == ',')
            {
              ++input_line_pointer;
            }
          if ((attr & SHF_GROUP) != 0 && *input_line_pointer == ',')
            {
              ++input_line_pointer;
-             group_name = obj_elf_section_name ();
-             if (group_name == NULL)
+             match.group_name = obj_elf_section_name ();
+             if (match.group_name == NULL)
                attr &= ~SHF_GROUP;
                attr &= ~SHF_GROUP;
-             else if (strncmp (input_line_pointer, ",comdat", 7) == 0)
+             else if (*input_line_pointer == ',')
                {
                {
-                 input_line_pointer += 7;
-                 linkonce = 1;
+                 ++input_line_pointer;
+                 SKIP_WHITESPACE ();
+                 if (strncmp (input_line_pointer, "comdat", 6) == 0)
+                   {
+                     input_line_pointer += 6;
+                     linkonce = 1;
+                   }
                }
              else if (strncmp (name, ".gnu.linkonce", 13) == 0)
                linkonce = 1;
                }
              else if (strncmp (name, ".gnu.linkonce", 13) == 0)
                linkonce = 1;
@@ -1063,15 +1226,92 @@ obj_elf_section (int push)
              attr &= ~SHF_GROUP;
            }
 
              attr &= ~SHF_GROUP;
            }
 
-         if (clone)
+         if (is_clone)
            {
              const char *now_group = elf_group_name (now_seg);
              if (now_group != NULL)
                {
            {
              const char *now_group = elf_group_name (now_seg);
              if (now_group != NULL)
                {
-                 group_name = xstrdup (now_group);
+                 match.group_name = xstrdup (now_group);
                  linkonce = (now_seg->flags & SEC_LINK_ONCE) != 0;
                }
            }
                  linkonce = (now_seg->flags & SEC_LINK_ONCE) != 0;
                }
            }
+
+         if ((gnu_attr & SHF_GNU_MBIND) != 0 && *input_line_pointer == ',')
+           {
+             char *save = input_line_pointer;
+             ++input_line_pointer;
+             SKIP_WHITESPACE ();
+             if (ISDIGIT (* input_line_pointer))
+               {
+                 char *t = input_line_pointer;
+                 match.info = strtoul (input_line_pointer,
+                                       &input_line_pointer, 0);
+                 if (match.info == (unsigned int) -1)
+                   {
+                     as_warn (_("unsupported mbind section info: %s"), t);
+                     match.info = 0;
+                   }
+               }
+             else
+               input_line_pointer = save;
+           }
+
+         if (*input_line_pointer == ',')
+           {
+             char *save = input_line_pointer;
+             ++input_line_pointer;
+             SKIP_WHITESPACE ();
+             if (strncmp (input_line_pointer, "unique", 6) == 0)
+               {
+                 input_line_pointer += 6;
+                 SKIP_WHITESPACE ();
+                 if (*input_line_pointer == ',')
+                   {
+                     ++input_line_pointer;
+                     SKIP_WHITESPACE ();
+                     if (ISDIGIT (* input_line_pointer))
+                       {
+                         bfd_vma id;
+                         bfd_boolean overflow;
+                         char *t = input_line_pointer;
+                         if (sizeof (bfd_vma) <= sizeof (unsigned long))
+                           {
+                             errno = 0;
+                             id = strtoul (input_line_pointer,
+                                           &input_line_pointer, 0);
+                             overflow = (id == (unsigned long) -1
+                                         && errno == ERANGE);
+                           }
+                         else
+                           {
+                             id = bfd_scan_vma
+                               (input_line_pointer,
+                                (const char **) &input_line_pointer, 0);
+                             overflow = id == ~(bfd_vma) 0;
+                           }
+                         if (overflow || id > (unsigned int) -1)
+                           {
+                             char *linefeed, saved_char = 0;
+                             if ((linefeed = strchr (t, '\n')) != NULL)
+                               {
+                                 saved_char = *linefeed;
+                                 *linefeed = '\0';
+                               }
+                             as_bad (_("unsupported section id: %s"), t);
+                             if (saved_char)
+                               *linefeed = saved_char;
+                           }
+                         else
+                           {
+                             match.section_id = id;
+                             match.flags |= SEC_ASSEMBLER_SECTION_ID;
+                           }
+                       }
+                   }
+               }
+             else
+               input_line_pointer = save;
+           }
        }
       else
        {
        }
       else
        {
@@ -1086,11 +1326,12 @@ obj_elf_section (int push)
                  ignore_rest_of_line ();
                  return;
                }
                  ignore_rest_of_line ();
                  return;
                }
-             beg = ++input_line_pointer;
-             c = get_symbol_end ();
-             *input_line_pointer = c;
+             ++input_line_pointer;
+             c = get_symbol_name (& beg);
+             (void) restore_line_pointer (c);
 
 
-             attr |= obj_elf_section_word (beg, input_line_pointer - beg, & type);
+             attr |= obj_elf_section_word (beg, input_line_pointer - beg,
+                                           &type);
 
              SKIP_WHITESPACE ();
            }
 
              SKIP_WHITESPACE ();
            }
@@ -1099,10 +1340,29 @@ obj_elf_section (int push)
        }
     }
 
        }
     }
 
-done:
+ done:
   demand_empty_rest_of_line ();
 
   demand_empty_rest_of_line ();
 
-  obj_elf_change_section (name, type, attr, entsize, group_name, linkonce, push);
+  obj_elf_change_section (name, type, attr, entsize, &match, linkonce,
+                         push);
+
+  if ((gnu_attr & SHF_GNU_MBIND) != 0)
+    {
+      struct elf_backend_data *bed;
+
+      if ((attr & SHF_ALLOC) == 0)
+       as_bad (_("SHF_ALLOC isn't set for GNU_MBIND section: %s"), name);
+
+      bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput);
+      if (bed->elf_osabi == ELFOSABI_NONE)
+       bed->elf_osabi = ELFOSABI_GNU;
+      else if (bed->elf_osabi != ELFOSABI_GNU
+              && bed->elf_osabi != ELFOSABI_FREEBSD)
+       as_bad (_("GNU_MBIND section is supported only by GNU "
+                 "and FreeBSD targets"));
+      elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_mbind;
+    }
+  elf_section_flags (now_seg) |= gnu_attr;
 
   if (push && new_subsection != -1)
     subseg_set (now_seg, new_subsection);
 
   if (push && new_subsection != -1)
     subseg_set (now_seg, new_subsection);
@@ -1255,6 +1515,70 @@ obj_elf_line (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
   demand_empty_rest_of_line ();
 }
 
+static struct elf_versioned_name_list *
+obj_elf_find_and_add_versioned_name (const char *version_name,
+                                    const char *sym_name,
+                                    const char *ver,
+                                    struct elf_obj_sy *sy_obj)
+{
+  struct elf_versioned_name_list *versioned_name;
+  const char *p;
+
+  for (p = ver + 1; *p == ELF_VER_CHR; p++)
+    ;
+
+  /* NB: Since some tests in ld/testsuite/ld-elfvers have no version
+     names, we have to disable this.  */
+  if (0 && *p == '\0')
+    {
+      as_bad (_("missing version name in `%s' for symbol `%s'"),
+             version_name, sym_name);
+      return NULL;
+    }
+
+  versioned_name = sy_obj->versioned_name;
+
+  switch (p - ver)
+    {
+    case 1:
+    case 2:
+      break;
+    case 3:
+      if (sy_obj->rename)
+       {
+         if (strcmp (versioned_name->name, version_name) == 0)
+           return versioned_name;
+         else
+           {
+             as_bad (_("only one version name with `@@@' is allowed "
+                       "for symbol `%s'"), sym_name);
+             return NULL;
+           }
+       }
+      sy_obj->rename = TRUE;
+      break;
+    default:
+      as_bad (_("invalid version name '%s' for symbol `%s'"),
+             version_name, sym_name);
+      return NULL;
+    }
+
+  for (;
+       versioned_name != NULL;
+       versioned_name = versioned_name->next)
+    if (strcmp (versioned_name->name, version_name) == 0)
+      return versioned_name;
+
+  /* Add this versioned name to the head of the list,  */
+  versioned_name = (struct elf_versioned_name_list *)
+    xmalloc (sizeof (*versioned_name));
+  versioned_name->name = xstrdup (version_name);
+  versioned_name->next = sy_obj->versioned_name;
+  sy_obj->versioned_name = versioned_name;
+
+  return versioned_name;
+}
+
 /* This handles the .symver pseudo-op, which is used to specify a
    symbol version.  The syntax is ``.symver NAME,SYMVERNAME''.
    SYMVERNAME may contain ELF_VER_CHR ('@') characters.  This
 /* This handles the .symver pseudo-op, which is used to specify a
    symbol version.  The syntax is ``.symver NAME,SYMVERNAME''.
    SYMVERNAME may contain ELF_VER_CHR ('@') characters.  This
@@ -1265,9 +1589,12 @@ static void
 obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
 {
   char *name;
 obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
 {
   char *name;
+  const char *sym_name;
   char c;
   char old_lexat;
   symbolS *sym;
   char c;
   char old_lexat;
   symbolS *sym;
+  struct elf_obj_sy *sy_obj;
+  char *p;
 
   sym = get_sym_from_input_line_and_check ();
 
 
   sym = get_sym_from_input_line_and_check ();
 
@@ -1280,42 +1607,65 @@ obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
 
   ++input_line_pointer;
   SKIP_WHITESPACE ();
 
   ++input_line_pointer;
   SKIP_WHITESPACE ();
-  name = input_line_pointer;
 
   /* Temporarily include '@' in symbol names.  */
   old_lexat = lex_type[(unsigned char) '@'];
   lex_type[(unsigned char) '@'] |= LEX_NAME;
 
   /* Temporarily include '@' in symbol names.  */
   old_lexat = lex_type[(unsigned char) '@'];
   lex_type[(unsigned char) '@'] |= LEX_NAME;
-  c = get_symbol_end ();
+  c = get_symbol_name (& name);
   lex_type[(unsigned char) '@'] = old_lexat;
   lex_type[(unsigned char) '@'] = old_lexat;
+  sym_name = S_GET_NAME (sym);
 
 
-  if (symbol_get_obj (sym)->versioned_name == NULL)
+  if (S_IS_COMMON (sym))
     {
     {
-      symbol_get_obj (sym)->versioned_name = xstrdup (name);
+      as_bad (_("`%s' can't be versioned to common symbol '%s'"),
+             name, sym_name);
+      ignore_rest_of_line ();
+      return;
+    }
 
 
-      *input_line_pointer = c;
+  p = strchr (name, ELF_VER_CHR);
+  if (p == NULL)
+    {
+      as_bad (_("missing version name in `%s' for symbol `%s'"),
+             name, sym_name);
+      ignore_rest_of_line ();
+      return;
+    }
 
 
-      if (strchr (symbol_get_obj (sym)->versioned_name,
-                 ELF_VER_CHR) == NULL)
-       {
-         as_bad (_("missing version name in `%s' for symbol `%s'"),
-                 symbol_get_obj (sym)->versioned_name,
-                 S_GET_NAME (sym));
-         ignore_rest_of_line ();
-         return;
-       }
+  sy_obj = symbol_get_obj (sym);
+  if (obj_elf_find_and_add_versioned_name (name, sym_name,
+                                          p, sy_obj) == NULL)
+    {
+      sy_obj->bad_version = TRUE;
+      ignore_rest_of_line ();
+      return;
     }
     }
-  else
+
+  (void) restore_line_pointer (c);
+
+  if (*input_line_pointer == ',')
     {
     {
-      if (strcmp (symbol_get_obj (sym)->versioned_name, name))
+      char *save = input_line_pointer;
+
+      ++input_line_pointer;
+      SKIP_WHITESPACE ();
+      if (strncmp (input_line_pointer, "local", 5) == 0)
        {
        {
-         as_bad (_("multiple versions [`%s'|`%s'] for symbol `%s'"),
-                 name, symbol_get_obj (sym)->versioned_name,
-                 S_GET_NAME (sym));
-         ignore_rest_of_line ();
-         return;
+         input_line_pointer += 5;
+         sy_obj->visibility = visibility_local;
        }
        }
-
-      *input_line_pointer = c;
+      else if (strncmp (input_line_pointer, "hidden", 6) == 0)
+       {
+         input_line_pointer += 6;
+         sy_obj->visibility = visibility_hidden;
+       }
+      else if (strncmp (input_line_pointer, "remove", 6) == 0)
+       {
+         input_line_pointer += 6;
+         sy_obj->visibility = visibility_remove;
+       }
+      else
+       input_line_pointer = save;
     }
 
   demand_empty_rest_of_line ();
     }
 
   demand_empty_rest_of_line ();
@@ -1326,7 +1676,7 @@ obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
    syntax is ".vtable_inherit CHILDNAME, PARENTNAME".  */
 
 struct fix *
    syntax is ".vtable_inherit CHILDNAME, PARENTNAME".  */
 
 struct fix *
-obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
+obj_elf_get_vtable_inherit (void)
 {
   char *cname, *pname;
   symbolS *csym, *psym;
 {
   char *cname, *pname;
   symbolS *csym, *psym;
@@ -1335,8 +1685,7 @@ obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
   if (*input_line_pointer == '#')
     ++input_line_pointer;
 
   if (*input_line_pointer == '#')
     ++input_line_pointer;
 
-  cname = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (& cname);
   csym = symbol_find (cname);
 
   /* GCFIXME: should check that we don't have two .vtable_inherits for
   csym = symbol_find (cname);
 
   /* GCFIXME: should check that we don't have two .vtable_inherits for
@@ -1352,7 +1701,7 @@ obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
 
   *input_line_pointer = c;
 
 
   *input_line_pointer = c;
 
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
   if (*input_line_pointer != ',')
     {
       as_bad (_("expected comma after name in .vtable_inherit"));
   if (*input_line_pointer != ',')
     {
       as_bad (_("expected comma after name in .vtable_inherit"));
@@ -1375,10 +1724,9 @@ obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
     }
   else
     {
     }
   else
     {
-      pname = input_line_pointer;
-      c = get_symbol_end ();
+      c = get_symbol_name (& pname);
       psym = symbol_find_or_make (pname);
       psym = symbol_find_or_make (pname);
-      *input_line_pointer = c;
+      restore_line_pointer (c);
     }
 
   demand_empty_rest_of_line ();
     }
 
   demand_empty_rest_of_line ();
@@ -1392,12 +1740,21 @@ obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
                  0, psym, 0, 0, BFD_RELOC_VTABLE_INHERIT);
 }
 
                  0, psym, 0, 0, BFD_RELOC_VTABLE_INHERIT);
 }
 
+/* This is a version of obj_elf_get_vtable_inherit() that is
+   suitable for use in struct _pseudo_type tables.  */
+
+void
+obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
+{
+  (void) obj_elf_get_vtable_inherit ();
+}
+
 /* This handles the .vtable_entry pseudo-op, which is used to indicate
    to the linker that a vtable slot was used.  The syntax is
    ".vtable_entry tablename, offset".  */
 
 struct fix *
 /* This handles the .vtable_entry pseudo-op, which is used to indicate
    to the linker that a vtable slot was used.  The syntax is
    ".vtable_entry tablename, offset".  */
 
 struct fix *
-obj_elf_vtable_entry (int ignore ATTRIBUTE_UNUSED)
+obj_elf_get_vtable_entry (void)
 {
   symbolS *sym;
   offsetT offset;
 {
   symbolS *sym;
   offsetT offset;
@@ -1425,6 +1782,204 @@ obj_elf_vtable_entry (int ignore ATTRIBUTE_UNUSED)
                  BFD_RELOC_VTABLE_ENTRY);
 }
 
                  BFD_RELOC_VTABLE_ENTRY);
 }
 
+/* This is a version of obj_elf_get_vtable_entry() that is
+   suitable for use in struct _pseudo_type tables.  */
+
+void
+obj_elf_vtable_entry (int ignore ATTRIBUTE_UNUSED)
+{
+  (void) obj_elf_get_vtable_entry ();
+}
+
+#define skip_whitespace(str)  do { if (*(str) == ' ') ++(str); } while (0)
+
+static inline int
+skip_past_char (char ** str, char c)
+{
+  if (**str == c)
+    {
+      (*str)++;
+      return 0;
+    }
+  else
+    return -1;
+}
+#define skip_past_comma(str) skip_past_char (str, ',')
+
+/* A list of attributes that have been explicitly set by the assembly code.
+   VENDOR is the vendor id, BASE is the tag shifted right by the number
+   of bits in MASK, and bit N of MASK is set if tag BASE+N has been set.  */
+struct recorded_attribute_info {
+  struct recorded_attribute_info *next;
+  int vendor;
+  unsigned int base;
+  unsigned long mask;
+};
+static struct recorded_attribute_info *recorded_attributes;
+
+/* Record that we have seen an explicit specification of attribute TAG
+   for vendor VENDOR.  */
+
+static void
+record_attribute (int vendor, unsigned int tag)
+{
+  unsigned int base;
+  unsigned long mask;
+  struct recorded_attribute_info *rai;
+
+  base = tag / (8 * sizeof (rai->mask));
+  mask = 1UL << (tag % (8 * sizeof (rai->mask)));
+  for (rai = recorded_attributes; rai; rai = rai->next)
+    if (rai->vendor == vendor && rai->base == base)
+      {
+       rai->mask |= mask;
+       return;
+      }
+
+  rai = XNEW (struct recorded_attribute_info);
+  rai->next = recorded_attributes;
+  rai->vendor = vendor;
+  rai->base = base;
+  rai->mask = mask;
+  recorded_attributes = rai;
+}
+
+/* Return true if we have seen an explicit specification of attribute TAG
+   for vendor VENDOR.  */
+
+bfd_boolean
+obj_elf_seen_attribute (int vendor, unsigned int tag)
+{
+  unsigned int base;
+  unsigned long mask;
+  struct recorded_attribute_info *rai;
+
+  base = tag / (8 * sizeof (rai->mask));
+  mask = 1UL << (tag % (8 * sizeof (rai->mask)));
+  for (rai = recorded_attributes; rai; rai = rai->next)
+    if (rai->vendor == vendor && rai->base == base)
+      return (rai->mask & mask) != 0;
+  return FALSE;
+}
+
+/* Parse an attribute directive for VENDOR.
+   Returns the attribute number read, or zero on error.  */
+
+int
+obj_elf_vendor_attribute (int vendor)
+{
+  expressionS exp;
+  int type;
+  int tag;
+  unsigned int i = 0;
+  char *s = NULL;
+
+  /* Read the first number or name.  */
+  skip_whitespace (input_line_pointer);
+  s = input_line_pointer;
+  if (ISDIGIT (*input_line_pointer))
+    {
+      expression (& exp);
+      if (exp.X_op != O_constant)
+       goto bad;
+      tag = exp.X_add_number;
+    }
+  else
+    {
+      char *name;
+
+      /* A name may contain '_', but no other punctuation.  */
+      for (; ISALNUM (*input_line_pointer) || *input_line_pointer == '_';
+          ++input_line_pointer)
+       i++;
+      if (i == 0)
+       goto bad;
+
+      name = xstrndup (s, i);
+
+#ifndef CONVERT_SYMBOLIC_ATTRIBUTE
+#define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
+#endif
+
+      tag = CONVERT_SYMBOLIC_ATTRIBUTE (name);
+      if (tag == -1)
+       {
+         as_bad (_("Attribute name not recognised: %s"), name);
+         ignore_rest_of_line ();
+         free (name);
+         return 0;
+       }
+      free (name);
+    }
+
+  type = _bfd_elf_obj_attrs_arg_type (stdoutput, vendor, tag);
+
+  if (skip_past_comma (&input_line_pointer) == -1)
+    goto bad;
+  if (type & 1)
+    {
+      expression (& exp);
+      if (exp.X_op != O_constant)
+       {
+         as_bad (_("expected numeric constant"));
+         ignore_rest_of_line ();
+         return 0;
+       }
+      i = exp.X_add_number;
+    }
+  if ((type & 3) == 3
+      && skip_past_comma (&input_line_pointer) == -1)
+    {
+      as_bad (_("expected comma"));
+      ignore_rest_of_line ();
+      return 0;
+    }
+  if (type & 2)
+    {
+      int len;
+
+      skip_whitespace (input_line_pointer);
+      if (*input_line_pointer != '"')
+       goto bad_string;
+      s = demand_copy_C_string (&len);
+    }
+
+  record_attribute (vendor, tag);
+  switch (type & 3)
+    {
+    case 3:
+      bfd_elf_add_obj_attr_int_string (stdoutput, vendor, tag, i, s);
+      break;
+    case 2:
+      bfd_elf_add_obj_attr_string (stdoutput, vendor, tag, s);
+      break;
+    case 1:
+      bfd_elf_add_obj_attr_int (stdoutput, vendor, tag, i);
+      break;
+    default:
+      abort ();
+    }
+
+  demand_empty_rest_of_line ();
+  return tag;
+ bad_string:
+  as_bad (_("bad string constant"));
+  ignore_rest_of_line ();
+  return 0;
+ bad:
+  as_bad (_("expected <tag> , <value>"));
+  ignore_rest_of_line ();
+  return 0;
+}
+
+/* Parse a .gnu_attribute directive.  */
+
+static void
+obj_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+  obj_elf_vendor_attribute (OBJ_ATTR_GNU);
+}
+
 void
 elf_obj_read_begin_hook (void)
 {
 void
 elf_obj_read_begin_hook (void)
 {
@@ -1461,7 +2016,7 @@ elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
   if (srcelf->size)
     {
       if (destelf->size == NULL)
   if (srcelf->size)
     {
       if (destelf->size == NULL)
-       destelf->size = (expressionS *) xmalloc (sizeof (expressionS));
+       destelf->size = XNEW (expressionS);
       *destelf->size = *srcelf->size;
     }
   else
       *destelf->size = *srcelf->size;
     }
   else
@@ -1505,9 +2060,8 @@ obj_elf_version (int ignore ATTRIBUTE_UNUSED)
 
       /* Create the .note section.  */
       note_secp = subseg_new (".note", 0);
 
       /* Create the .note section.  */
       note_secp = subseg_new (".note", 0);
-      bfd_set_section_flags (stdoutput,
-                            note_secp,
-                            SEC_HAS_CONTENTS | SEC_READONLY);
+      bfd_set_section_flags (note_secp, SEC_HAS_CONTENTS | SEC_READONLY);
+      record_alignment (note_secp, 2);
 
       /* Process the version string.  */
       len = strlen (name) + 1;
 
       /* Process the version string.  */
       len = strlen (name) + 1;
@@ -1539,15 +2093,15 @@ obj_elf_version (int ignore ATTRIBUTE_UNUSED)
 static void
 obj_elf_size (int ignore ATTRIBUTE_UNUSED)
 {
 static void
 obj_elf_size (int ignore ATTRIBUTE_UNUSED)
 {
-  char *name = input_line_pointer;
-  char c = get_symbol_end ();
+  char *name;
+  char c = get_symbol_name (&name);
   char *p;
   expressionS exp;
   symbolS *sym;
 
   p = input_line_pointer;
   *p = c;
   char *p;
   expressionS exp;
   symbolS *sym;
 
   p = input_line_pointer;
   *p = c;
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
   if (*input_line_pointer != ',')
     {
       *p = 0;
   if (*input_line_pointer != ',')
     {
       *p = 0;
@@ -1578,8 +2132,7 @@ obj_elf_size (int ignore ATTRIBUTE_UNUSED)
     }
   else
     {
     }
   else
     {
-      symbol_get_obj (sym)->size =
-          (expressionS *) xmalloc (sizeof (expressionS));
+      symbol_get_obj (sym)->size = XNEW (expressionS);
       *symbol_get_obj (sym)->size = exp;
     }
   demand_empty_rest_of_line ();
       *symbol_get_obj (sym)->size = exp;
     }
   demand_empty_rest_of_line ();
@@ -1619,7 +2172,7 @@ obj_elf_type_name (char *cp)
       *input_line_pointer = '\0';
     }
   else
       *input_line_pointer = '\0';
     }
   else
-    *cp = get_symbol_end ();
+    *cp = get_symbol_name (&p);
 
   return p;
 }
 
   return p;
 }
@@ -1698,14 +2251,20 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
           || strcmp (type_name, "10") == 0
           || strcmp (type_name, "STT_GNU_IFUNC") == 0)
     {
           || strcmp (type_name, "10") == 0
           || strcmp (type_name, "STT_GNU_IFUNC") == 0)
     {
-      const struct elf_backend_data *bed;
+      struct elf_backend_data *bed;
 
 
-      bed = get_elf_backend_data (stdoutput);
-      if (!(bed->elf_osabi == ELFOSABI_LINUX
-           /* GNU/Linux is still using the default value 0.  */
-           || bed->elf_osabi == ELFOSABI_NONE))
-       as_bad (_("symbol type \"%s\" is supported only by GNU targets"),
-               type_name);
+      bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput);
+      if (bed->elf_osabi == ELFOSABI_NONE)
+       bed->elf_osabi = ELFOSABI_GNU;
+      else if (bed->elf_osabi != ELFOSABI_GNU
+              && bed->elf_osabi != ELFOSABI_FREEBSD)
+       as_bad (_("symbol type \"%s\" is supported only by GNU "
+                 "and FreeBSD targets"), type_name);
+      /* MIPS targets do not support IFUNCS.  */
+      else if (bed->target_id == MIPS_ELF_DATA)
+       as_bad (_("symbol type \"%s\" is not supported by "
+                    "MIPS targets"), type_name);
+      elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_ifunc;
       type = BSF_FUNCTION | BSF_GNU_INDIRECT_FUNCTION;
     }
   else if (strcmp (type_name, "gnu_unique_object") == 0)
       type = BSF_FUNCTION | BSF_GNU_INDIRECT_FUNCTION;
     }
   else if (strcmp (type_name, "gnu_unique_object") == 0)
@@ -1713,14 +2272,13 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
       struct elf_backend_data *bed;
 
       bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput);
       struct elf_backend_data *bed;
 
       bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput);
-      if (!(bed->elf_osabi == ELFOSABI_LINUX
-           /* GNU/Linux is still using the default value 0.  */
-           || bed->elf_osabi == ELFOSABI_NONE))
+      if (bed->elf_osabi == ELFOSABI_NONE)
+       bed->elf_osabi = ELFOSABI_GNU;
+      else if (bed->elf_osabi != ELFOSABI_GNU)
        as_bad (_("symbol type \"%s\" is supported only by GNU targets"),
                type_name);
        as_bad (_("symbol type \"%s\" is supported only by GNU targets"),
                type_name);
+      elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_unique;
       type = BSF_OBJECT | BSF_GNU_UNIQUE;
       type = BSF_OBJECT | BSF_GNU_UNIQUE;
-      /* PR 10549: Always set OSABI field to LINUX for objects containing unique symbols.  */
-      bed->elf_osabi = ELFOSABI_LINUX;
     }
 #ifdef md_elf_symbol_type
   else if ((type = md_elf_symbol_type (type_name, sym, elfsym)) != -1)
     }
 #ifdef md_elf_symbol_type
   else if ((type = md_elf_symbol_type (type_name, sym, elfsym)) != -1)
@@ -1734,7 +2292,38 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
   if (*input_line_pointer == '"')
     ++input_line_pointer;
 
   if (*input_line_pointer == '"')
     ++input_line_pointer;
 
-  elfsym->symbol.flags |= type;
+#ifdef md_elf_symbol_type_change
+  if (!md_elf_symbol_type_change (sym, elfsym, type))
+#endif
+    {
+      flagword mask = BSF_FUNCTION | BSF_OBJECT;
+
+      if (type != BSF_FUNCTION)
+       mask |= BSF_GNU_INDIRECT_FUNCTION;
+      if (type != BSF_OBJECT)
+       {
+         mask |= BSF_GNU_UNIQUE | BSF_THREAD_LOCAL;
+
+         if (S_IS_COMMON (sym))
+           {
+             as_bad (_("cannot change type of common symbol '%s'"),
+                     S_GET_NAME (sym));
+             mask = type = 0;
+           }
+       }
+
+      /* Don't warn when changing to STT_NOTYPE.  */
+      if (type)
+       {
+         flagword new = (elfsym->symbol.flags & ~mask) | type;
+
+         if (new != (elfsym->symbol.flags | type))
+           as_warn (_("symbol '%s' already has its type set"), S_GET_NAME (sym));
+         elfsym->symbol.flags = new;
+       }
+      else
+       elfsym->symbol.flags &= ~mask;
+    }
 
   demand_empty_rest_of_line ();
 }
 
   demand_empty_rest_of_line ();
 }
@@ -1754,9 +2343,8 @@ obj_elf_ident (int ignore ATTRIBUTE_UNUSED)
     {
       char *p;
       comment_section = subseg_new (".comment", 0);
     {
       char *p;
       comment_section = subseg_new (".comment", 0);
-      bfd_set_section_flags (stdoutput, comment_section,
-                            SEC_READONLY | SEC_HAS_CONTENTS
-                            | SEC_MERGE | SEC_STRINGS);
+      bfd_set_section_flags (comment_section, (SEC_READONLY | SEC_HAS_CONTENTS
+                                              | SEC_MERGE | SEC_STRINGS));
       comment_section->entsize = 1;
 #ifdef md_elf_section_change_hook
       md_elf_section_change_hook ();
       comment_section->entsize = 1;
 #ifdef md_elf_section_change_hook
       md_elf_section_change_hook ();
@@ -1777,24 +2365,22 @@ obj_elf_ident (int ignore ATTRIBUTE_UNUSED)
 void
 obj_elf_init_stab_section (segT seg)
 {
 void
 obj_elf_init_stab_section (segT seg)
 {
-  char *file;
+  const char *file;
   char *p;
   char *stabstr_name;
   unsigned int stroff;
 
   /* Force the section to align to a longword boundary.  Without this,
      UnixWare ar crashes.  */
   char *p;
   char *stabstr_name;
   unsigned int stroff;
 
   /* Force the section to align to a longword boundary.  Without this,
      UnixWare ar crashes.  */
-  bfd_set_section_alignment (stdoutput, seg, 2);
+  bfd_set_section_alignment (seg, 2);
 
   /* Make space for this first symbol.  */
   p = frag_more (12);
   /* Zero it out.  */
   memset (p, 0, 12);
 
   /* Make space for this first symbol.  */
   p = frag_more (12);
   /* Zero it out.  */
   memset (p, 0, 12);
-  as_where (&file, NULL);
-  stabstr_name = (char *) xmalloc (strlen (segment_name (seg)) + 4);
-  strcpy (stabstr_name, segment_name (seg));
-  strcat (stabstr_name, "str");
-  stroff = get_stab_string_offset (file, stabstr_name);
+  file = as_where (NULL);
+  stabstr_name = concat (segment_name (seg), "str", (char *) NULL);
+  stroff = get_stab_string_offset (file, stabstr_name, TRUE);
   know (stroff == 1 || (stroff == 0 && file[0] == '\0'));
   md_number_to_chars (p, stroff, 4);
   seg_info (seg)->stabu.p = p;
   know (stroff == 1 || (stroff == 0 && file[0] == '\0'));
   md_number_to_chars (p, stroff, 4);
   seg_info (seg)->stabu.p = p;
@@ -1817,21 +2403,20 @@ adjust_stab_sections (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   if (!strcmp ("str", sec->name + strlen (sec->name) - 3))
     return;
 
   if (!strcmp ("str", sec->name + strlen (sec->name) - 3))
     return;
 
-  name = (char *) alloca (strlen (sec->name) + 4);
-  strcpy (name, sec->name);
-  strcat (name, "str");
+  name = concat (sec->name, "str", NULL);
   strsec = bfd_get_section_by_name (abfd, name);
   if (strsec)
   strsec = bfd_get_section_by_name (abfd, name);
   if (strsec)
-    strsz = bfd_section_size (abfd, strsec);
+    strsz = bfd_section_size (strsec);
   else
     strsz = 0;
   else
     strsz = 0;
-  nsyms = bfd_section_size (abfd, sec) / 12 - 1;
+  nsyms = bfd_section_size (sec) / 12 - 1;
 
   p = seg_info (sec)->stabu.p;
   gas_assert (p != 0);
 
   bfd_h_put_16 (abfd, nsyms, p + 6);
   bfd_h_put_32 (abfd, strsz, p + 8);
 
   p = seg_info (sec)->stabu.p;
   gas_assert (p != 0);
 
   bfd_h_put_16 (abfd, nsyms, p + 6);
   bfd_h_put_32 (abfd, strsz, p + 8);
+  free (name);
 }
 
 #ifdef NEED_ECOFF_DEBUG
 }
 
 #ifdef NEED_ECOFF_DEBUG
@@ -1879,6 +2464,8 @@ void
 elf_frob_symbol (symbolS *symp, int *puntp)
 {
   struct elf_obj_sy *sy_obj;
 elf_frob_symbol (symbolS *symp, int *puntp)
 {
   struct elf_obj_sy *sy_obj;
+  expressionS *size;
+  struct elf_versioned_name_list *versioned_name;
 
 #ifdef NEED_ECOFF_DEBUG
   if (ECOFF_DEBUGGING)
 
 #ifdef NEED_ECOFF_DEBUG
   if (ECOFF_DEBUGGING)
@@ -1887,86 +2474,66 @@ elf_frob_symbol (symbolS *symp, int *puntp)
 
   sy_obj = symbol_get_obj (symp);
 
 
   sy_obj = symbol_get_obj (symp);
 
-  if (sy_obj->size != NULL)
+  size = sy_obj->size;
+  if (size != NULL)
     {
     {
-      switch (sy_obj->size->X_op)
+      if (resolve_expression (size)
+         && size->X_op == O_constant)
+       S_SET_SIZE (symp, size->X_add_number);
+      else
        {
        {
-       case O_subtract:
-         S_SET_SIZE (symp,
-                     (S_GET_VALUE (sy_obj->size->X_add_symbol)
-                      + sy_obj->size->X_add_number
-                      - S_GET_VALUE (sy_obj->size->X_op_symbol)));
-         break;
-       case O_constant:
-         S_SET_SIZE (symp,
-                     (S_GET_VALUE (sy_obj->size->X_add_symbol)
-                      + sy_obj->size->X_add_number));
-         break;
-       default:
-         as_bad (_(".size expression too complicated to fix up"));
-         break;
+         if (!flag_allow_nonconst_size)
+           as_bad (_(".size expression for %s "
+                     "does not evaluate to a constant"), S_GET_NAME (symp));
+         else
+           as_warn (_(".size expression for %s "
+                      "does not evaluate to a constant"), S_GET_NAME (symp));
        }
       free (sy_obj->size);
       sy_obj->size = NULL;
     }
 
        }
       free (sy_obj->size);
       sy_obj->size = NULL;
     }
 
-  if (sy_obj->versioned_name != NULL)
+  versioned_name = sy_obj->versioned_name;
+  if (versioned_name)
     {
     {
-      char *p;
-
-      p = strchr (sy_obj->versioned_name, ELF_VER_CHR);
-      know (p != NULL);
-
       /* This symbol was given a new name with the .symver directive.
       /* This symbol was given a new name with the .symver directive.
-
         If this is an external reference, just rename the symbol to
         include the version string.  This will make the relocs be
         If this is an external reference, just rename the symbol to
         include the version string.  This will make the relocs be
-        against the correct versioned symbol.
-
-        If this is a definition, add an alias.  FIXME: Using an alias
-        will permit the debugging information to refer to the right
-        symbol.  However, it's not clear whether it is the best
-        approach.  */
-
-      if (! S_IS_DEFINED (symp))
+        against the correct versioned symbol.  */
+
+      /* We will have already reported an version error.  */
+      if (sy_obj->bad_version)
+       *puntp = TRUE;
+      /* elf_frob_file_before_adjust only allows one version symbol for
+        renamed symbol.  */
+      else if (sy_obj->rename)
+       S_SET_NAME (symp, versioned_name->name);
+      else if (S_IS_COMMON (symp))
        {
        {
-         /* Verify that the name isn't using the @@ syntax--this is
-            reserved for definitions of the default version to link
-            against.  */
-         if (p[1] == ELF_VER_CHR)
-           {
-             as_bad (_("invalid attempt to declare external version name as default in symbol `%s'"),
-                     sy_obj->versioned_name);
-             *puntp = TRUE;
-           }
-         S_SET_NAME (symp, sy_obj->versioned_name);
+         as_bad (_("`%s' can't be versioned to common symbol '%s'"),
+                 versioned_name->name, S_GET_NAME (symp));
+         *puntp = TRUE;
        }
       else
        {
        }
       else
        {
-         if (p[1] == ELF_VER_CHR && p[2] == ELF_VER_CHR)
-           {
-             size_t l;
-
-             /* The @@@ syntax is a special case. It renames the
-                symbol name to versioned_name with one `@' removed.  */
-             l = strlen (&p[3]) + 1;
-             memmove (&p[2], &p[3], l);
-             S_SET_NAME (symp, sy_obj->versioned_name);
-           }
-         else
+         asymbol *bfdsym;
+         elf_symbol_type *elfsym;
+
+         /* This is a definition.  Add an alias for each version.
+            FIXME: Using an alias will permit the debugging information
+            to refer to the right symbol.  However, it's not clear
+            whether it is the best approach.  */
+
+         /* FIXME: Creating a new symbol here is risky.  We're
+            in the final loop over the symbol table.  We can
+            get away with it only because the symbol goes to
+            the end of the list, where the loop will still see
+            it.  It would probably be better to do this in
+            obj_frob_file_before_adjust.  */
+         for (; versioned_name != NULL;
+              versioned_name = versioned_name->next)
            {
            {
-             symbolS *symp2;
-
-             /* FIXME: Creating a new symbol here is risky.  We're
-                in the final loop over the symbol table.  We can
-                get away with it only because the symbol goes to
-                the end of the list, where the loop will still see
-                it.  It would probably be better to do this in
-                obj_frob_file_before_adjust.  */
-
-             symp2 = symbol_find_or_make (sy_obj->versioned_name);
-
-             /* Now we act as though we saw symp2 = sym.  */
+             symbolS *symp2 = symbol_find_or_make (versioned_name->name);
 
              S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
 
 
              S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
 
@@ -1989,6 +2556,27 @@ elf_frob_symbol (symbolS *symp, int *puntp)
              if (S_IS_EXTERNAL (symp))
                S_SET_EXTERNAL (symp2);
            }
              if (S_IS_EXTERNAL (symp))
                S_SET_EXTERNAL (symp2);
            }
+
+         switch (symbol_get_obj (symp)->visibility)
+           {
+           case visibility_unchanged:
+             break;
+           case visibility_hidden:
+             bfdsym = symbol_get_bfdsym (symp);
+             elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym),
+                                       bfdsym);
+             elfsym->internal_elf_sym.st_other &= ~3;
+             elfsym->internal_elf_sym.st_other |= STV_HIDDEN;
+             break;
+           case visibility_remove:
+             /* Remove the symbol if it isn't used in relocation.  */
+             if (!symbol_used_in_reloc_p (symp))
+               symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+             break;
+           case visibility_local:
+             S_CLEAR_EXTERNAL (symp);
+             break;
+           }
        }
     }
 
        }
     }
 
@@ -1999,40 +2587,26 @@ elf_frob_symbol (symbolS *symp, int *puntp)
        as_bad (_("symbol `%s' can not be both weak and common"),
                S_GET_NAME (symp));
     }
        as_bad (_("symbol `%s' can not be both weak and common"),
                S_GET_NAME (symp));
     }
-
-#ifdef TC_MIPS
-  /* The Irix 5 and 6 assemblers set the type of any common symbol and
-     any undefined non-function symbol to STT_OBJECT.  We try to be
-     compatible, since newer Irix 5 and 6 linkers care.  However, we
-     only set undefined symbols to be STT_OBJECT if we are on Irix,
-     because that is the only time gcc will generate the necessary
-     .global directives to mark functions.  */
-
-  if (S_IS_COMMON (symp))
-    symbol_get_bfdsym (symp)->flags |= BSF_OBJECT;
-
-  if (strstr (TARGET_OS, "irix") != NULL
-      && ! S_IS_DEFINED (symp)
-      && (symbol_get_bfdsym (symp)->flags & BSF_FUNCTION) == 0)
-    symbol_get_bfdsym (symp)->flags |= BSF_OBJECT;
-#endif
 }
 
 struct group_list
 {
   asection **head;             /* Section lists.  */
 }
 
 struct group_list
 {
   asection **head;             /* Section lists.  */
-  unsigned int *elt_count;     /* Number of sections in each list.  */
   unsigned int num_group;      /* Number of lists.  */
   struct hash_control *indexes; /* Maps group name to index in head array.  */
 };
 
   unsigned int num_group;      /* Number of lists.  */
   struct hash_control *indexes; /* Maps group name to index in head array.  */
 };
 
+static struct group_list groups;
+
 /* Called via bfd_map_over_sections.  If SEC is a member of a group,
    add it to a list of sections belonging to the group.  INF is a
    pointer to a struct group_list, which is where we store the head of
 /* Called via bfd_map_over_sections.  If SEC is a member of a group,
    add it to a list of sections belonging to the group.  INF is a
    pointer to a struct group_list, which is where we store the head of
-   each list.  */
+   each list.  If its link_to_symbol_name isn't NULL, set up its
+   linked-to section.  */
 
 static void
 
 static void
-build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+build_additional_section_info (bfd *abfd ATTRIBUTE_UNUSED,
+                                 asection *sec, void *inf)
 {
   struct group_list *list = (struct group_list *) inf;
   const char *group_name = elf_group_name (sec);
 {
   struct group_list *list = (struct group_list *) inf;
   const char *group_name = elf_group_name (sec);
@@ -2040,6 +2614,18 @@ build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   unsigned int *elem_idx;
   unsigned int *idx_ptr;
 
   unsigned int *elem_idx;
   unsigned int *idx_ptr;
 
+  if (sec->map_head.linked_to_symbol_name)
+    {
+      symbolS *linked_to_sym;
+      linked_to_sym = symbol_find (sec->map_head.linked_to_symbol_name);
+      if (!linked_to_sym || !S_IS_DEFINED (linked_to_sym))
+       as_bad (_("undefined linked-to symbol `%s' on section `%s'"),
+               sec->map_head.linked_to_symbol_name,
+               bfd_section_name (sec));
+      else
+       elf_linked_to_section (sec) = S_GET_SEGMENT (linked_to_sym);
+    }
+
   if (group_name == NULL)
     return;
 
   if (group_name == NULL)
     return;
 
@@ -2050,7 +2636,6 @@ build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
     {
       elf_next_in_group (sec) = list->head[*elem_idx];
       list->head[*elem_idx] = sec;
     {
       elf_next_in_group (sec) = list->head[*elem_idx];
       list->head[*elem_idx] = sec;
-      list->elt_count[*elem_idx] += 1;
       return;
     }
 
       return;
     }
 
@@ -2060,17 +2645,13 @@ build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
   if ((i & 127) == 0)
     {
       unsigned int newsize = i + 128;
   if ((i & 127) == 0)
     {
       unsigned int newsize = i + 128;
-      list->head = (asection **) xrealloc (list->head,
-                                           newsize * sizeof (*list->head));
-      list->elt_count = (unsigned int *)
-          xrealloc (list->elt_count, newsize * sizeof (*list->elt_count));
+      list->head = XRESIZEVEC (asection *, list->head, newsize);
     }
   list->head[i] = sec;
     }
   list->head[i] = sec;
-  list->elt_count[i] = 1;
   list->num_group += 1;
 
   /* Add index to hash.  */
   list->num_group += 1;
 
   /* Add index to hash.  */
-  idx_ptr = (unsigned int *) xmalloc (sizeof (unsigned int));
+  idx_ptr = XNEW (unsigned int);
   *idx_ptr = i;
   hash_insert (list->indexes, group_name, idx_ptr);
 }
   *idx_ptr = i;
   hash_insert (list->indexes, group_name, idx_ptr);
 }
@@ -2080,38 +2661,38 @@ static void free_section_idx (const char *key ATTRIBUTE_UNUSED, void *val)
   free ((unsigned int *) val);
 }
 
   free ((unsigned int *) val);
 }
 
+/* Create symbols for group signature.  */
+
 void
 elf_adjust_symtab (void)
 {
 void
 elf_adjust_symtab (void)
 {
-  struct group_list list;
   unsigned int i;
 
   /* Go find section groups.  */
   unsigned int i;
 
   /* Go find section groups.  */
-  list.num_group = 0;
-  list.head = NULL;
-  list.elt_count = NULL;
-  list.indexes = hash_new ();
-  bfd_map_over_sections (stdoutput, build_group_lists, &list);
-  
+  groups.num_group = 0;
+  groups.head = NULL;
+  groups.indexes = hash_new ();
+  bfd_map_over_sections (stdoutput, build_additional_section_info,
+                        &groups);
+
   /* Make the SHT_GROUP sections that describe each section group.  We
      can't set up the section contents here yet, because elf section
      indices have yet to be calculated.  elf.c:set_group_contents does
      the rest of the work.  */
   /* Make the SHT_GROUP sections that describe each section group.  We
      can't set up the section contents here yet, because elf section
      indices have yet to be calculated.  elf.c:set_group_contents does
      the rest of the work.  */
- for (i = 0; i < list.num_group; i++)
+ for (i = 0; i < groups.num_group; i++)
     {
     {
-      const char *group_name = elf_group_name (list.head[i]);
+      const char *group_name = elf_group_name (groups.head[i]);
       const char *sec_name;
       asection *s;
       flagword flags;
       struct symbol *sy;
       const char *sec_name;
       asection *s;
       flagword flags;
       struct symbol *sy;
-      bfd_size_type size;
 
       flags = SEC_READONLY | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_GROUP;
 
       flags = SEC_READONLY | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_GROUP;
-      for (s = list.head[i]; s != NULL; s = elf_next_in_group (s))
+      for (s = groups.head[i]; s != NULL; s = elf_next_in_group (s))
        if ((s->flags ^ flags) & SEC_LINK_ONCE)
          {
            flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
        if ((s->flags ^ flags) & SEC_LINK_ONCE)
          {
            flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
-           if (s != list.head[i])
+           if (s != groups.head[i])
              {
                as_warn (_("assuming all members of group `%s' are COMDAT"),
                         group_name);
              {
                as_warn (_("assuming all members of group `%s' are COMDAT"),
                         group_name);
@@ -2122,8 +2703,8 @@ elf_adjust_symtab (void)
       sec_name = ".group";
       s = subseg_force_new (sec_name, 0);
       if (s == NULL
       sec_name = ".group";
       s = subseg_force_new (sec_name, 0);
       if (s == NULL
-         || !bfd_set_section_flags (stdoutput, s, flags)
-         || !bfd_set_section_alignment (stdoutput, s, 2))
+         || !bfd_set_section_flags (s, flags)
+         || !bfd_set_section_alignment (s, 2))
        {
          as_fatal (_("can't create group: %s"),
                    bfd_errmsg (bfd_get_error ()));
        {
          as_fatal (_("can't create group: %s"),
                    bfd_errmsg (bfd_get_error ()));
@@ -2131,32 +2712,27 @@ elf_adjust_symtab (void)
       elf_section_type (s) = SHT_GROUP;
 
       /* Pass a pointer to the first section in this group.  */
       elf_section_type (s) = SHT_GROUP;
 
       /* Pass a pointer to the first section in this group.  */
-      elf_next_in_group (s) = list.head[i];
+      elf_next_in_group (s) = groups.head[i];
+      elf_sec_group (groups.head[i]) = s;
       /* Make sure that the signature symbol for the group has the
         name of the group.  */
       sy = symbol_find_exact (group_name);
       /* Make sure that the signature symbol for the group has the
         name of the group.  */
       sy = symbol_find_exact (group_name);
-      if (!sy
-         || (sy != symbol_lastP
-             && (sy->sy_next == NULL
-                 || sy->sy_next->sy_previous != sy)))
+      if (!sy || !symbol_on_chain (sy, symbol_rootP, symbol_lastP))
        {
          /* Create the symbol now.  */
          sy = symbol_new (group_name, now_seg, (valueT) 0, frag_now);
        {
          /* Create the symbol now.  */
          sy = symbol_new (group_name, now_seg, (valueT) 0, frag_now);
+#ifdef TE_SOLARIS
+         /* Before Solaris 11 build 154, Sun ld rejects local group
+            signature symbols, so make them weak hidden instead.  */
+         symbol_get_bfdsym (sy)->flags |= BSF_WEAK;
+         S_SET_OTHER (sy, STV_HIDDEN);
+#else
          symbol_get_obj (sy)->local = 1;
          symbol_get_obj (sy)->local = 1;
+#endif
          symbol_table_insert (sy);
        }
       elf_group_id (s) = symbol_get_bfdsym (sy);
          symbol_table_insert (sy);
        }
       elf_group_id (s) = symbol_get_bfdsym (sy);
-
-      size = 4 * (list.elt_count[i] + 1);
-      bfd_set_section_size (stdoutput, s, size);
-      s->contents = (unsigned char *) frag_more (size);
-      frag_now->fr_fix = frag_now_fix_octets ();
-      frag_wane (frag_now);
     }
     }
-
-  /* Cleanup hash.  */
-  hash_traverse (list.indexes, free_section_idx);
-  hash_die (list.indexes);
 }
 
 void
 }
 
 void
@@ -2179,37 +2755,61 @@ elf_frob_file_before_adjust (void)
       symbolS *symp;
 
       for (symp = symbol_rootP; symp; symp = symbol_next (symp))
       symbolS *symp;
 
       for (symp = symbol_rootP; symp; symp = symbol_next (symp))
-       if (!S_IS_DEFINED (symp))
-         {
-           if (symbol_get_obj (symp)->versioned_name)
-             {
-               char *p;
-
-               /* The @@@ syntax is a special case. If the symbol is
-                  not defined, 2 `@'s will be removed from the
-                  versioned_name.  */
-
-               p = strchr (symbol_get_obj (symp)->versioned_name,
-                           ELF_VER_CHR);
-               know (p != NULL);
-               if (p[1] == ELF_VER_CHR && p[2] == ELF_VER_CHR)
-                 {
-                   size_t l = strlen (&p[3]) + 1;
-                   memmove (&p[1], &p[3], l);
-                 }
-               if (symbol_used_p (symp) == 0
-                   && symbol_used_in_reloc_p (symp) == 0)
-                 symbol_remove (symp, &symbol_rootP, &symbol_lastP);
-             }
+       {
+         struct elf_obj_sy *sy_obj = symbol_get_obj (symp);
+         int is_defined = !!S_IS_DEFINED (symp);
 
 
-           /* If there was .weak foo, but foo was neither defined nor
-              used anywhere, remove it.  */
+         if (sy_obj->versioned_name)
+           {
+             char *p = strchr (sy_obj->versioned_name->name,
+                               ELF_VER_CHR);
 
 
-           else if (S_IS_WEAK (symp)
-                    && symbol_used_p (symp) == 0
-                    && symbol_used_in_reloc_p (symp) == 0)
-             symbol_remove (symp, &symbol_rootP, &symbol_lastP);
-         }
+             if (sy_obj->rename)
+               {
+                 /* The @@@ syntax is a special case. If the symbol is
+                    not defined, 2 `@'s will be removed from the
+                    versioned_name. Otherwise, 1 `@' will be removed.   */
+                 size_t l = strlen (&p[3]) + 1;
+                 memmove (&p[1 + is_defined], &p[3], l);
+               }
+
+             if (!is_defined)
+               {
+                 /* Verify that the name isn't using the @@ syntax--this
+                    is reserved for definitions of the default version
+                    to link against.  */
+                 if (!sy_obj->rename && p[1] == ELF_VER_CHR)
+                   {
+                     as_bad (_("invalid attempt to declare external "
+                               "version name as default in symbol `%s'"),
+                             sy_obj->versioned_name->name);
+                     return;
+                   }
+
+                 /* Only one version symbol is allowed for undefined
+                    symbol.  */
+                 if (sy_obj->versioned_name->next)
+                   {
+                     as_bad (_("multiple versions [`%s'|`%s'] for "
+                               "symbol `%s'"),
+                             sy_obj->versioned_name->name,
+                             sy_obj->versioned_name->next->name,
+                             S_GET_NAME (symp));
+                     return;
+                   }
+
+                 sy_obj->rename = TRUE;
+               }
+           }
+
+         /* If there was .symver or .weak, but symbol was neither
+            defined nor used anywhere, remove it.  */
+         if (!is_defined
+             && (sy_obj->versioned_name || S_IS_WEAK (symp))
+             && symbol_used_p (symp) == 0
+             && symbol_used_in_reloc_p (symp) == 0)
+           symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+       }
     }
 }
 
     }
 }
 
@@ -2221,6 +2821,31 @@ elf_frob_file_before_adjust (void)
 void
 elf_frob_file_after_relocs (void)
 {
 void
 elf_frob_file_after_relocs (void)
 {
+  unsigned int i;
+
+  /* Set SHT_GROUP section size.  */
+  for (i = 0; i < groups.num_group; i++)
+    {
+      asection *s, *head, *group;
+      bfd_size_type size;
+
+      head = groups.head[i];
+      size = 4;
+      for (s = head; s != NULL; s = elf_next_in_group (s))
+       size += (s->flags & SEC_RELOC) != 0 ? 8 : 4;
+
+      group = elf_sec_group (head);
+      subseg_set (group, 0);
+      bfd_set_section_size (group, size);
+      group->contents = (unsigned char *) frag_more (size);
+      frag_now->fr_fix = frag_now_fix_octets ();
+      frag_wane (frag_now);
+    }
+
+  /* Cleanup hash.  */
+  hash_traverse (groups.indexes, free_section_idx);
+  hash_die (groups.indexes);
+
 #ifdef NEED_ECOFF_DEBUG
   if (ECOFF_DEBUGGING)
     /* Generate the ECOFF debugging information.  */
 #ifdef NEED_ECOFF_DEBUG
   if (ECOFF_DEBUGGING)
     /* Generate the ECOFF debugging information.  */
@@ -2269,8 +2894,8 @@ elf_frob_file_after_relocs (void)
         to force the ELF backend to allocate a file position, and then
         write out the data.  FIXME: Is this really the best way to do
         this?  */
         to force the ELF backend to allocate a file position, and then
         write out the data.  FIXME: Is this really the best way to do
         this?  */
-      bfd_set_section_size
-       (stdoutput, sec, bfd_ecoff_debug_size (stdoutput, &debug, debug_swap));
+      bfd_set_section_size (sec, bfd_ecoff_debug_size (stdoutput, &debug,
+                                                      debug_swap));
 
       /* Pass BUF to bfd_set_section_contents because this will
         eventually become a call to fwrite, and ISO C prohibits
 
       /* Pass BUF to bfd_set_section_contents because this will
         eventually become a call to fwrite, and ISO C prohibits
@@ -2291,103 +2916,6 @@ elf_frob_file_after_relocs (void)
 #endif /* NEED_ECOFF_DEBUG */
 }
 
 #endif /* NEED_ECOFF_DEBUG */
 }
 
-#ifdef SCO_ELF
-
-/* Heavily plagiarized from obj_elf_version.  The idea is to emit the
-   SCO specific identifier in the .notes section to satisfy the SCO
-   linker.
-
-   This looks more complicated than it really is.  As opposed to the
-   "obvious" solution, this should handle the cross dev cases
-   correctly.  (i.e, hosting on a 64 bit big endian processor, but
-   generating SCO Elf code) Efficiency isn't a concern, as there
-   should be exactly one of these sections per object module.
-
-   SCO OpenServer 5 identifies it's ELF modules with a standard ELF
-   .note section.
-
-   int_32 namesz  = 4 ;  Name size
-   int_32 descsz  = 12 ; Descriptive information
-   int_32 type    = 1 ;
-   char   name[4] = "SCO" ; Originator name ALWAYS SCO + NULL
-   int_32 version = (major ver # << 16)  | version of tools ;
-   int_32 source  = (tool_id << 16 ) | 1 ;
-   int_32 info    = 0 ;    These are set by the SCO tools, but we
-                          don't know enough about the source
-                          environment to set them.  SCO ld currently
-                          ignores them, and recommends we set them
-                          to zero.  */
-
-#define SCO_MAJOR_VERSION 0x1
-#define SCO_MINOR_VERSION 0x1
-
-void
-sco_id (void)
-{
-
-  char *name;
-  unsigned int c;
-  char ch;
-  char *p;
-  asection *seg = now_seg;
-  subsegT subseg = now_subseg;
-  Elf_Internal_Note i_note;
-  Elf_External_Note e_note;
-  asection *note_secp = NULL;
-  int i, len;
-
-  /* create the .note section */
-
-  note_secp = subseg_new (".note", 0);
-  bfd_set_section_flags (stdoutput,
-                        note_secp,
-                        SEC_HAS_CONTENTS | SEC_READONLY);
-
-  /* process the version string */
-
-  i_note.namesz = 4;
-  i_note.descsz = 12;          /* 12 descriptive bytes */
-  i_note.type = NT_VERSION;    /* Contains a version string */
-
-  p = frag_more (sizeof (i_note.namesz));
-  md_number_to_chars (p, i_note.namesz, 4);
-
-  p = frag_more (sizeof (i_note.descsz));
-  md_number_to_chars (p, i_note.descsz, 4);
-
-  p = frag_more (sizeof (i_note.type));
-  md_number_to_chars (p, i_note.type, 4);
-
-  p = frag_more (4);
-  strcpy (p, "SCO");
-
-  /* Note: this is the version number of the ELF we're representing */
-  p = frag_more (4);
-  md_number_to_chars (p, (SCO_MAJOR_VERSION << 16) | (SCO_MINOR_VERSION), 4);
-
-  /* Here, we pick a magic number for ourselves (yes, I "registered"
-     it with SCO.  The bottom bit shows that we are compat with the
-     SCO ABI.  */
-  p = frag_more (4);
-  md_number_to_chars (p, 0x4c520000 | 0x0001, 4);
-
-  /* If we knew (or cared) what the source language options were, we'd
-     fill them in here.  SCO has given us permission to ignore these
-     and just set them to zero.  */
-  p = frag_more (4);
-  md_number_to_chars (p, 0x0000, 4);
-
-  frag_align (2, 0, 0);
-
-  /* We probably can't restore the current segment, for there likely
-     isn't one yet...  */
-  if (seg && subseg)
-    subseg_set (seg, subseg);
-
-}
-
-#endif /* SCO_ELF */
-
 static void
 elf_generate_asm_lineno (void)
 {
 static void
 elf_generate_asm_lineno (void)
 {
@@ -2398,12 +2926,12 @@ elf_generate_asm_lineno (void)
 }
 
 static void
 }
 
 static void
-elf_process_stab (segT sec,
-                 int what,
-                 const char *string,
-                 int type,
-                 int other,
-                 int desc)
+elf_process_stab (segT sec ATTRIBUTE_UNUSED,
+                 int what ATTRIBUTE_UNUSED,
+                 const char *string ATTRIBUTE_UNUSED,
+                 int type ATTRIBUTE_UNUSED,
+                 int other ATTRIBUTE_UNUSED,
+                 int desc ATTRIBUTE_UNUSED)
 {
 #ifdef NEED_ECOFF_DEBUG
   if (ECOFF_DEBUGGING)
 {
 #ifdef NEED_ECOFF_DEBUG
   if (ECOFF_DEBUGGING)
This page took 0.045484 seconds and 4 git commands to generate.