2001-07-10 John Healy <jhealy@redhat.com>
[binutils-gdb.git] / gas / config / obj-elf.c
index 6feb5c5ba15b7f9fe992a5d63c1e1ebd1bcbf3e3..9498c1884359d46b6686e3f85be2187c8310a6a5 100644 (file)
@@ -1,5 +1,6 @@
 /* ELF object file format
-   Copyright (C) 1992, 93-98, 1999 Free Software Foundation, Inc.
+   Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+   Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -16,7 +17,7 @@
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to the Free
    Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   02111-1307, USA.  */
 
 #define OBJ_HEADER "obj-elf.h"
 #include "as.h"
 #include "elf/ppc.h"
 #endif
 
+#ifdef TC_I370
+#include "elf/i370.h"
+#endif
+
 static bfd_vma elf_s_get_size PARAMS ((symbolS *));
 static void elf_s_set_size PARAMS ((symbolS *, bfd_vma));
 static bfd_vma elf_s_get_align PARAMS ((symbolS *));
 static void elf_s_set_align PARAMS ((symbolS *, bfd_vma));
+static void elf_s_set_other PARAMS ((symbolS *, int));
 static void elf_copy_symbol_attributes PARAMS ((symbolS *, symbolS *));
 static int elf_sec_sym_ok_for_reloc PARAMS ((asection *));
 static void adjust_stab_sections PARAMS ((bfd *, asection *, PTR));
+static int elf_separate_stab_sections PARAMS ((void));
+static void elf_init_stab_section PARAMS ((segT));
 
 #ifdef NEED_ECOFF_DEBUG
 static boolean elf_get_extr PARAMS ((asymbol *, EXTR *));
@@ -65,9 +73,8 @@ static void obj_elf_type PARAMS ((int));
 static void obj_elf_ident PARAMS ((int));
 static void obj_elf_weak PARAMS ((int));
 static void obj_elf_local PARAMS ((int));
+static void obj_elf_visibility PARAMS ((int));
 static void obj_elf_symver PARAMS ((int));
-static void obj_elf_vtable_inherit PARAMS ((int));
-static void obj_elf_vtable_entry PARAMS ((int));
 static void obj_elf_subsection PARAMS ((int));
 static void obj_elf_popsection PARAMS ((int));
 
@@ -89,6 +96,11 @@ static const pseudo_typeS elf_pseudo_table[] =
   {"version", obj_elf_version, 0},
   {"weak", obj_elf_weak, 0},
 
+  /* These define symbol visibility.  */
+  {"internal", obj_elf_visibility, STV_INTERNAL},
+  {"hidden", obj_elf_visibility, STV_HIDDEN},
+  {"protected", obj_elf_visibility, STV_PROTECTED},
+
   /* These are used for stabs-in-elf configurations.  */
   {"line", obj_elf_line, 0},
 
@@ -99,10 +111,10 @@ static const pseudo_typeS elf_pseudo_table[] =
   {"subsection", obj_elf_subsection, 0},
 
   /* These are GNU extensions to aid in garbage collecting C++ vtables.  */
-  {"vtable_inherit", obj_elf_vtable_inherit, 0},
-  {"vtable_entry", obj_elf_vtable_entry, 0},
+  {"vtable_inherit", (void (*) PARAMS ((int))) &obj_elf_vtable_inherit, 0},
+  {"vtable_entry", (void (*) PARAMS ((int))) &obj_elf_vtable_entry, 0},
 
-  /* These are used for dwarf. */
+  /* These are used for dwarf.  */
   {"2byte", cons, 2},
   {"4byte", cons, 4},
   {"8byte", cons, 8},
@@ -215,6 +227,21 @@ elf_s_set_align (sym, align)
   S_SET_ALIGN (sym, align);
 }
 
+int
+elf_s_get_other (sym)
+     symbolS *sym;
+{
+  return elf_symbol (symbol_get_bfdsym (sym))->internal_elf_sym.st_other;
+}
+
+static void
+elf_s_set_other (sym, other)
+     symbolS *sym;
+     int other;
+{
+  S_SET_OTHER (sym, other);
+}
+
 static void
 elf_copy_symbol_attributes (dest, src)
      symbolS *dest, *src;
@@ -231,7 +258,7 @@ elf_sec_sym_ok_for_reloc (sec)
 
 void
 elf_file_symbol (s)
