cgraph.h (symtab_node): Add symver flag.
authorJan Hubicka <hubicka@ucw.cz>
Sat, 30 Nov 2019 21:03:25 +0000 (22:03 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sat, 30 Nov 2019 21:03:25 +0000 (21:03 +0000)
2019-11-30  Jan Hubicka  <hubicka@ucw.cz>

* cgraph.h (symtab_node): Add symver flag.
* cgraphunit.c (process_symver_attribute): New.
(process_common_attributes): Use process_symver_attribute.
* lto-cgraph.c (lto_output_node): Stream symver.
(lto_output_varpool_node): Stream symver.
(input_overwrite_node): Stream symver.
(input_varpool_node): Stream symver.
* output.h (do_assemble_symver): Decalre.
* symtab.c (symtab_node::dump_base): Dump symver.
(symtab_node::verify_base): Verify symver.
(symtab_node::resolve_alias): Handle symver.
* varasm.c (do_assemble_symver): New function.
* varpool.c (varpool_node::assemble_aliases): Use it.
* doc/extend.texi: (symver attribute): Document.
* config/elfos.h (ASM_OUTPUT_SYMVER_DIRECTIVE): New.

c-family/ChangeLog:

2019-11-30  Jan Hubicka  <hubicka@ucw.cz>

* c-attribs.c (handle_symver_attribute): New function
(c_common_attributes): Add symver.

From-SVN: r278878

12 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-attribs.c
gcc/cgraph.h
gcc/cgraphunit.c
gcc/config/elfos.h
gcc/doc/extend.texi
gcc/lto-cgraph.c
gcc/output.h
gcc/symtab.c
gcc/varasm.c
gcc/varpool.c

index 72c8d2e03d4134df05a23f8e2ec08cf49b8dbf64..c03556b8d3b768f2405a204f37060ab9912f8647 100644 (file)
@@ -1,3 +1,21 @@
+2019-11-30  Jan Hubicka  <hubicka@ucw.cz>
+
+       * cgraph.h (symtab_node): Add symver flag.
+       * cgraphunit.c (process_symver_attribute): New.
+       (process_common_attributes): Use process_symver_attribute.
+       * lto-cgraph.c (lto_output_node): Stream symver.
+       (lto_output_varpool_node): Stream symver.
+       (input_overwrite_node): Stream symver.
+       (input_varpool_node): Stream symver.
+       * output.h (do_assemble_symver): Decalre.
+       * symtab.c (symtab_node::dump_base): Dump symver.
+       (symtab_node::verify_base): Verify symver.
+       (symtab_node::resolve_alias): Handle symver.
+       * varasm.c (do_assemble_symver): New function.
+       * varpool.c (varpool_node::assemble_aliases): Use it.
+       * doc/extend.texi: (symver attribute): Document.
+       * config/elfos.h (ASM_OUTPUT_SYMVER_DIRECTIVE): New.
+
 2019-11-30  Richard Sandiford  <richard.sandiford@arm.com>
 
        * target.h (type_context_kind): New enum.
index 763e5a269f6546cc1432e3855737a901a98e1378..fa3b94232fb3acdbeadf92f734569bf95d4f1476 100644 (file)
@@ -1,3 +1,8 @@
+2019-11-30  Jan Hubicka  <hubicka@ucw.cz>
+
+       * c-attribs.c (handle_symver_attribute): New function
+       (c_common_attributes): Add symver.
+
 2019-11-30  Richard Sandiford  <richard.sandiford@arm.com>
 
        * c-common.c (pointer_int_sum): Use verify_type_context to check
index 35388e83a83a936b231f4fe2338e795aa28beaaf..dc56e2ec62ffc7f494cc59ddf02452ac0cb406de 100644 (file)
@@ -66,6 +66,7 @@ static tree handle_stack_protect_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noclone_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nocf_check_attribute (tree *, tree, tree, int, bool *);
+static tree handle_symver_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noicf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noipa_attribute (tree *, tree, tree, int, bool *);
 static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
@@ -475,6 +476,8 @@ const struct attribute_spec c_common_attribute_table[] =
                              NULL },
   { "nocf_check",            0, 0, false, true, true, true,
                              handle_nocf_check_attribute, NULL },
