2001-07-10 John Healy <jhealy@redhat.com>
[binutils-gdb.git] / gas / config / obj-elf.c
index 1c481a6f5b410b5e5f539743f4497e75b8c23428..9498c1884359d46b6686e3f85be2187c8310a6a5 100644 (file)
@@ -1,5 +1,5 @@
 /* ELF object file format
-   Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 99, 2000
+   Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -17,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"
@@ -75,8 +75,6 @@ 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));
 
@@ -98,7 +96,7 @@ static const pseudo_typeS elf_pseudo_table[] =
   {"version", obj_elf_version, 0},
   {"weak", obj_elf_weak, 0},
 
-  /* These define symbol visibility. */
+  /* These define symbol visibility.  */
   {"internal", obj_elf_visibility, STV_INTERNAL},
   {"hidden", obj_elf_visibility, STV_HIDDEN},
   {"protected", obj_elf_visibility, STV_PROTECTED},
@@ -113,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},
@@ -544,7 +542,6 @@ obj_elf_visibility (visibility)
   demand_empty_rest_of_line ();
 }
 
-
 static segT previous_section;
 static int previous_subsection;
 
@@ -557,7 +554,6 @@ struct section_stack
 
 static struct section_stack *section_stack;
 
-
 /* Handle the .section pseudo-op.  This code supports two different
    syntaxes.
 
@@ -624,9 +620,9 @@ 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;
 {
   asection *old_sec;
   segT sec;
@@ -694,7 +690,9 @@ obj_elf_change_section (name, type, attr, push)
           | ((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_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);
 #endif
@@ -708,6 +706,8 @@ obj_elf_change_section (name, type, attr, push)
         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);
@@ -723,8 +723,10 @@ obj_elf_change_section (name, type, attr, push)
         saw the first time.  */
       if ((old_sec->flags ^ flags)
          & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
-            | SEC_EXCLUDE | SEC_SORT_ENTRIES))
+            | 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
@@ -752,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)
@@ -826,6 +834,7 @@ obj_elf_section (push)
 {
   char *name, *beg, *end;
   int type, attr, dummy;
+  int entsize;
 
 #ifndef TC_I370
   if (flag_mri)
@@ -881,6 +890,7 @@ obj_elf_section (push)
 
   type = SHT_NULL;
   attr = 0;
+  entsize = 0;
 
   if (*input_line_pointer == ',')
     {
@@ -923,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
        {
@@ -952,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.  */
@@ -1085,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 ();
 }
@@ -1111,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 != ',')
     {
@@ -1137,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 ();
@@ -1156,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;
 {
@@ -1189,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;
@@ -1216,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;
 {
@@ -1250,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;
@@ -1259,10 +1303,10 @@ 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
@@ -1308,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 ()))
@@ -1401,7 +1445,7 @@ 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
@@ -1411,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
@@ -1422,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 ();
@@ -1435,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;
 
@@ -1448,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 ();
 }
@@ -1502,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);
@@ -1636,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
@@ -1649,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'"),
@@ -1666,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);
+           }
        }
     }
 
@@ -1746,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
@@ -1950,6 +2057,7 @@ const struct format_ops elf_format_ops =
   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,