-     char *s;
+     const char *s;
 {
   symbolS *sym;
 
@@ -474,6 +501,47 @@ obj_elf_weak (ignore)
   demand_empty_rest_of_line ();
 }
 
+static void
+obj_elf_visibility (visibility)
+     int visibility;
+{
+  char *name;
+  int c;
+  symbolS *symbolP;
+  asymbol *bfdsym;
+  elf_symbol_type *elfsym;
+
+  do
+    {
+      name = input_line_pointer;
+      c = get_symbol_end ();
+      symbolP = symbol_find_or_make (name);
+      *input_line_pointer = c;
+
+      SKIP_WHITESPACE ();
+
+      bfdsym = symbol_get_bfdsym (symbolP);
+      elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+
+      assert (elfsym);
+
+      elfsym->internal_elf_sym.st_other = visibility;
+
+      if (c == ',')
+       {
+         input_line_pointer ++;
+
+         SKIP_WHITESPACE ();
+
+         if (*input_line_pointer == '\n')
+           c = '\n';
+       }
+    }
+  while (c == ',');
+
+  demand_empty_rest_of_line ();
+}
+
 static segT previous_section;
 static int previous_subsection;
 
@@ -486,7 +554,6 @@ struct section_stack
 
 static struct section_stack *section_stack;
 
-
 /* Handle the .section pseudo-op.  This code supports two different
    syntaxes.
 
@@ -553,12 +620,14 @@ static struct special_section const special_sections[] =
 };
 
 void
-obj_elf_change_section (name, type, attr, push)
+obj_elf_change_section (name, type, attr, entsize, push)
      char *name;
-     int type, attr, push;
+     int type, attr, entsize, push;
 {
-  int new_sec;
+  asection *old_sec;
   segT sec;
+  flagword flags;
+  int i;
 
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
@@ -579,54 +648,66 @@ obj_elf_change_section (name, type, attr, push)
   previous_section = now_seg;
   previous_subsection = now_subseg;
 
-  new_sec = bfd_get_section_by_name (stdoutput, name) == NULL;
+  old_sec = bfd_get_section_by_name (stdoutput, name);
   sec = subseg_new (name, 0);
 
-  if (new_sec)
-    {
-      flagword flags;
-      symbolS *secsym;
-      int i;
-
-      /* See if this is one of the special sections.  */
-      for (i = 0; special_sections[i].name != NULL; i++)
-        if (strcmp (name, special_sections[i].name) == 0)
-          {
-           if (type == SHT_NULL)
-             type = special_sections[i].type;
-           else if (type != special_sections[i].type)
-             as_warn (_("Setting incorrect section type for %s"), name);
-
-           if ((attr &~ special_sections[i].attributes) != 0)
+  /* See if this is one of the special sections.  */
+  for (i = 0; special_sections[i].name != NULL; i++)
+    if (strcmp (name, special_sections[i].name) == 0)
+      {
+       if (type == SHT_NULL)
+         type = special_sections[i].type;
+       else if (type != special_sections[i].type)
+         {
+           if (old_sec == NULL)
+             {
+               as_warn (_("Setting incorrect section type for %s"), name);
+             }
+           else
              {
-               /* As a GNU extension, we permit a .note section to be
-                  allocatable.  If the linker sees an allocateable .note
-                  section, it will create a PT_NOTE segment in the output
-                  file.  */
-               if (strcmp (name, ".note") != 0
-                   || attr != SHF_ALLOC)
-                 as_warn (_("Setting incorrect section attributes for %s"),
-                          name);
+               as_warn (_("Ignoring incorrect section type for %s"), name);
+               type = special_sections[i].type;
              }
-           attr |= special_sections[i].attributes;
-           break;
          }
-
-      /* Convert ELF type and flags to BFD flags.  */
-      flags = (SEC_RELOC
-              | ((attr & SHF_WRITE) ? 0 : SEC_READONLY)
-              | ((attr & SHF_ALLOC) ? SEC_ALLOC : 0)
-              | (((attr & SHF_ALLOC) && type != SHT_NOBITS) ? SEC_LOAD : 0)
-              | ((attr & SHF_EXECINSTR) ? SEC_CODE : 0));
+       if ((attr &~ special_sections[i].attributes) != 0
+           && old_sec == NULL)
+         {
+           /* As a GNU extension, we permit a .note section to be
+              allocatable.  If the linker sees an allocateable .note
+              section, it will create a PT_NOTE segment in the output
+              file.  */
+           if (strcmp (name, ".note") != 0
+               || attr != SHF_ALLOC)
+             as_warn (_("Setting incorrect section attributes for %s"),
+                      name);
+         }
+       attr |= special_sections[i].attributes;
+       break;
+      }
+
+  /* Convert ELF type and flags to BFD flags.  */
+  flags = (SEC_RELOC
+          | ((attr & SHF_WRITE) ? 0 : SEC_READONLY)
+          | ((attr & SHF_ALLOC) ? SEC_ALLOC : 0)
+          | (((attr & SHF_ALLOC) && type != SHT_NOBITS) ? SEC_LOAD : 0)
+          | ((attr & SHF_EXECINSTR) ? SEC_CODE : 0)
+          | ((attr & SHF_MERGE) ? SEC_MERGE : 0)
+          | ((attr & SHF_STRINGS) ? SEC_STRINGS : 0));
 #ifdef md_elf_section_flags
-      flags = md_elf_section_flags (flags, attr, type);
+  flags = md_elf_section_flags (flags, attr, type);
 #endif
 
+  if (old_sec == NULL)
+    {
+      symbolS *secsym;
+
       /* Prevent SEC_HAS_CONTENTS from being inadvertently set.  */
       if (type == SHT_NOBITS)
         seg_info (sec)->bss = 1;
 
       bfd_set_section_flags (stdoutput, sec, flags);
+      if (flags & SEC_MERGE)
+       sec->entsize = entsize;
 
       /* Add a symbol for this section to the symbol table.  */
       secsym = symbol_find (name);
@@ -635,9 +716,21 @@ obj_elf_change_section (name, type, attr, push)
       else
         symbol_table_insert (section_symbol (sec));
     }
+  else if (attr != 0)
+    {
+      /* If section attributes are specified the second time we see a
+        particular section, then check that they are the same as we
+        saw the first time.  */
+      if ((old_sec->flags ^ flags)
+         & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
+            | SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS))
+       as_warn (_("Ignoring changed section attributes for %s"), name);
+      else if ((flags & SEC_MERGE) && old_sec->entsize != entsize)
+       as_warn (_("Ignoring changed section entity size for %s"), name);
+    }
 
 #ifdef md_elf_section_change_hook
