This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] detect attribute mismatches in alias declarations (PR 81824)


PR 81824 is a request to detect and diagnose alias declarations
with less restrictive attributes than those of their targets.
I promised I'd implement this for GCC 9 so with the end of
stage 1 approaching I figured it was about time to post my
attempt at this enhancement.  I expect it to need tweaking
to make it easier to adopt.

The solution reuses for this purpose the -Wmissing-attributes
warning introduced in GCC 8 for C++.  It goes beyond the C++
warning and also beyond what Joseph asked for and detects both
less and more restrictive attributes. (The latter triggers
-Wattributes but with the growing number of distinct checkes
in -Wattributes it might be worth thinking about splitting
some out into new options.)

Testing the patch with Glibc triggers thousands of warnings of
both kinds.  After reviewing a small subset it became apparent
that dealing with the inconsistencies on such a scale calls for
a convenient mechanism to (at at a minimum) automatically copy
attributes between declarations, similar to how __typeof__ makes
it possible to use the type of an existing declaration as that
of a new one.  The patch helps with this by introducing a new
attribute called copy.  The attribute copies attributes from
one declaration (or type) to another.  The attribute doesn't
resolve all the warnings but it helps.

The class of warnings I noticed that can't be so easily handled
are due to inconsistencies between ifuncs and their resolvers.
One way to solve it might be to have resolvers automatically
"inherit" all the attributes of their targets (and enhance
GCC to warn for violations).  Another way might be to expect
resolvers to be explicitly declared with attribute copy to copy
the attributes of all the targets (and also warn for violations).

In the patch I have hardcoded a few attributes that don't get
copied I call those linkage and visibility attributes.  I'm not
too happy about hardcoding things like this but the only other
alternative I could think of was parameterizing the copy
attribute on the set of other attributes not to copy, but since
those would almost always be the same as the harcoded ones, it
didn't seem worthwhile.

Martin

PS With the attached GCC and Glibc patches I get the following
breakdown of warnings in Glibc (the numbers are the total count,
the number of unique instances, and the number of files they are
in):

  Diagnostic                        Count   Unique    Files
  -Wattributes                       1743      724      599
  -Wmissing-attributes                 90       24       22

The -Wattributes are of the sort:

version.c:54:37: warning: ‘gnu_get_libc_release’ specifies more restrictive attribute than its target ‘__gnu_get_libc_release’: ‘nothrow’ [-Wattributes]

(The absence of the nothrow attribute accounts for the majority
of the warnings.)

An example of the -Wmissing-attributes instance is:

./../include/libc-symbols.h:534:26: warning: ‘__EI___redirect_strcat’ specifies less restrictive attributes than its target ‘strcat’: ‘leaf’, ‘nonnull’ [-Wmissing-attributes]

This is the ifunc resolver mismatch I mention above.
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index 8b9273c..b0fb728 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -143,7 +143,7 @@
    If weak aliases are not available, this defines a strong alias.  */
 # define weak_alias(name, aliasname) _weak_alias (name, aliasname)
 # define _weak_alias(name, aliasname) \