+  { "symver",                1, -1, true, false, false, false,
+                             handle_symver_attribute, NULL},
   { "copy",                   1, 1, false, false, false, false,
                              handle_copy_attribute, NULL },
   { "noinit",                0, 0, true,  false, false, false,
@@ -2335,6 +2338,62 @@ handle_noplt_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "symver" attribute.  */
+
+static tree
+handle_symver_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+                        int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree symver;
+  const char *symver_str;
+
+  if (TREE_CODE (*node) != FUNCTION_DECL && TREE_CODE (*node) != VAR_DECL)
+    {
+      warning (OPT_Wattributes,
+              "%<symver%> attribute only applies to functions and variables");
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  if (!decl_in_symtab_p (*node))
+    {
+      warning (OPT_Wattributes,
+              "%<symver%> attribute is only applicable to symbols");
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  for (; args; args = TREE_CHAIN (args))
+    {
+      symver = TREE_VALUE (args);
+      if (TREE_CODE (symver) != STRING_CST)
+       {
+         error ("%<symver%> attribute argument not a string constant");
+         *no_add_attrs = true;
+         return NULL_TREE;
+       }
+
+      symver_str = TREE_STRING_POINTER (symver);
+
+      int ats = 0;
+      for (int n = 0; (int)n < TREE_STRING_LENGTH (symver); n++)
+       if (symver_str[n] == '@')
+         ats++;
+
+      if (ats != 1 && ats != 2)
+       {
+         error ("symver attribute argument must have format %<name@nodename%>");
+         error ("%<symver%> attribute argument %qs must contain one or two "
+                "%<@%>", symver_str);
+         *no_add_attrs = true;
+         return NULL_TREE;
+       }
+    }
+
+  return NULL_TREE;
+}
+
+
 /* Handle an "alias" or "ifunc" attribute; arguments as in
    struct attribute_spec.handler, except that IS_ALIAS tells us
    whether this is an alias as opposed to ifunc attribute.  */
index 2aedaaedd611db5bd61d36a1f9a7872bbabc35fc..9c086fedaef4f436eae002e50c073d2c3c6626dc 100644 (file)
@@ -497,6 +497,8 @@ public:
      and their visibility needs to be copied from their "masters" at
      the end of parsing.  */
   unsigned cpp_implicit_alias : 1;
+  /* The alias is a symbol version.  */
+  unsigned symver : 1;
   /* Set once the definition was analyzed.  The list of references and
      other properties are built during analysis.  */
   unsigned analyzed : 1;
index aa26160bf3ff14aabd80ae8a0174c6c6586dd682..75ff10bc0cdbf32b4d734d48d2fa97f9d6c06b13 100644 (file)
@@ -711,6 +711,89 @@ symbol_table::process_same_body_aliases (void)
   cpp_implicit_aliases_done = true;
 }
 
+/* Process a symver attribute.  */
+
+static void
+process_symver_attribute (symtab_node *n)
+{
+  tree value = lookup_attribute ("symver", DECL_ATTRIBUTES (n->decl));
+
+  if (!value)
+    return;
+  if (lookup_attribute ("symver", TREE_CHAIN (value)))
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "multiple versions for one symbol");
+      return;
+    }
+  tree symver = get_identifier_with_length
+                 (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (value))),
+                  TREE_STRING_LENGTH (TREE_VALUE (TREE_VALUE (value))));
+  symtab_node *def = symtab_node::get_for_asmname (symver);
+
+  if (def)
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "duplicate definition of a symbol version");
+      inform (DECL_SOURCE_LOCATION (def->decl),
+             "same version was previously defined here");
+      return;
+    }
+  if (!n->definition)
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "symbol needs to be defined to have a version");
+      return;
+    }
+  if (DECL_COMMON (n->decl))
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "common symbol cannot be versioned");
+      return;
+    }
+  if (DECL_COMDAT (n->decl))
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "comdat symbol cannot be versioned");
+      return;
+    }
+  if (n->weakref)
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "weakref cannot be versioned");
+      return;
+    }
+  if (!TREE_PUBLIC (n->decl))
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "versioned symbol must be public");
+      return;
+    }
+  if (DECL_VISIBILITY (n->decl) != VISIBILITY_DEFAULT)
+    {
+      error_at (DECL_SOURCE_LOCATION (n->decl),
+               "versioned symbol must have default visibility");
+      return;
+    }
+
+  /* Create new symbol table entry representing the version.  */
+  tree new_decl = copy_node (n->decl);
+
+  DECL_INITIAL (new_decl) = NULL_TREE;
+  if (TREE_CODE (new_decl) == FUNCTION_DECL)
+    DECL_STRUCT_FUNCTION (new_decl) = NULL;
+  SET_DECL_ASSEMBLER_NAME (new_decl, symver);
+  TREE_PUBLIC (new_decl) = 1;
+  DECL_ATTRIBUTES (new_decl) = NULL;
+
+  symtab_node *symver_node = symtab_node::get_create (new_decl);
+  symver_node->alias = true;
+  symver_node->definition = true;
+  symver_node->symver = true;
+  symver_node->create_reference (n, IPA_REF_ALIAS, NULL);
+  symver_node->analyzed = true;
+}
+
 /* Process attributes common for vars and functions.  */
 
 static void