-      md_elf_section_change_hook ();
+  md_elf_section_change_hook ();
 #endif
 }
 
@@ -661,9 +754,15 @@ obj_elf_parse_section_letters (str, len)
        case 'x':
          attr |= SHF_EXECINSTR;
          break;
+       case 'm':
+         attr |= SHF_MERGE;
+         break;
+       case 's':
+         attr |= SHF_STRINGS;
+         break;
        default:
          {
-           char *bad_msg = _("Unrecognized .section attribute: want a,w,x");
+           char *bad_msg = _("Unrecognized .section attribute: want a,m,s,w,x");
 #ifdef md_elf_section_letter
            int md_attr = md_elf_section_letter (*str, &bad_msg);
            if (md_attr >= 0)
@@ -671,7 +770,7 @@ obj_elf_parse_section_letters (str, len)
            else
 #endif
              {
-               as_warn (bad_msg);
+               as_warn ("%s", bad_msg);
                attr = -1;
              }
          }
@@ -735,13 +834,15 @@ obj_elf_section (push)
 {
   char *name, *beg, *end;
   int type, attr, dummy;
+  int entsize;
 
+#ifndef TC_I370
   if (flag_mri)
     {
       char mri_type;
 
 #ifdef md_flush_pending_output
-  md_flush_pending_output ();
+      md_flush_pending_output ();
 #endif
 
       previous_section = now_seg;
@@ -755,6 +856,7 @@ obj_elf_section (push)
 
       return;
     }
+#endif /* ! defined (TC_I370) */
 
   /* Get name of section.  */
   SKIP_WHITESPACE ();
@@ -778,7 +880,7 @@ obj_elf_section (push)
          ignore_rest_of_line ();
          return;
        }
-    
+
       name = xmalloc (end - input_line_pointer + 1);
       memcpy (name, input_line_pointer, end - input_line_pointer);
       name[end - input_line_pointer] = '\0';