-  extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
+  extern __typeof (name) aliasname __attribute__ ((weak, alias (#name), copy (name)));
 
 /* Same as WEAK_ALIAS, but mark symbol as hidden.  */
 # define weak_hidden_alias(name, aliasname) \
@@ -532,7 +532,7 @@ for linking")
 #  define __hidden_ver1(local, internal, name) \
   extern __typeof (name) __EI_##name __asm__(__hidden_asmname (#internal)); \
   extern __typeof (name) __EI_##name \
-	__attribute__((alias (__hidden_asmname (#local))))
+    __attribute__((alias (__hidden_asmname (#local)), copy (name)))
 #  define hidden_ver(local, name)	__hidden_ver1(local, __GI_##name, name);
 #  define hidden_data_ver(local, name)	hidden_ver(local, name)
 #  define hidden_def(name)		__hidden_ver1(__GI_##name, name, name);
@@ -545,7 +545,8 @@ for linking")
 #  define __hidden_nolink1(local, internal, name, version) \
   __hidden_nolink2 (local, internal, name, version)
 #  define __hidden_nolink2(local, internal, name, version) \
-  extern __typeof (name) internal __attribute__ ((alias (#local))); \
+  extern __typeof (name) internal __attribute__ ((alias (#local), \
+						    copy (name)));	\
   __hidden_nolink3 (local, internal, #name "@" #version)
 #  define __hidden_nolink3(local, internal, vername) \
   __asm__ (".symver " #internal ", " vername);
PR middle-end/81824 - Warn for missing attributes with function aliases

gcc/c-family/ChangeLog:

	PR middle-end/81824
	* c-attribs.c (handle_copy_attribute_impl): New function.
	* c-attribs.c (handle_copy_attribute): Same.	.

gcc/cp/ChangeLog:

	PR middle-end/81824
	* pt.c (warn_spec_missing_attributes): Move code to attribs.c.
	Call decls_mismatched_attributes.

gcc/ChangeLog:

	PR middle-end/81824
	* attribs.c (has_attribute): New helper function.
	(decls_mismatched_attributes, maybe_diag_alias_attributes): Same.
	* attribs.h (decls_mismatched_attributes): Declare.
	* cgraphunit.c (handle_alias_pairs): Call maybe_diag_alias_attributes.
	* doc/extend.texi (Common Function Attributes): Document copy.
	(Common Variable Attributes): Same.
	* doc/invoke.texi (-Wmissing-attributes): Document enhancement.

libgomp/ChangeLog:

	PR c/81824
	* libgomp.h (strong_alias, ialias, ialias_redirect): Use attribute
	copy.

gcc/testsuite/ChangeLog:

	PR middle-end/81824
	* gcc.dg/Wmissing-attributes.c: New test.
	* gcc.dg/attr-copy.c: New test.
	* gcc.dg/attr-copy-2.c: New test.
	* gcc.dg/attr-copy-3.c: New test.
	* gcc.dg/attr-copy-4.c: New test.

diff --git a/gcc/attribs.c b/gcc/attribs.c
index 8b72127..22021bf 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -30,6 +30,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "selftest.h"
 #include "hash-set.h"
+#include "diagnostic.h"
+#include "pretty-print.h"
+#include "intl.h"
 
 /* Table of the tables of attributes (common, language, format, machine)
    searched.  */
@@ -1812,6 +1815,178 @@ private_lookup_attribute (const char *attr_name, size_t attr_len, tree list)
   return list;
 }
 
+/* Return true if the function decl or type NODE has been declared
+   with attribute ANAME among attributes ATTRS.  */
+
+static bool
+has_attribute (tree node, tree attrs, const char *aname)
+{
+  if (!strcmp (aname, "const"))
+    {
+      if (DECL_P (node) && TREE_READONLY (node))
+	return true;
+    }
+  else if (!strcmp (aname, "malloc"))
+    {
+      if (DECL_P (node) && DECL_IS_MALLOC (node))
+	return true;
+    }
+  else if (!strcmp (aname, "noreturn"))
+    {
+      if (DECL_P (node) && TREE_THIS_VOLATILE (node))
+	return true;
+    }
+  else if (!strcmp (aname, "nothrow"))
+    {
+      if (TREE_NOTHROW (node))
+	return true;
+    }
+  else if (!strcmp (aname, "pure"))
+    {
+      if (DECL_P (node) && DECL_PURE_P (node))
+	return true;
+    }
+
+  return lookup_attribute (aname, attrs);
+}
+
+/* Return the number of mismatched function or type attributes between
+   the "template" function declaration TMPL and DECL.  The word "template"
+   doesn't necessarily refer to a C++ template but rather a declaration
+   whose attributes should be matched by those on DECL.  For a non-zero
+   return value set *ATTRSTR to a string representation of the list of
+   mismatched attributes with quoted names.
+   ATTRLIST is a list of additional attributes that SPEC should be
+   taken to ultimately be declared with.  */
+
+unsigned
+decls_mismatched_attributes (tree tmpl, tree decl, tree attrlist,
+			     const char* const blacklist[],
+			     pretty_printer *attrstr)
+{
+  if (TREE_CODE (tmpl) != FUNCTION_DECL)
+    return 0;
+
+  /* Avoid warning if either declaration or its type is deprecated.  */
+  if (TREE_DEPRECATED (tmpl)
+      || TREE_DEPRECATED (decl))
+    return 0;
+
+  const tree tmpls[] = { tmpl, TREE_TYPE (tmpl) };
+  const tree decls[] = { decl, TREE_TYPE (decl) };
+
+  if (TREE_DEPRECATED (tmpls[1])
+      || TREE_DEPRECATED (decls[1])
+      || TREE_DEPRECATED (TREE_TYPE (tmpls[1]))
+      || TREE_DEPRECATED (TREE_TYPE (decls[1])))
+    return 0;
+
+  tree tmpl_attrs[] = { DECL_ATTRIBUTES (tmpl), TYPE_ATTRIBUTES (tmpls[1]) };
+  tree decl_attrs[] = { DECL_ATTRIBUTES (decl), TYPE_ATTRIBUTES (decls[1]) };
+
+  if (!decl_attrs[0])
+    decl_attrs[0] = attrlist;
+  else if (!decl_attrs[1])
+    decl_attrs[1] = attrlist;
+
+  /* Avoid warning if the template has no attributes.  */
+  if (!tmpl_attrs[0] && !tmpl_attrs[1])
+    return 0;
+
+  /* Avoid warning if either declaration contains an attribute on
+     the white list below.  */
+  const char* const whitelist[] = {
+    "error", "warning"
+  };
+
+  for (unsigned i = 0; i != 2; ++i)
+    for (unsigned j = 0; j != sizeof whitelist / sizeof *whitelist; ++j)
+      if (lookup_attribute (whitelist[j], tmpl_attrs[i])
+	  || lookup_attribute (whitelist[j], decl_attrs[i]))
+	return 0;
+
+  /* Put together a list of the black-listed attributes that the template
+     is declared with and the declaration is not, in case it's not apparent
+     from the most recent declaration of the template.  */
+  unsigned nattrs = 0;
+
+  for (unsigned i = 0; blacklist[i]; ++i)
+    {
+      for (unsigned j = 0; j != 2; ++j)
+	{
+	  if (!has_attribute (tmpls[j], tmpl_attrs[j], blacklist[i]))
+	    continue;
+
+	  for (unsigned k = 0; k != 1 + !!decl_attrs[1]; ++k)
+	    {
+	      if (has_attribute (decls[k], decl_attrs[k], blacklist[i]))
+		break;
+
+	      if (nattrs)
+		pp_string (attrstr, ", ");
+	      pp_begin_quote (attrstr, pp_show_color (global_dc->printer));
+	      pp_string (attrstr, blacklist[i]);
+	      pp_end_quote (attrstr, pp_show_color (global_dc->printer));
+	      ++nattrs;
+	    }
+	}
+    }
+
+  return nattrs;
+}
+
+/* Issue a warning for the declaration ALIAS for TARGET where ALIAS
+   specifies either attributes that are incompatible with those of
+   TARGET, or attributes that are missing and that declaring ALIAS
+   with would benefit.  */
+
+void
+maybe_diag_alias_attributes (tree alias, tree target)
+{
+  const char* const blacklist[] = {
+    "alloc_align", "alloc_size", "cold", "const", "hot", "leaf", "malloc",
+    "nonnull", "noreturn", "nothrow", "pure", "returns_nonnull",
+    "returns_twice", NULL
+  };
+
+  /* Detect alias declarations that are more restrictive than their
+     targets first.  Those indicate potential codegen bugs.  */
+  pretty_printer attrnames;
+  if (unsigned n = decls_mismatched_attributes (alias, target, NULL_TREE,
+						blacklist, &attrnames))
+    {
+      auto_diagnostic_group d;
+      if (warning_n (DECL_SOURCE_LOCATION (alias), OPT_Wattributes, n,
+		     "%qD specifies more restrictive attribute than "
+		     "its target %qD: %s",
+		     "%qD specifies more restrictive attributes than "
+		     "its target %qD: %s",
+		     alias, target, pp_formatted_text (&attrnames)))
+	inform (DECL_SOURCE_LOCATION (target),
+		"%qD target declared here", alias);
+      return;
+    }
+
+  /* Detect alias declarations that are less restrictive than their
+     targets.  Those suggest potential optimization optimization
+     opportunities (solved by adding the missing attribute(s) to
+     the alias).  */
+  if (unsigned n = decls_mismatched_attributes (target, alias, NULL_TREE,
+						blacklist, &attrnames))
+    {
+      auto_diagnostic_group d;
+      if (warning_n (DECL_SOURCE_LOCATION (alias), OPT_Wmissing_attributes, n,
+		     "%qD specifies less restrictive attribute than "
+		     "its target %qD: %s",
+		     "%qD specifies less restrictive attributes than "
+		     "its target %qD: %s",
+		     alias, target, pp_formatted_text (&attrnames)))
+	inform (DECL_SOURCE_LOCATION (target),
+		"%qD target declared here", alias);
+    }
+}
+
+
 #if CHECKING_P
 
 namespace selftest
diff --git a/gcc/attribs.h b/gcc/attribs.h
index c277e1b..5b76c4c 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -105,6 +105,12 @@ extern int attribute_list_contained (const_tree, const_tree);
 extern tree private_lookup_attribute (const char *attr_name, size_t attr_len,
 				      tree list);
 
+extern unsigned decls_mismatched_attributes (tree, tree, tree,
+					     const char* const[],
+					     pretty_printer*);
+
+extern void maybe_diag_alias_attributes (tree, tree);
+
 /* For a given IDENTIFIER_NODE, strip leading and trailing '_' characters
    so that we have a canonical form of attribute names.  */
 
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 5454e09..3a88766 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -146,6 +146,7 @@ static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *);
 static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *);
 static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
 						       int, bool *);
+static tree handle_copy_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
@@ -455,6 +456,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      NULL },
   { "nocf_check",	      0, 0, false, true, true, true,
 			      handle_nocf_check_attribute, NULL },
+  { "copy",                   1, 1, false, false, false, false,
+			      handle_copy_attribute, NULL },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -2134,6 +2137,172 @@ handle_alias_attribute (tree *node, tree name, tree args,
   return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
 }
 
+/* Handle the "copy" attribute by copying the set of attributes
+   from the symbol referenced by ARGS to the declaration of *NODE.  */
+
+static tree
+handle_copy_attribute_impl (tree *node, tree name, tree args,
+			    int flags, bool *no_add_attrs)
+{
+  /* Do not apply the copy attribute itself.  It serves no purpose
+     other than to copy other attributes.  */
+  *no_add_attrs = true;
+
+  tree decl = *node;
+
+  tree ref = TREE_VALUE (args);
+  if (ref == error_mark_node)
+    return NULL_TREE;
+
+  if (TREE_CODE (ref) == STRING_CST)
+    {
+      /* Explicitly handle this case since using a string literal
+	 as an argument is a likely mistake.  */
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute argument cannot be a string",
+		name);
+      return NULL_TREE;
+    }
+
+  if (CONSTANT_CLASS_P (ref)
+      && (INTEGRAL_TYPE_P (TREE_TYPE (ref))
+	  || FLOAT_TYPE_P (TREE_TYPE (ref))))
+    {
+      /* Similar to the string case, since some function attributes
+	 accept literal numbers as arguments (e.g., alloc_size or
+	 nonnull) using one here is a likely mistake.  */
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute argument cannot be a constant arithmetic "
+		"expression",
+		name);
+      return NULL_TREE;
+    }
+
+  if (ref == node[1])
+    {
+      /* Another possible mistake (but indirect self-references aren't
+	 and diagnosed and shouldn't be).  */
+      if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		      "%qE attribute ignored on a redeclaration "
+		      "of the referenced symbol",
+		      name))
+	inform (DECL_SOURCE_LOCATION (node[1]),
+		"previous declaration here");
+      return NULL_TREE;
+    }
+
+  /* Consider address-of expressions in the attribute argument
+     as requests to copy from the referenced entity.  For constant
+     expressions, consider those to be requests to copy from their
+     type, such as in:
+       struct __attribute__ (copy ((struct T *)0)) U { ... };
+     which copies type attributes from struct T to the declaration
+     of struct U.  */
+  if (TREE_CODE (ref) == ADDR_EXPR)
+    ref = TREE_OPERAND (ref, 0);
+  else if (CONSTANT_CLASS_P (ref))
+    ref = TREE_TYPE (ref);
+
+  if (DECL_P (decl))
+    {
+      if ((VAR_P (decl)
+	   && (TREE_CODE (ref) == FUNCTION_DECL
+	       || (EXPR_P (ref)
+		   && POINTER_TYPE_P (TREE_TYPE (ref))
+		   && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (ref))))))
+	  || (TREE_CODE (decl) == FUNCTION_DECL
+	      && (VAR_P (ref)
+		  || (EXPR_P (ref)
+		      && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (ref))))))
+	{
+	  /* It makes no sense to try to copy function attributes
+	     to a variable, or variable attributes to a function.  */
+	  if (warning (OPT_Wattributes,
+		       "%qE attribute ignored on a declaration of "
+		       "a different kind than referenced symbol",
+		       name)
+	      && DECL_P (ref))
+	    inform (DECL_SOURCE_LOCATION (ref),
+		    "symbol %qD referenced by %qD declared here", ref, decl);
+	  return NULL_TREE;
+	}
+	
+      tree attrs = NULL_TREE;
+      if (DECL_P (ref))
+	attrs = DECL_ATTRIBUTES (ref);
+      else if (TYPE_P (ref))
+	attrs = TYPE_ATTRIBUTES (ref);
+
+      /* Copy decl attributes from REF to DECL.  */
+      for (tree at = attrs; at; at = TREE_CHAIN (at))
+	{
+	  /* Avoid copying attributes that affect a symbol linkage or
+	     visibility since those in all likelihood only apply to
+	     the target.
+	     FIXME: make it possible to specify which attributes to
+	     copy or not to copy in the copy attribute itself.  */
+	  tree atname = get_attribute_name (at);
+	  if (is_attribute_p ("alias", atname)
+	      || is_attribute_p ("ifunc", atname)
+	      || is_attribute_p ("visibility", atname)
+	      || is_attribute_p ("weak", atname)
+	      || is_attribute_p ("weakref", atname))
+	    continue;
+
+	  tree atargs = TREE_VALUE (at);
+	  /* Create a copy of just the one attribute ar AT, including
+	     its argumentsm and add it to DECL.  */
+	  tree attr = tree_cons (atname, copy_list (atargs), NULL_TREE);
+	  decl_attributes (&decl, attr, flags);
+	}
+
+      /* Proceed to copy type attributes below.  */
+    }
+  else if (!TYPE_P (decl))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute must apply to a declaration",
+		name);
+      return NULL_TREE;
+    }
+
+  if (DECL_P (ref) || EXPR_P (ref))
+    ref = TREE_TYPE (ref);
+
+  if (POINTER_TYPE_P (ref))
+    ref = TREE_TYPE (ref);
+
+  tree attrs = TYPE_ATTRIBUTES (ref);
+
+  /* Copy type attributes from REF to DECL.  */
+  for (tree at = attrs; at; at = TREE_CHAIN (at))
+    decl_attributes (&decl, at, flags);
+
+  return NULL_TREE;
+}
+
+/* Handle the "copy" attribute by copying the set of attributes
+   from the symbol referenced by ARGS to the declaration of *NODE.  */
+
+static tree
+handle_copy_attribute (tree *node, tree name, tree args,
+		       int flags, bool *no_add_attrs)
+{
+  /* Break cycles in circular references.  */
+  static hash_set<tree> attr_copy_visited;
+
+  if (attr_copy_visited.contains (*node))
+    return NULL_TREE;
+
+  attr_copy_visited.add (*node);
+
+  tree ret = handle_copy_attribute_impl (node, name, args, flags, no_add_attrs);
+
+  attr_copy_visited.remove (*node);
+
+  return ret;
+}
+
 /* Handle a "weakref" attribute; arguments as in struct
    attribute_spec.handler.  */
 
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index ec490d7..48fc4cc 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1437,6 +1437,8 @@ handle_alias_pairs (void)
 	{
 	  maybe_diag_incompatible_alias (p->decl, target_node->decl);
 
+	  maybe_diag_alias_attributes (p->decl, target_node->decl);
+
 	  cgraph_node *src_node = cgraph_node::get (p->decl);
 	  if (src_node && src_node->definition)
 	    src_node->reset ();
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b8b6545..cf4a6cd 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2647,81 +2647,19 @@ warn_spec_missing_attributes (tree tmpl, tree spec, tree attrlist)
   if (DECL_FUNCTION_TEMPLATE_P (tmpl))
     tmpl = DECL_TEMPLATE_RESULT (tmpl);
 
-  if (TREE_CODE (tmpl) != FUNCTION_DECL)
-    return;
-
-  /* Avoid warning if either declaration or its type is deprecated.  */
-  if (TREE_DEPRECATED (tmpl)
-      || TREE_DEPRECATED (spec))
-    return;
-
-  tree tmpl_type = TREE_TYPE (tmpl);
-  tree spec_type = TREE_TYPE (spec);
-
-  if (TREE_DEPRECATED (tmpl_type)
-      || TREE_DEPRECATED (spec_type)
-      || TREE_DEPRECATED (TREE_TYPE (tmpl_type))
-      || TREE_DEPRECATED (TREE_TYPE (spec_type)))
-    return;
-
-  tree tmpl_attrs[] = { DECL_ATTRIBUTES (tmpl), TYPE_ATTRIBUTES (tmpl_type) };
-  tree spec_attrs[] = { DECL_ATTRIBUTES (spec), TYPE_ATTRIBUTES (spec_type) };
-
-  if (!spec_attrs[0])
-    spec_attrs[0] = attrlist;
-  else if (!spec_attrs[1])
-    spec_attrs[1] = attrlist;
-
-  /* Avoid warning if the primary has no attributes.  */
-  if (!tmpl_attrs[0] && !tmpl_attrs[1])
-    return;
-
-  /* Avoid warning if either declaration contains an attribute on
-     the white list below.  */
-  const char* const whitelist[] = {
-    "error", "warning"
-  };
-
-  for (unsigned i = 0; i != 2; ++i)
-    for (unsigned j = 0; j != sizeof whitelist / sizeof *whitelist; ++j)
-      if (lookup_attribute (whitelist[j], tmpl_attrs[i])
-	  || lookup_attribute (whitelist[j], spec_attrs[i]))
-	return;
-
   /* Avoid warning if the difference between the primary and
      the specialization is not in one of the attributes below.  */
   const char* const blacklist[] = {
     "alloc_align", "alloc_size", "assume_aligned", "format",
-    "format_arg", "malloc", "nonnull"
+    "format_arg", "malloc", "nonnull", NULL
   };
 
   /* Put together a list of the black listed attributes that the primary
      template is declared with that the specialization is not, in case
      it's not apparent from the most recent declaration of the primary.  */
-  unsigned nattrs = 0;
   pretty_printer str;
-
-  for (unsigned i = 0; i != sizeof blacklist / sizeof *blacklist; ++i)
-    {
-      for (unsigned j = 0; j != 2; ++j)
-	{
-	  if (!lookup_attribute (blacklist[i], tmpl_attrs[j]))
-	    continue;
-
-	  for (unsigned k = 0; k != 1 + !!spec_attrs[1]; ++k)
-	    {
-	      if (lookup_attribute (blacklist[i], spec_attrs[k]))
-		break;
-
-	      if (nattrs)
-		pp_string (&str, ", ");
-	      pp_begin_quote (&str, pp_show_color (global_dc->printer));
-	      pp_string (&str, blacklist[i]);
-	      pp_end_quote (&str, pp_show_color (global_dc->printer));
-	      ++nattrs;
-	    }
-	}
-    }
+  unsigned nattrs = decls_mismatched_attributes (tmpl, spec, attrlist,
+						 blacklist, &str);
 
   if (!nattrs)
     return;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index cfe6a8e..8ffb0cd 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2548,6 +2548,40 @@ decorated with attribute @code{constructor} are invoked is unspecified.
 In mixed declarations, attribute @code{init_priority} can be used to
 impose a specific ordering.
 
+@item copy
+@itemx copy (@var{function})
+@cindex @code{copy} function attribute
+The @code{copy} attribute applies the set of attributes with which
+@var{function} has been declared to the declaration of the function
+to which the attribute is applied.  The attribute is designed for
+libraries that define aliases or function resolvers that are expected
+to specify the same set of attributes as their targets.  The @code{copy}
+attribute can be used with functions, variables, or types.  However,
+the kind of symbol to which the attribute is applied (either function
+or variable) must match the kind of symbol to which the argument refers.
+The @code{copy} attribute copies only syntaxtic and semantic attributes
+but attributes that affect a symbol's linkage or visibility such as
+@code{alias}, @code{visibility}, or @code{weak}.  The @code{deprecated}
+attribute is also not copied.  @xref{Common Type Attributes}.
+@xref{Common Variable Attributes}.
+
+For example, the @var{StrongAlias} macro below makes use of the @code{alias}
+and @code{copy} attributes to define an alias named @var{alloc} for function
+@var{allocate} declated with attributes @var{alloc_size}, @var{malloc}, and
+@var{nothrow}.  Thanks to the @code{__typeof__} operator the alias has
+the same type as the target function.  As a result of the @code{copy}
+attribute the alias also shares the same attributes as the target.
+
+@smallexample
+#define StrongAlias(TagetFunc, AliasDecl)   \
+  extern __typeof__ (TargetFunc) AliasDecl  \
+    __attribute__ ((alias (#TargetFunc), copy (TargetFunc)));
+
+extern __attribute__ ((alloc_size (1), malloc, nothrow))
+  void* allocate (size_t);
+StrongAlias (allocate, alloc);
+@end smallexample
+
 @item deprecated
 @itemx deprecated (@var{msg})
 @cindex @code{deprecated} function attribute
@@ -6133,6 +6167,23 @@ opposite---to allocate space for it directly.
 These attributes override the default chosen by the
 @option{-fno-common} and @option{-fcommon} flags respectively.
 
+@item copy
+@itemx copy (@var{variable})
+@cindex @code{copy} variable attribute
+The @code{copy} attribute applies the set of attributes with which
+@var{variable} has been declared to the declaration of the variable
+to which the attribute is applied.  The attribute is designed for
+libraries that define aliases that are expected to specify the same
+set of attributes as the aliased symbols.  The @code{copy} attribute
+can be used with variables, functions or types.  However, the kind
+of symbol to which the attribute is applied (either varible or
+function) must match the kind of symbol to which the argument refers.
+The @code{copy} attribute copies only syntaxtic and semantic attributes
+but attributes that affect a symbol's linkage or visibility such as
+@code{alias}, @code{visibility}, or @code{weak}.  The @code{deprecated}
+attribute is also not copied.  @xref{Common Function Attributes}.
+@xref{Common Type Attributes}.
+
 @item deprecated
 @itemx deprecated (@var{msg})
 @cindex @code{deprecated} variable attribute
@@ -7060,6 +7111,38 @@ struct foo
 
 This warning can be disabled by @option{-Wno-if-not-aligned}.
 
+@item copy
+@itemx copy (@var{expression})
+@cindex @code{copy} type attribute
+The @code{copy} attribute applies the set of attributes with which
+the type of the @var{expression} has been declared to the declaration
+of the type to which the attribute is applied.  The attribute is
+designed for libraries that define aliases that are expected to
+specify the same set of attributes as the aliased symbols.
+The @code{copy} attribute can be used with types, variables, or
+functions.  However, the kind of symbol to which the attribute is
+applied (either varible or function) must match the kind of symbol
+to which the argument refers.
+The @code{copy} attribute copies only syntaxtic and semantic attributes
+but attributes that affect a symbol's linkage or visibility such as
+@code{alias}, @code{visibility}, or @code{weak}.  The @code{deprecated}
+attribute is also not copied.  @xref{Common Function Attributes}.
+@xref{Common Variable Attributes}.
+
+For example, suppose @code{struct A} below is defined in some third
+partly library header to have the alignment requirement @code{N} and
+to force a warning whenever a variable of the type is not so aligned
+due to attribute @code{packed}.  Specifying the @code{copy} attribute
+on the definition on the unrelated @code{struct B} has the effect of
+copying all relevant attributes from the type referenced by the pointer
+expression to @code{struct B}.
+
+@smallexample
+struct __attribute__ ((aligned (N), warn_if_not_aligned (N)))
+A @{ /* @r{@dots{}} */ @};
+struct __attribute__ ((copy ( (struct A *)0)) B @{ /* @r{@dots{}} */ @};
+@end smallexample
+
 @item deprecated
 @itemx deprecated (@var{msg})
 @cindex @code{deprecated} type attribute
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5c95f67..647774f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -4775,8 +4775,15 @@ This warning is enabled by @option{-Wall} in C and C++.
 @opindex Wno-missing-attributes
 Warn when a declaration of a function is missing one or more attributes
 that a related function is declared with and whose absence may adversely
-affect the correctness or efficiency of generated code.  For example, in
-C++, the warning is issued when an explicit specialization of a primary
+affect the correctness or efficiency of generated code.  For example,
+the warning is issued for declarations of aliases that use attributes
+to specify less restrictive requirements than those of their targets.
+Attributesc considered include @code{alloc_align}, @code{alloc_size},
+@code{cold}, @code{const}, @code{hot}, @code{leaf}, @code{malloc},
+@code{nonnull}, @code{noreturn}, @code{nothrow}, @code{pure},
+@code{returns_nonnull}, and @code{returns_twice}.
+
+In C++, the warning is issued when an explicitcspecialization of a primary
 template declared with attribute @code{alloc_align}, @code{alloc_size},
 @code{assume_aligned}, @code{format}, @code{format_arg}, @code{malloc},
 or @code{nonnull} is declared without it.  Attributes @code{deprecated},
diff --git a/gcc/testsuite/gcc.dg/Wmissing-attributes.c b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
new file mode 100644
index 0000000..874476a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
@@ -0,0 +1,63 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define ATTR(list)   __attribute__ (list)
+
+
+int alias_no_const (void);
+
+ATTR ((const)) int
+target_const (void)             /* { dg-message ".alias_no_const. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_const"))) int
+alias_no_const (void);          /* { dg-warning ".alias_no_const. specifies less restrictive attribute than its target .target_const.: .const." } */
+
+
+ATTR ((alloc_size (1), malloc)) void*
+target_malloc (int n)           /* { dg-message ".alias_no_malloc. target declared here" } */
+{ return __builtin_malloc (n); }
+
+ATTR ((alias ("target_malloc"))) void*
+alias_no_malloc (int);          /* { dg-warning ".alias_no_malloc. specifies less restrictive attributes than its target .target_malloc.: .alloc_size., .malloc." } */
+
+
+void
+target_no_nothrow (void)        /* { dg-message ".alias_nothrow. target declared here" } */
+{ }
+
+ATTR ((alias ("target_no_nothrow"), nothrow)) void
+alias_nothrow (void);           /* { dg-warning ".alias_nothrow. specifies more restrictive attribute than its target .target_no_nothrow.: .nothrow." } */
+
+
+ATTR ((pure)) int
+alias_pure (void);
+
+int
+target_no_pure (void)           /* { dg-message ".alias_pure. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_no_pure"))) int
+alias_pure (void);              /* { dg-warning ".alias_pure. specifies more restrictive attribute than its target .target_no_pure.: .pure." } */
+
+
+ATTR ((const)) int
+alias_const (void);
+
+int
+target_pure (void)              /* { dg-message ".alias_const. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_pure"))) int
+alias_const (void);             /* { dg-warning ".alias_const. specifies more restrictive attribute than its target .target_pure.: .const." } */
+
+
+/* Verify that __typeof__ doesn't include attributes.  */
+
+ATTR ((cold)) int
+target_cold (void)
+{ return 0; }
+
+__typeof__ (target_cold) ATTR ((alias ("target_cold")))
+alias_cold;                   /* { dg-warning ".alias_cold. specifies less restrictive attribute than its target .target_cold.: .cold." } */
diff --git a/gcc/testsuite/gcc.dg/attr-copy-2.c b/gcc/testsuite/gcc.dg/attr-copy-2.c
new file mode 100644
index 0000000..1ac156e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy-2.c
@@ -0,0 +1,149 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise attribute copy for functions.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define Assert(expr)   typedef char AssertExpr[2 * !!(expr) - 1]
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Verify that referencing a symbol with no attributes is accepted
+   with no diagnostics.  */
+
+void ref0 (void);
+
+ATTR ((copy (ref0))) void
+f0 (void);
+
+/* Verify that referencing a symbol using the address-of and dereferencing
+   operators is also accepted with no diagnostics.  */
+
+ATTR ((copy (&ref0))) void f1 (void);
+ATTR ((copy (*ref0))) void f2 (void);
+
+/* Verify that referencing a symbol of a different kind than that
+   of the one the attribute is applied to is diagnosed.  */
+
+int v0;                       /* { dg-message "symbol .v0. referenced by .f3. declared here" } */
+
+ATTR ((copy (v0))) void
+f3 (void);                    /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+void f4 (void);              /* { dg-message "symbol .f4. referenced by .v1. declared here" } */
+
+ATTR ((copy (f4))) int
+v1;                           /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+
+ATTR ((copy (v0 + 1)))
+void f5 (void);               /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+void f6 (void);
+
+ATTR ((copy (f6 - 1)))
+int v1;                       /* { dg-warning ".copy. attribute ignored on a declaration of a different kind than referenced symbol" } */
+
+
+
+/* Verify that circular references of the copy function attribute
+   are handled gracefully (i.e., not by getting into an infinite
+   recursion) by issuing a diagnostic.  */
+
+void xref1 (void);
+ATTR ((copy (xref1))) void
+xref1 (void);                 /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1))) void
+xref1 (void);                 /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1), copy (xref1))) void
+xref1 (void);                 /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+
+
+/* Use attribute noreturn to verify that circular references propagate
+   atttibutes as expected, and unlike in the self-referential instances
+   above, without a warning.  Also use the address-of operator to make
+   sure it doesn't change anything.  */
+
+ATTR ((noreturn))      void xref2 (void);
+ATTR ((copy (xref2)))  void xref3 (void);
+ATTR ((copy (&xref3))) void xref4 (void);
+ATTR ((copy (xref4)))  void xref5 (void);
+ATTR ((copy (&xref5))) void xref6 (void);
+ATTR ((copy (xref6)))  void xref7 (void);
+ATTR ((copy (&xref7))) void xref8 (void);
+ATTR ((copy (xref8)))  void xref9 (void);
+ATTR ((copy (&xref9))) void xref2 (void);
+
+int call_ref2 (void) { xref2 (); }
+int call_ref3 (void) { xref3 (); }
+int call_ref4 (void) { xref4 (); }
+int call_ref5 (void) { xref5 (); }
+int call_ref6 (void) { xref6 (); }
+int call_ref7 (void) { xref7 (); }
+int call_ref8 (void) { xref8 (); }
+int call_ref9 (void) { xref9 (); }
+
+
+/* Verify that copying attributes from multiple symbols into one works
+   as expected.  */
+
+ATTR ((malloc)) void*
+xref10 (void);
+
+ATTR ((alloc_size (1)))
+void* xref11 (int);
+
+ATTR ((copy (xref10), copy (xref11)))
+void* xref12 (int);
+
+void* call_xref12 (void)
+{
+  void *p = xref12 (3);
+  __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0));   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+  return p;
+}
+
+
+/* Verify that attribute exclusions apply.  */
+
+ATTR ((const)) int
+fconst (void);
+
+ATTR ((pure)) int
+fpure (void);
+
+ATTR ((copy (fconst), copy (fpure))) int
+fconst_pure (void);           /* { dg-warning "ignoring attribute .pure. because it conflicts with attribute .const." } */
+
+
+/* Verify that attribute deprecated isn't copied (but referencing
+   the deprecated declaration still triggers a warning).  */
+
+ATTR ((deprecated)) void fdeprecated (void);
+
+ATTR ((copy (fdeprecated))) int fcurrent (void);  /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
+ATTR ((copy (fcurrent))) int
+fcurrent2 (void);
+
+int call_fcurrent (void) { return fcurrent () + fcurrent2 (); }
+
+
+/* Verify that attributes are copied on a declaration using __typeof__
+   and that -Wmissing-attributes is not issued.  */
+
+ATTR ((cold)) int
+target_cold (void)
+{ return 0; }
+
+__typeof__ (target_cold) ATTR ((copy (target_cold), alias ("target_cold")))
+alias_cold;                   /* { dg-bogus "\\\[-Wmissing-attributes]." } */
+
+
+/* Verify that attribute alias is not copied.  This also indirectly
+   verifies that attribute copy itself isn't copied.  */
+
+ATTR ((noreturn)) void fnoret (void) { __builtin_abort (); }
+ATTR ((alias ("fnoret"), copy (fnoret))) void fnoret_alias (void);
+ATTR ((copy (fnoret_alias))) void fnoret2 (void) { __builtin_exit (1); }
+
+/* Expect no warning below.  */
+int call_noret (void) { fnoret2 (); }
diff --git a/gcc/testsuite/gcc.dg/attr-copy-3.c b/gcc/testsuite/gcc.dg/attr-copy-3.c
new file mode 100644
index 0000000..88e5e5e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy-3.c
@@ -0,0 +1,75 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise attribute copy for variables.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Verify that referencing a symbol with no attributes is accepted
+   with no diagnostics.  */
+
+int ref0;
+
+ATTR ((copy (ref0))) long
+var0;
+
+/* Verify that referencing a symbol using the address-of and dereferencing
+   operators is also accepted with no diagnostics.  */
+
+ATTR ((copy (&ref0))) void* ptr0;
+ATTR ((copy (*&ref0))) int arr[1];
+
+/* Verify that referencing a symbol of a different kind than that
+   of the one the attribute is applied to is diagnosed.  */
+
+int ref1;                     /* { dg-message "previous declaration here" } */
+
+ATTR ((copy (ref1))) int
+ref1;                         /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol " } */
+
+
+/* Verify that circular references of the copy variable attribute
+   are handled gracefully (i.e., not by getting into an infinite
+   recursion) by issuing a diagnostic.  */
+
+char xref1;
+ATTR ((copy (xref1))) char
+xref1;                        /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1))) char
+xref1;                        /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+ATTR ((copy (xref1), copy (xref1))) char
+xref1;                        /* { dg-warning ".copy. attribute ignored on a redeclaration of the referenced symbol" } */
+
+
+/* Use attribute unused to verify that circular references propagate
+   atttibutes as expected (expect no warnings the circular reference
+   or for any of the unused symbols).  Also use the address-of operator
+   to make sure it doesn't change anything.  */
+
+static ATTR ((unused))        int xref2;
+static ATTR ((copy (xref2)))  int xref3;
+static ATTR ((copy (&xref3))) int xref4;
+static ATTR ((copy (xref4)))  int xref5;
+static ATTR ((copy (&xref5))) int xref6;
+static ATTR ((copy (xref6)))  int xref7;
+static ATTR ((copy (&xref7))) int xref8;
+static ATTR ((copy (xref8)))  int xref9;
+static ATTR ((copy (&xref9))) int xref2;
+
+/* Verify that attribute exclusions apply.  */
+
+ATTR ((common)) int common_var;
+ATTR ((nocommon)) double nocommon_var;
+
+ATTR ((copy (common_var), copy (nocommon_var))) long
+common_copy;                  /* { dg-warning "ignoring attribute .nocommon. because it conflicts with attribute .common." } */
+
+
+/* Verify that attribute deprecated isn't copied.  */
+
+ATTR ((deprecated)) char deprecated_var;
+
+ATTR ((copy (deprecated_var))) int current_var;  /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
+ATTR ((copy (current_var))) int current_var_2;
+
+int return_current_vars (void) { return current_var + current_var_2; }
diff --git a/gcc/testsuite/gcc.dg/attr-copy-4.c b/gcc/testsuite/gcc.dg/attr-copy-4.c
new file mode 100644
index 0000000..7020bad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy-4.c
@@ -0,0 +1,61 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise attribute copy for types.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define Assert(expr)   typedef char AssertExpr[2 * !!(expr) - 1]
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Use attribute packed to verify that type attributes are copied
+   from one type to another.  */
+
+struct ATTR ((packed)) PackedA { int i; char c; };
+
+Assert (__alignof (struct PackedA) == 1);
+
+struct ATTR ((copy ((struct PackedA*)0))) PackedB { long i; char c; };
+
+Assert (__alignof (struct PackedA) == __alignof (struct PackedB));
+
+struct PackedMember
+{
+  char c;
+  ATTR ((copy ((struct PackedB*)0))) double packed_mem;
+};
+
+Assert (__alignof (struct PackedMember) == 1);
+
+
+extern const struct PackedA packed;
+
+struct Unpacked { int i; char c; };
+Assert (__alignof (struct Unpacked) > 1);
+
+/* Verify that copying the packed attribute to the declaration
+   of an object is ignored with a warning.  (There should be
+   a way to copy just the subset of attributes from a type that
+   aren't ignored and won't cause a warning, maybe via attribute
+   copy_except or something like that.)  */
+extern ATTR ((copy ((struct PackedA*)0))) const struct Unpacked
+  unpacked;                   /* { dg-warning ".packed. attribute ignored" } */
+
+Assert (__alignof (packed) == 1);
+Assert (__alignof (unpacked) == __alignof (struct Unpacked));
+
+
+
+/* Verify that attribute deprecated isn't copied (but referencing
+   the deprecated type in the copy attribute still triggers a warning).  */
+
+struct ATTR ((aligned (8), deprecated))
+AlignedDeprecated { char c; };
+
+struct ATTR ((copy ((struct AlignedDeprecated *)0)))        /* { dg-warning "\\\[-Wdeprecated-declarations]" } */
+AlignedCopy { short s; };
+
+Assert (__alignof (struct AlignedCopy) == 8);
+
+struct AlignedCopy aligned_copy;
+
+Assert (__alignof (aligned_copy) == 8);
diff --git a/gcc/testsuite/gcc.dg/attr-copy.c b/gcc/testsuite/gcc.dg/attr-copy.c
new file mode 100644
index 0000000..27cd7bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy.c
@@ -0,0 +1,33 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   Exercise error handling for attribute copy.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define ATTR(list)   __attribute__ (list)
+
+/* Verify incorrect numbers of arguments.  */
+ATTR ((copy)) void
+fno_args (void);              /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
+
+ATTR ((copy ())) void
+fno_args2 (void);             /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
+
+ATTR ((copy (fno_args, fno_args))) void
+fmlti_args (void);            /* { dg-error "wrong number of arguments specified for .copy. attribute" } */
+
+/* Verify that referencing an undeclared symbol is rejected with an error.  */
+
+ATTR ((copy (foobar)))        /* { dg-error ".foobar. undeclared" } */
+void fundeclared (void);
+
+/* Verify that using a string argument triggers a descriptive error
+   (given attributes like alias and weakref using a string is a likely
+   mistake).  */
+
+ATTR ((copy ("foobar")))
+void fstring (void);          /* { dg-error ".copy. attribute argument cannot be a string" } */
+
+/* Ditto for an integer.  */
+
+ATTR ((copy (123)))
+void fnumber (void);          /* { dg-error ".copy. attribute argument cannot be a constant arithmetic expression" } */
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 3a8cc2b..1a01d13 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -1089,16 +1089,26 @@ extern int gomp_test_nest_lock_25 (omp_nest_lock_25_t *) __GOMP_NOTHROW;
 # define attribute_hidden
 #endif
 
+#if __GNUC__ >= 9
+#  define HAVE_ATTRIBUTE_COPY
+#endif
+
+#ifdef HAVE_ATTRIBUTE_COPY
+# define attribute_copy(arg) __attribute__ ((copy (arg)))
+#else
+# define attribute_copy(arg)
+#endif
+
 #ifdef HAVE_ATTRIBUTE_ALIAS
 # define strong_alias(fn, al) \
-  extern __typeof (fn) al __attribute__ ((alias (#fn)));
+  extern __typeof (fn) al __attribute__ ((alias (#fn))) attribute_copy (fn);
 
 # define ialias_ulp	ialias_str1(__USER_LABEL_PREFIX__)
 # define ialias_str1(x)	ialias_str2(x)
 # define ialias_str2(x)	#x
 # define ialias(fn) \
   extern __typeof (fn) gomp_ialias_##fn \
-    __attribute__ ((alias (#fn))) attribute_hidden;
+    __attribute__ ((alias (#fn))) attribute_hidden attribute_copy (fn);
 # define ialias_redirect(fn) \
   extern __typeof (fn) fn __asm__ (ialias_ulp "gomp_ialias_" #fn) attribute_hidden;
 # define ialias_call(fn) gomp_ialias_ ## fn

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]