@@ -730,6 +813,7 @@ process_common_attributes (symtab_node *node, tree decl)
 
   if (lookup_attribute ("no_reorder", DECL_ATTRIBUTES (decl)))
     node->no_reorder = 1;
+  process_symver_attribute (node);
 }
 
 /* Look for externally_visible and used attributes and mark cgraph nodes
@@ -2137,8 +2221,12 @@ cgraph_node::assemble_thunks_and_aliases (void)
          /* Force assemble_alias to really output the alias this time instead
             of buffering it in same alias pairs.  */
          TREE_ASM_WRITTEN (decl) = 1;
-         do_assemble_alias (alias->decl,
-                            DECL_ASSEMBLER_NAME (decl));
+         if (alias->symver)
+           do_assemble_symver (alias->decl,
+                               DECL_ASSEMBLER_NAME (decl));
+         else
+           do_assemble_alias (alias->decl,
+                              DECL_ASSEMBLER_NAME (decl));
          alias->assemble_thunks_and_aliases ();
          TREE_ASM_WRITTEN (decl) = saved_written;
        }
index e00d437c27ac61f8674a9085757889c2b9b222d7..e91c0f34c72e490dc09b4f16d2f9c8f58933e62c 100644 (file)
@@ -248,6 +248,17 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     }                                  \
   while (0)
 