@@ -788,6 +890,7 @@ obj_elf_section (push)
 
   type = SHT_NULL;
   attr = 0;
+  entsize = 0;
 
   if (*input_line_pointer == ',')
     {
@@ -830,6 +933,20 @@ obj_elf_section (push)
                  type = obj_elf_section_type (beg, input_line_pointer - beg);
                }
            }
+
+         SKIP_WHITESPACE ();
+         if ((attr & SHF_MERGE) && *input_line_pointer == ',')
+           {
+             ++input_line_pointer;
+             SKIP_WHITESPACE ();
+             entsize = get_absolute_expression ();
+             if (entsize < 0)
+               {
+                 as_warn (_("Bad .section directive - invalid merge entity size"));
+                 attr &= ~SHF_MERGE;
+                 entsize = 0;
+               }
+           }
        }
       else
        {
@@ -859,7 +976,17 @@ obj_elf_section (push)
 
   demand_empty_rest_of_line ();
 
-  obj_elf_change_section (name, type, attr, push);
+  if ((attr & SHF_MERGE) && entsize == 0)
+    {
+      as_warn (_("Entity size for SHF_MERGE not specified.\nSpecify entity size as 4th argument"));
+      attr &= SHF_MERGE;
+    }
+
+#ifdef md_elf_section_change_data_hook
+      md_elf_section_change_data_hook (name, type, attr, entsize, push);
+#endif
+
+  obj_elf_change_section (name, type, attr, entsize, push);
 }
 
 /* Change to the .data section.  */
@@ -992,7 +1119,7 @@ obj_elf_line (ignore)
      int ignore ATTRIBUTE_UNUSED;
 {
   /* Assume delimiter is part of expression.  BSD4.2 as fails with
-     delightful bug, so we are not being incompatible here. */
+     delightful bug, so we are not being incompatible here.  */
   new_logical_line ((char *) NULL, (int) (get_absolute_expression ()));
   demand_empty_rest_of_line ();
 }
@@ -1018,14 +1145,6 @@ obj_elf_symver (ignore)
 
   *input_line_pointer = c;
 
-  if (symbol_get_obj (sym)->versioned_name != NULL)
-    {
-      as_bad (_("multiple .symver directives for symbol `%s'"),
-             S_GET_NAME (sym));
-      ignore_rest_of_line ();
-      return;
-    }
-
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
@@ -1044,16 +1163,34 @@ obj_elf_symver (ignore)
       *input_line_pointer++ = c;
     }
 
-  symbol_get_obj (sym)->versioned_name = xstrdup (name);
+  if (symbol_get_obj (sym)->versioned_name == NULL)
+    {
+      symbol_get_obj (sym)->versioned_name = xstrdup (name);
 
-  *input_line_pointer = c;
+      *input_line_pointer = c;
 
-  if (strchr (symbol_get_obj (sym)->versioned_name, ELF_VER_CHR) == NULL)
+      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;
+       }
+    }
+  else
     {
-      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;
+      if (strcmp (symbol_get_obj (sym)->versioned_name, name))
+       {
+         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 = c;
     }
 
   demand_empty_rest_of_line ();
@@ -1063,7 +1200,7 @@ obj_elf_symver (ignore)
    to the linker the hierarchy in which a particular table resides.  The
    syntax is ".vtable_inherit CHILDNAME, PARENTNAME".  */
 
-static void
+struct fix *
 obj_elf_vtable_inherit (ignore)
      int ignore ATTRIBUTE_UNUSED;
 {
@@ -1096,7 +1233,7 @@ obj_elf_vtable_inherit (ignore)
     {
       as_bad ("expected comma after name in .vtable_inherit");
       ignore_rest_of_line ();
-      return;
+      return NULL;
     }
 
   ++input_line_pointer;
@@ -1123,19 +1260,19 @@ obj_elf_vtable_inherit (ignore)
   demand_empty_rest_of_line ();
 
   if (bad)
-    return;
+    return NULL;
 
   assert (symbol_get_value_expression (csym)->X_op == O_constant);
-  fix_new (symbol_get_frag (csym),
-          symbol_get_value_expression (csym)->X_add_number, 0, psym, 0, 0,
-          BFD_RELOC_VTABLE_INHERIT);
+  return fix_new (symbol_get_frag (csym),
+                 symbol_get_value_expression (csym)->X_add_number,
+                 0, psym, 0, 0, BFD_RELOC_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".  */
 
-static void
+struct fix *
 obj_elf_vtable_entry (ignore)
      int ignore ATTRIBUTE_UNUSED;
 {
@@ -1157,7 +1294,7 @@ obj_elf_vtable_entry (ignore)
     {
       as_bad ("expected comma after name in .vtable_entry");
       ignore_rest_of_line ();
-      return;
+      return NULL;
     }
 
   ++input_line_pointer;
@@ -1166,14 +1303,14 @@ obj_elf_vtable_entry (ignore)
 
   offset = get_absolute_expression ();
 
-  fix_new (frag_now, frag_now_fix (), 0, sym, offset, 0,
-          BFD_RELOC_VTABLE_ENTRY);
-
   demand_empty_rest_of_line ();
+
+  return fix_new (frag_now, frag_now_fix (), 0, sym, offset, 0,
+                 BFD_RELOC_VTABLE_ENTRY);
 }
 
 void
-obj_read_begin_hook ()
+elf_obj_read_begin_hook ()
 {
 #ifdef NEED_ECOFF_DEBUG
   if (ECOFF_DEBUGGING)
@@ -1182,7 +1319,7 @@ obj_read_begin_hook ()
 }
 
 void
-obj_symbol_new_hook (symbolP)
+elf_obj_symbol_new_hook (symbolP)
      symbolS *symbolP;
 {
   struct elf_obj_sy *sy_obj;
@@ -1215,7 +1352,7 @@ obj_elf_version (ignore)
   SKIP_WHITESPACE ();
   if (*input_line_pointer == '\"')
     {
-      ++input_line_pointer;    /* -> 1st char of string. */
+      ++input_line_pointer;    /* -> 1st char of string.  */
       name = input_line_pointer;
 
       while (is_a_char (c = next_char_of_string ()))
@@ -1308,8 +1445,8 @@ obj_elf_size (ignore)
 }
 
 /* Handle the ELF .type pseudo-op.  This sets the type of a symbol.
-   There are four syntaxes:
-   
+   There are five syntaxes:
+
    The first (used on Solaris) is
        .type SYM,#function
    The second (used on UnixWare) is
@@ -1318,6 +1455,8 @@ obj_elf_size (ignore)
        .type SYM STT_FUNC
    The fourth (used on NetBSD/Arm and Linux/ARM) is
        .type SYM,%function
+   The fifth (used on SVR4/860) is
+       .type SYM,"function"
    */
 
 static void
@@ -1329,10 +1468,12 @@ obj_elf_type (ignore)
   int type;
   const char *typename;
   symbolS *sym;
+  elf_symbol_type *elfsym;
 
   name = input_line_pointer;
   c = get_symbol_end ();
   sym = symbol_find_or_make (name);
+  elfsym = (elf_symbol_type *) symbol_get_bfdsym (sym);
   *input_line_pointer = c;
 
   SKIP_WHITESPACE ();
@@ -1342,6 +1483,7 @@ obj_elf_type (ignore)
   SKIP_WHITESPACE ();
   if (   *input_line_pointer == '#'
       || *input_line_pointer == '@'
+      || *input_line_pointer == '"'
       || *input_line_pointer == '%')
     ++input_line_pointer;
 
@@ -1355,12 +1497,19 @@ obj_elf_type (ignore)
   else if (strcmp (typename, "object") == 0
           || strcmp (typename, "STT_OBJECT") == 0)
     type = BSF_OBJECT;
+#ifdef md_elf_symbol_type
+  else if ((type = md_elf_symbol_type (typename, sym, elfsym)) != -1)
+    ;
+#endif
   else
     as_bad (_("ignoring unrecognized symbol type \"%s\""), typename);
 
   *input_line_pointer = c;
 
-  symbol_get_bfdsym (sym)->flags |= type;
+  if (*input_line_pointer == '"')
+    ++input_line_pointer;
+
+  elfsym->symbol.flags |= type;
 
   demand_empty_rest_of_line ();
 }
@@ -1373,6 +1522,10 @@ obj_elf_ident (ignore)
   segT old_section = now_seg;
   int old_subsection = now_subseg;
 
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
   if (!comment_section)
     {
       char *p;
@@ -1405,9 +1558,9 @@ obj_elf_init_stab_section (seg)
      UnixWare ar crashes.  */
   bfd_set_section_alignment (stdoutput, seg, 2);
 
-  /* Make space for this first symbol. */
+  /* Make space for this first symbol.  */
   p = frag_more (12);
-  /* Zero it out. */
+  /* Zero it out.  */
   memset (p, 0, 12);
   as_where (&file, (unsigned int *) NULL);
   stabstr_name = (char *) alloca (strlen (segment_name (seg)) + 4);
@@ -1493,8 +1646,8 @@ elf_get_extr (sym, ext)
 /*ARGSUSED*/
 static void
 elf_set_index (sym, indx)
-     asymbol *sym;
-     bfd_size_type indx;
+     asymbol *sym ATTRIBUTE_UNUSED;
+     bfd_size_type indx ATTRIBUTE_UNUSED;
 {
 }
 
@@ -1539,6 +1692,11 @@ elf_frob_symbol (symp, puntp)
 
   if (sy_obj->versioned_name != NULL)
     {
+      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.
 
          If this is an external reference, just rename the symbol to
@@ -1552,13 +1710,9 @@ elf_frob_symbol (symp, puntp)
 
       if (! S_IS_DEFINED (symp))
        {
-         char *p;
-
          /* Verify that the name isn't using the @@ syntax--this is
              reserved for definitions of the default version to link
              against.  */
-         p = strchr (sy_obj->versioned_name, ELF_VER_CHR);
-         know (p != NULL);
          if (p[1] == ELF_VER_CHR)
            {
              as_bad (_("invalid attempt to declare external version name as default in symbol `%s'"),
@@ -1569,36 +1723,50 @@ elf_frob_symbol (symp, puntp)
        }
       else
        {
-         symbolS *symp2;
+         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
+           {
+             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. */
+             /* 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);
+             symp2 = symbol_find_or_make (sy_obj->versioned_name);
 
-         /* Now we act as though we saw symp2 = sym.  */
+             /* Now we act as though we saw symp2 = sym.  */
 
-         S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
+             S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
 
-         /* Subtracting out the frag address here is a hack because
-             we are in the middle of the final loop.  */
-         S_SET_VALUE (symp2,
-                      (S_GET_VALUE (symp)
-                       - symbol_get_frag (symp)->fr_address));
+             /* Subtracting out the frag address here is a hack
+                because we are in the middle of the final loop.  */
+             S_SET_VALUE (symp2,
+                          (S_GET_VALUE (symp)
+                           - symbol_get_frag (symp)->fr_address));
 
-         symbol_set_frag (symp2, symbol_get_frag (symp));
+             symbol_set_frag (symp2, symbol_get_frag (symp));
 
-         /* This will copy over the size information.  */
-         copy_symbol_attributes (symp2, symp);
+             /* This will copy over the size information.  */
+             copy_symbol_attributes (symp2, symp);
 
-         if (S_IS_WEAK (symp))
-           S_SET_WEAK (symp2);
+             if (S_IS_WEAK (symp))
+               S_SET_WEAK (symp2);
 
-         if (S_IS_EXTERNAL (symp))
-           S_SET_EXTERNAL (symp2);
+             if (S_IS_EXTERNAL (symp))
+               S_SET_EXTERNAL (symp2);
+           }
        }
     }
 
@@ -1627,9 +1795,11 @@ elf_frob_symbol (symp, puntp)
     symbol_get_bfdsym (symp)->flags |= BSF_OBJECT;
 #endif
 
-#ifdef TC_PPC
-  /* Frob the PowerPC, so that the symbol always has object type
-     if it is not some other type.  VxWorks needs this.  */
+#if 0 /* TC_PPC */
+  /* If TC_PPC is defined, we used to force the type of a symbol to be
+     BSF_OBJECT if it was otherwise unset.  This was required by some
+     version of VxWorks.  Thomas de Lellis <tdel@windriver.com> says
+     that this is no longer needed, so it is now commented out.  */
   if ((symbol_get_bfdsym (symp)->flags
        & (BSF_FUNCTION | BSF_FILE | BSF_SECTION_SYM)) == 0
       && S_IS_DEFINED (symp))
@@ -1647,6 +1817,42 @@ elf_frob_file ()
 #endif
 }
 
+/* It removes any unneeded versioned symbols from the symbol table.  */
+
+void
+elf_frob_file_before_adjust ()
+{
+  if (symbol_rootP)
+    {
+      symbolS *symp;
+
+      for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+       if (symbol_get_obj (symp)->versioned_name)
+         {
+           if (!S_IS_DEFINED (symp))
+             {
+               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);
+             }
+         }
+    }
+}
+
 /* It is required that we let write_relocs have the opportunity to
    optimize away fixups before output has begun, since it is possible
    to eliminate all fixups for a section and thus we never should
@@ -1705,7 +1911,11 @@ elf_frob_file_after_relocs ()
         this?  */
       sec->_raw_size = bfd_ecoff_debug_size (stdoutput, &debug, debug_swap);
 
-      if (! bfd_set_section_contents (stdoutput, sec, (PTR) NULL,
+      /* Pass BUF to bfd_set_section_contents because this will
+         eventually become a call to fwrite, and ISO C prohibits
+         passing a NULL pointer to a stdio function even if the
+         pointer will not be used.  */
+      if (! bfd_set_section_contents (stdoutput, sec, (PTR) buf,
                                      (file_ptr) 0, (bfd_size_type) 0))
        as_fatal (_("Can't start writing .mdebug section: %s"),
                  bfd_errmsg (bfd_get_error ()));
@@ -1736,14 +1946,14 @@ elf_frob_file_after_relocs ()
    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 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 
+                           don't know enough about the source
                           environment to set them.  SCO ld currently
                           ignores them, and recommends we set them
                           to zero.  */
@@ -1775,7 +1985,7 @@ sco_id ()
 
   /* process the version string */
 
-  i_note.namesz = 4; 
+  i_note.namesz = 4;
   i_note.descsz = 12;          /* 12 descriptive bytes */
   i_note.type = NT_VERSION;    /* Contains a version string */
 
@@ -1789,7 +1999,7 @@ sco_id ()
   md_number_to_chars (p, (valueT) i_note.type, 4);
 
   p = frag_more (4);
-  strcpy (p, "SCO"); 
+  strcpy (p, "SCO");
 
   /* Note: this is the version number of the ELF we're representing */
   p = frag_more (4);
@@ -1806,7 +2016,7 @@ sco_id ()
      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
@@ -1818,31 +2028,62 @@ sco_id ()
 
 #endif /* SCO_ELF */
 
+static int
+elf_separate_stab_sections ()
+{
+#ifdef NEED_ECOFF_DEBUG
+  return (!ECOFF_DEBUGGING);
+#else
+  return 1;
+#endif
+}
+
+static void
+elf_init_stab_section (seg)
+     segT seg;
+{
+#ifdef NEED_ECOFF_DEBUG
+  if (!ECOFF_DEBUGGING)
+#endif
+    obj_elf_init_stab_section (seg);
+}
+
 const struct format_ops elf_format_ops =
 {
   bfd_target_elf_flavour,
-  0,
-  1,
+  0,   /* dfl_leading_underscore */
+  1,   /* emit_section_symbols */
+  elf_begin,
+  elf_file_symbol,
   elf_frob_symbol,
   elf_frob_file,
+  elf_frob_file_before_adjust,
   elf_frob_file_after_relocs,
   elf_s_get_size, elf_s_set_size,
   elf_s_get_align, elf_s_set_align,
+  elf_s_get_other,
+  elf_s_set_other,
+  0,   /* s_get_desc */
+  0,   /* s_set_desc */
+  0,   /* s_get_type */
+  0,   /* s_set_type */
   elf_copy_symbol_attributes,
 #ifdef NEED_ECOFF_DEBUG
   ecoff_generate_asm_lineno,
   ecoff_stab,
 #else
-  0,
-  0,                           /* process_stab */
+  0,   /* generate_asm_lineno */
+  0,   /* process_stab */
 #endif
+  elf_separate_stab_sections,
+  elf_init_stab_section,
   elf_sec_sym_ok_for_reloc,
   elf_pop_insert,
 #ifdef NEED_ECOFF_DEBUG
   elf_ecoff_set_ext,
 #else
-  0,
+  0,   /* ecoff_set_ext */
 #endif
-  obj_read_begin_hook,
-  obj_symbol_new_hook,
+  elf_obj_read_begin_hook,
+  elf_obj_symbol_new_hook
 };