+#define ASM_OUTPUT_SYMVER_DIRECTIVE(FILE, NAME, NAME2)         \
+  do                                                           \
+    {                                                          \
+      fputs ("\t.symver\t", (FILE));                           \
+      assemble_name ((FILE), (NAME));                          \
+      fputs (", ", (FILE));                                    \
+      assemble_name ((FILE), (NAME2));                         \
+      fputc ('\n', (FILE));                                    \
+    }                                                          \
+  while (0)
+
 /* The following macro defines the format used to output the second
    operand of the .type assembler directive.  Different svr4 assemblers
    expect various different forms for this operand.  The one given here
index 56a3d60a947293940c27a22cd4547ff059e37431..b487e03fa963974747fbf2d3a96437e213f97275 100644 (file)
@@ -3711,6 +3711,41 @@ Function Attributes}, @ref{PowerPC Function Attributes},
 @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
 for details.
 
+@item symver ("@var{name2}@@@var{nodename}")
+On ELF targets this attribute creates a symbol version.  The @var{name2} part
+of the parameter is the actual name of the symbol by which it will be
+externally referenced.  The @code{nodename} portion should be the name of a
+node specified in the version script supplied to the linker when building a
+shared library.  Versioned symbol must be defined and must be exported with
+default visibility.
+
+@smallexample
+__attribute__ ((__symver__ ("foo@@VERS_1"))) int
+foo_v1 (void)
+@{
+@}
+@end smallexample
+
+Will produce a @code{.symver foo_v1, foo@@VERS_1} directive in the assembler
+output. 
+
+It's an error to define multiple version of a given symbol.  In such case
+an alias can be used.
+
+@smallexample
+__attribute__ ((__symver__ ("foo@@VERS_2")))
+__attribute__ ((alias ("foo_v1")))
+int symver_foo_v1 (void);
+@end smallexample
+
+This example creates an alias of @code{foo_v1} with symbol name
+@code{symver_foo_v1} which will be version @code{VERS_2} of @code{foo}.
+
+Finally if the parameter is @code{"@var{name2}@@@@@var{nodename}"} then in
+addition to creating a symbol version (as if
+@code{"@var{name2}@@@var{nodename}"} was used) the version will be also used
+to resolve @var{name2} by the linker.
+
 @item target_clones (@var{options})
 @cindex @code{target_clones} function attribute
 The @code{target_clones} attribute is used to specify that a function
index d2a5d5376fe49654220f4e13890e80519e0ed575..b5221cd41f93daf066933ae8988ae19d34a67fb3 100644 (file)
@@ -526,6 +526,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
   bp_pack_value (&bp, node->alias, 1);
   bp_pack_value (&bp, node->transparent_alias, 1);
   bp_pack_value (&bp, node->weakref, 1);
+  bp_pack_value (&bp, node->symver, 1);
   bp_pack_value (&bp, node->frequency, 2);
   bp_pack_value (&bp, node->only_called_at_startup, 1);
   bp_pack_value (&bp, node->only_called_at_exit, 1);
@@ -609,6 +610,7 @@ lto_output_varpool_node (struct lto_simple_output_block *ob, varpool_node *node,
   bp_pack_value (&bp, node->alias, 1);
   bp_pack_value (&bp, node->transparent_alias, 1);
   bp_pack_value (&bp, node->weakref, 1);
+  bp_pack_value (&bp, node->symver, 1);
   bp_pack_value (&bp, node->analyzed && (!boundary_p || node->alias), 1);
   gcc_assert (node->definition || !node->analyzed);
   /* Constant pool initializers can be de-unified into individual ltrans units.
@@ -1173,6 +1175,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
   node->alias = bp_unpack_value (bp, 1);
   node->transparent_alias = bp_unpack_value (bp, 1);
   node->weakref = bp_unpack_value (bp, 1);
+  node->symver = bp_unpack_value (bp, 1);
   node->frequency = (enum node_frequency)bp_unpack_value (bp, 2);
   node->only_called_at_startup = bp_unpack_value (bp, 1);
   node->only_called_at_exit = bp_unpack_value (bp, 1);
@@ -1371,6 +1374,7 @@ input_varpool_node (struct lto_file_decl_data *file_data,
   node->alias = bp_unpack_value (&bp, 1);
   node->transparent_alias = bp_unpack_value (&bp, 1);
   node->weakref = bp_unpack_value (&bp, 1);
+  node->symver = bp_unpack_value (&bp, 1);
   node->analyzed = bp_unpack_value (&bp, 1);
   node->used_from_other_partition = bp_unpack_value (&bp, 1);
   node->in_other_partition = bp_unpack_value (&bp, 1);
index 6cccada4aeb1decad5050a282c8afa1524d5b9f4..4ac7d5077337bbd88bd1c157d70d9f9c20d028e3 100644 (file)
@@ -167,6 +167,7 @@ extern int decode_reg_name (const char *);
 extern int decode_reg_name_and_count (const char *, int *);
 
 extern void do_assemble_alias (tree, tree);
+extern void do_assemble_symver (tree, tree);
 
 extern void default_assemble_visibility (tree, int);
 
index 6816bd1f884faaf7983a3dbcad5cedc99c74a8e1..f4317d02b719bc817ef2b4bbb8ddd66c42d22ed7 100644 (file)
@@ -848,6 +848,8 @@ symtab_node::dump_base (FILE *f)
     fprintf (f, " transparent_alias");
   if (weakref)
     fprintf (f, " weakref");
+  if (symver)
+    fprintf (f, " symver");
   if (cpp_implicit_alias)
     fprintf (f, " cpp_implicit_alias");
   if (alias_target)
@@ -1147,6 +1149,11 @@ symtab_node::verify_base (void)
       error ("node is transparent_alias but not an alias");
       error_found = true;
     }
+  if (symver && !alias)
+    {
+      error ("node is symver but not alias");
+      error_found = true;
+    }
   if (same_comdat_group)
     {
       symtab_node *n = same_comdat_group;
@@ -1782,7 +1789,9 @@ symtab_node::resolve_alias (symtab_node *target, bool transparent)
          if (target->get_comdat_group ())
            alias_alias->add_to_same_comdat_group (target);
        }
-      if (!alias_alias->transparent_alias || transparent)
+      if ((!alias_alias->transparent_alias
+          && !alias_alias->symver)
+         || transparent)
        {
          alias_alias->remove_all_references ();
          alias_alias->create_reference (target, IPA_REF_ALIAS, NULL);
index 57365ad2e761a0f3e91f7b930eabb379054fc965..d9d02b3b2633d1178915b92d723f5a240457c392 100644 (file)
@@ -5960,6 +5960,23 @@ do_assemble_alias (tree decl, tree target)
 #endif
 }
 
+/* Output .symver directive.  */
+
+void
+do_assemble_symver (tree decl, tree target)
+{
+  tree id = DECL_ASSEMBLER_NAME (decl);
+  ultimate_transparent_alias_target (&id);
+  ultimate_transparent_alias_target (&target);
+#ifdef ASM_OUTPUT_SYMVER_DIRECTIVE
+  ASM_OUTPUT_SYMVER_DIRECTIVE (asm_out_file,
+                              IDENTIFIER_POINTER (target),
+                              IDENTIFIER_POINTER (id));
+#else
+  error ("symver is only supported on ELF platforms");
+#endif
+}
+
 /* Emit an assembler directive to make the symbol for DECL an alias to
    the symbol for TARGET.  */
 
index ff49ad9f5175d6722871061b740dee736983fefa..1a30ae49d548ea4175cb2c14709d18aea99a79aa 100644 (file)
@@ -540,7 +540,10 @@ varpool_node::assemble_aliases (void)
   FOR_EACH_ALIAS (this, ref)
     {
       varpool_node *alias = dyn_cast <varpool_node *> (ref->referring);
-      if (!alias->transparent_alias)
+      if (alias->symver)
+       do_assemble_symver (alias->decl,
+                           DECL_ASSEMBLER_NAME (decl));
+      else if (!alias->transparent_alias)
        do_assemble_alias (alias->decl,
                           DECL_ASSEMBLER_NAME (decl));
       alias->assemble_aliases ();