diff mbox series

detect attribute mismatches in alias declarations (PR 81824)

Message ID 42f8ec31-b967-08e7-ff7a-67a63a1a2a64@gmail.com
State New
Headers show
Series detect attribute mismatches in alias declarations (PR 81824) | expand

Commit Message

Martin Sebor Oct. 1, 2018, 8:59 p.m. UTC
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.

Comments

Joseph Myers Oct. 1, 2018, 11 p.m. UTC | #1
On Mon, 1 Oct 2018, Martin Sebor wrote:

> Testing the patch with Glibc triggers thousands of warnings of
> both kinds.  After reviewing a small subset it became apparent

Thousands of warnings suggests initially having the warning outside -Wall 
(though one might hope to move it into -Wall at some point, depending on 
how hard the warnings are to address and to what extent they appear at all 
for other packages - most don't make heavy use of aliases like that - or 
failing that, to enable it explicitly for glibc once all the warnings are 
fixed, since this is certainly a useful warning for glibc showing issues 
we want to fix) - it's not like the typical case of a new warning where 
you can quickly and easily fix all the instances in glibc, for all 
architectures, to keep it building with mainline GCC.

> 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.

(For actual use in glibc that use would of course need to be conditional 
on a GCC version supporting the attribute.)

> 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).

I'm not sure we should care about the attributes on IFUNC resolvers at 
all; no normal code will see a declaration of the resolver and also call 
the function, whereas lots of code calls functions under internal alias 
names that currently lack the same attributes as the public declaration 
has.  It's also not obvious whether there might be more cases of 
attributes for a function that are inapplicable to IFUNC resolvers than 
just the attributes relating to a symbol rather than the function itself 
which are hardcoded as excluded.
Martin Sebor Oct. 23, 2018, 3:26 a.m. UTC | #2
On 10/01/2018 05:00 PM, Joseph Myers wrote:
> On Mon, 1 Oct 2018, Martin Sebor wrote:
>
>> Testing the patch with Glibc triggers thousands of warnings of
>> both kinds.  After reviewing a small subset it became apparent
>
> Thousands of warnings suggests initially having the warning outside -Wall
> (though one might hope to move it into -Wall at some point, depending on
> how hard the warnings are to address and to what extent they appear at all
> for other packages - most don't make heavy use of aliases like that - or
> failing that, to enable it explicitly for glibc once all the warnings are
> fixed, since this is certainly a useful warning for glibc showing issues
> we want to fix) - it's not like the typical case of a new warning where
> you can quickly and easily fix all the instances in glibc, for all
> architectures, to keep it building with mainline GCC.

I have improved the Glibc patch to avoid many of the warnings.
To avoid most of the rest I have adjusted the GCC patch to make
-Wattribute-alias a two-level warning, and to disregard mismatches
between aliases and ifunc resolvers.  With -Wattribute-alias=1
that reduced the number of unique instances of the warnings for
a Glibc build to just 27.  Of those, all but one of
the -Wattributes instances are of the form:

   warning: ‘leaf’ attribute has no effect on unit local functions

All the -Wmissing-attributes instances are due to a missing
nonnull attribute on the __EI__ kinds of functions, like:

   warning: ‘__EI_vfprintf’ specifies less restrictive attribute than 
its target ‘vfprintf’: ‘nonnull’

I think both sets might be caused either by a bug/missing handling
in my Glibc patch, or by a bug in the GCC patch, but I'm a little
lost in the maze of Glibc ifunc macro to tell which just yet.
I can keep looking into it but it would make it easier if you
could apply it and see if the Glibc macros need tweaking.

Enabling -Wattribute-alias=2 shows the remaining attribute
mismatches (those highlighting "potential bugs," although
I suspect few, if any, are real bugs; more than likely they
are benign.)

>
>> 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.
>
> (For actual use in glibc that use would of course need to be conditional
> on a GCC version supporting the attribute.)

Sure.  The patch is just to show what I did to get the warnings.
(Attached is an update with the changes I mentioned above and
the Glibc warning breakdown with it.)

>
>> 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).
>
> I'm not sure we should care about the attributes on IFUNC resolvers at
> all; no normal code will see a declaration of the resolver and also call
> the function, whereas lots of code calls functions under internal alias
> names that currently lack the same attributes as the public declaration
> has.  It's also not obvious whether there might be more cases of
> attributes for a function that are inapplicable to IFUNC resolvers than
> just the attributes relating to a symbol rather than the function itself
> which are hardcoded as excluded.

I agree that checking attributes on ifunc resolvers probably doesn't
make much sense.  It would be helpful to check their targets against
the aliases, but that's a whole other project.

Attached is the latest update along with the Glibc warning breakdown.
Tested on x86_64-linux.

Martin
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.
	(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.
	(maybe_diag_incompatible_alias): Use OPT_Wattribute_alias_.
	* common.opt (-Wattribute-alias): Take an argument.
	(-Wno-attribute-alias): New option.
	* doc/extend.texi (Common Function Attributes): Document copy.
	(Common Variable Attributes): Same.
	* doc/invoke.texi (-Wmissing-attributes): Document enhancement.
	(-Wattribute-alias): Document new option argument.

libgomp/ChangeLog:

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

gcc/testsuite/ChangeLog:

	PR middle-end/81824
	* gcc.dg/Wattribute-alias.c: New test.
	* 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..03a7d49 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,188 @@ 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)
+{
+  /* Do not expect attributes to match between aliases and ifunc
+     resolvers.  There is no obvious correspondence between them.  */
+  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (alias)))
+    return;
+
+  const char* const blacklist[] = {
+    "alloc_align", "alloc_size", "cold", "const", "hot", "leaf", "malloc",
+    "nonnull", "noreturn", "nothrow", "pure", "returns_nonnull",
+    "returns_twice", NULL
+  };
+
+  pretty_printer attrnames;
+  if (warn_attribute_alias > 1)
+    {
+      /* With -Wattribute-alias=2 detect alias declarations that are more
+	 restrictive than their targets first.  Those indicate potential
+	 codegen bugs.  */
+      if (unsigned n = decls_mismatched_attributes (alias, target, NULL_TREE,
+						    blacklist, &attrnames))
+	{
+	  auto_diagnostic_group d;
+	  if (warning_n (DECL_SOURCE_LOCATION (alias),
+			 OPT_Wattribute_alias_, 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 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..f8da813 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1360,7 +1360,7 @@ maybe_diag_incompatible_alias (tree alias, tree target)
 
 	  auto_diagnostic_group d;
 	  if (warning_at (DECL_SOURCE_LOCATION (target),
-			  OPT_Wattribute_alias,
+			  OPT_Wattribute_alias_,
 			  "%<ifunc%> resolver for %qD should return %qT",
 			  alias, funcptr))
 	    inform (DECL_SOURCE_LOCATION (alias),
@@ -1370,11 +1370,11 @@ maybe_diag_incompatible_alias (tree alias, tree target)
 	{
 	  auto_diagnostic_group d;
 	  if (warning_at (DECL_SOURCE_LOCATION (alias),
-			    OPT_Wattribute_alias,
+			    OPT_Wattribute_alias_,
 			    "%qD alias between functions of incompatible "
 			    "types %qT and %qT", alias, altype, targtype))
 	    inform (DECL_SOURCE_LOCATION (target),
-		      "aliased declaration here");
+		    "aliased declaration here");
 	}
     }
 }
@@ -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/common.opt b/gcc/common.opt
index 53aac19..6382e75 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -550,9 +550,13 @@ Wattributes
 Common Var(warn_attributes) Init(1) Warning
 Warn about inappropriate attribute usage.
 
-Wattribute-alias
-Common Var(warn_attributes) Init(1) Warning
-Warn about type safety and similar errors in attribute alias and related.
+Wattribute-alias=
+Common Joined RejectNegative UInteger Var(warn_attribute_alias) Init(1) Warning IntegerRange(0, 2)
+Warn about type safety and similar errors and mismatches in attribute alias and related.
+
+Wno-attribute-alias
+Common Alias(Wattribute_alias=, 0, 0) Warning
+Disable -Wattribute-alias.
 
 Wcannot-profile
 Common Var(warn_cannot_profile) Init(1) Warning
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..c027acd 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -282,7 +282,8 @@ Objective-C and Objective-C++ Dialects}.
 -Walloc-zero  -Walloc-size-larger-than=@var{byte-size}
 -Walloca  -Walloca-larger-than=@var{byte-size} @gol
 -Wno-aggressive-loop-optimizations  -Warray-bounds  -Warray-bounds=@var{n} @gol
--Wno-attributes  -Wbool-compare  -Wbool-operation @gol
+-Wno-attributes -Wno-attribute-alias @gol
+-Wbool-compare  -Wbool-operation @gol
 -Wno-builtin-declaration-mismatch @gol
 -Wno-builtin-macro-redefined  -Wc90-c99-compat  -Wc99-c11-compat @gol
 -Wc++-compat  -Wc++11-compat  -Wc++14-compat  -Wc++17-compat  @gol
@@ -314,7 +315,7 @@ Objective-C and Objective-C++ Dialects}.
 -Winvalid-pch  -Wlarger-than=@var{byte-size} @gol
 -Wlogical-op  -Wlogical-not-parentheses  -Wlong-long @gol
 -Wmain  -Wmaybe-uninitialized  -Wmemset-elt-size  -Wmemset-transposed-args @gol
--Wmisleading-indentation  -Wmissing-attributes -Wmissing-braces @gol
+-Wmisleading-indentation  -Wno-missing-attributes -Wmissing-braces @gol
 -Wmissing-field-initializers  -Wmissing-include-dirs  -Wmissing-profile @gol
 -Wno-multichar  -Wmultistatement-macros  -Wnonnull  -Wnonnull-compare @gol
 -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
@@ -4770,13 +4771,24 @@ about the layout of the file that the directive references.
 
 This warning is enabled by @option{-Wall} in C and C++.
 
-@item -Wmissing-attributes
+@item -Wno-missing-attributes
 @opindex Wmissing-attributes
 @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.
+This typically represents a potential optimization oportunity rather
+than a hidden bug.  The @option{-Wattribute-alias} option controls warnings
+issued for mismatches between declarations of aliases and their targets
+that might be indicative of code generation bugs.
+Attributes 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},
@@ -5786,10 +5798,28 @@ pointers. This warning level may give a larger number of
 false positives and is deactivated by default.
 @end table
 
-@item -Wattribute-alias
+@item -Wattribute-alias=@var{n}
+@itemx -Wno-attribute-alias
+@opindex -Wattribute-alias
+@opindex -Wno-attribute-alias
 Warn about declarations using the @code{alias} and similar attributes whose
-target is incompatible with the type of the alias.  @xref{Function Attributes,
-,Declaring Attributes of Functions}.
+target is incompatible with the type of the alias.
+@xref{Function Attributes,,Declaring Attributes of Functions}.
+The @option{-Wattribute-alias=1} is  enabled by @option{-Wall}.
+
+@table @gcctabopt
+@item -Wattribute-alias=1
+The default warning level of the @option{-Wattribute-alias} option diagnoses
+incompatibilities between the type of the alias declaration and that of its
+target.  Such incompatibilities are typically indicative of bugs.
+
+@item -Wattribute-alias=2
+At this level @option{-Wattribute-alias} also diagnoses mismatches between
+the set of attributes of the alias declaration and the attributes applied
+to its target.  Although in some cases such mismatches may indicate bugs,
+in other cases they may be benign and could be resolved simply by adding
+the missing attribute to the target.
+@end table
 
 @item -Wbool-compare
 @opindex Wno-bool-compare
diff --git a/gcc/testsuite/gcc.dg/Wattribute-alias.c b/gcc/testsuite/gcc.dg/Wattribute-alias.c
new file mode 100644
index 0000000..175e40b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wattribute-alias.c
@@ -0,0 +1,49 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   { dg-do compile }
+   { dg-options "-Wall -Wattribute-alias=2" } */
+
+#define ATTR(...)   __attribute__ ((__VA_ARGS__))
+
+
+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." } */
+
+
+/* There is no obvious relationship between the attributes on an ifunc
+   resolver and those on its aliases.  Verify that mismatches between
+   aliases and ifunc resolvers do not trigger warnings.  */
+
+typedef int F (void);
+
+ATTR (pure, leaf) F* resolve_to_const (void)
+{ return alias_const; }
+
+ATTR (ifunc ("resolve_to_const")) F alias_no_const_ifunc;
+ATTR (const, ifunc ("resolve_to_const")) F alias_const_ifunc;
+ATTR (ifunc ("resolve_to_const")) int alias_no_leaf_ifunc (void);
diff --git a/gcc/testsuite/gcc.dg/Wmissing-attributes.c b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
new file mode 100644
index 0000000..2a98182
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
@@ -0,0 +1,95 @@
+/* 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." } */
+
+
+ATTR ((leaf)) int
+target_leaf (void)              /* { dg-message ".alias_no_leaf. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_leaf"))) int
+alias_no_leaf (void);           /* { dg-warning ".alias_no_leaf. specifies less restrictive attribute than its target .target_leaf.: .leaf." } */
+
+
+/* Verify that attributes noclone, noinline, and noipa on the target
+   don't cause a warning for aliases without the attribute.  */
+
+ATTR ((noclone)) int
+target_noclone (void)
+{ return 0; }
+
+ATTR ((alias ("target_noclone"))) int
+alias_no_noclone (void);
+
+
+ATTR ((noipa)) int
+target_noipa (void)
+{ return 0; }
+
+ATTR ((alias ("target_noipa"))) int
+alias_no_noipa (void);
+
+
+ATTR ((noinline)) int
+target_noinline (void)
+{ return 0; }
+
+ATTR ((alias ("target_noinline"))) int
+alias_no_noinline (void);
+
+
+ATTR ((nothrow)) int
+target_nothrow (void)           /* { dg-message ".alias_no_nothrow. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_nothrow"))) int
+alias_no_nothrow (void);        /* { dg-warning ".alias_no_nothrow. specifies less restrictive attribute than its target .target_nothrow.: .nothrow." } */
+
+
+/* Verify that attribute weak on the target doesn't cause and isn't
+   mentioned in a warning for aliases without the attribute.  */
+
+ATTR ((weak)) int
+target_weak (void)
+{ return 0; }
+
+ATTR ((alias ("target_weak"))) int
+alias_no_weak (void);
+
+
+ATTR ((nothrow, weak)) int
+target_nothrow_weak (void)      /* { dg-message ".alias_nothrow_no_weak. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_nothrow_weak"))) int
+alias_nothrow_no_weak (void);        /* { dg-warning ".alias_nothrow_no_weak. specifies less restrictive attribute than its target .target_nothrow_weak.: .nothrow." } */
+
+
+/* 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
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index 8b9273c..cb50192 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -132,7 +132,7 @@
 /* Define ALIASNAME as a strong alias for NAME.  */
 # define strong_alias(name, aliasname) _strong_alias(name, aliasname)
 # define _strong_alias(name, aliasname) \
-  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
+  extern __typeof (name) aliasname __attribute__ ((alias (#name), copy (name)));
 
 /* This comes between the return type and function name in
    a function definition to make that definition weak.  */
@@ -143,14 +143,14 @@
    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) \
   _weak_hidden_alias (name, aliasname)
 # define _weak_hidden_alias(name, aliasname) \
   extern __typeof (name) aliasname \
-    __attribute__ ((weak, alias (#name), __visibility__ ("hidden")));
+    __attribute__ ((weak, alias (#name), __visibility__ ("hidden"), copy (name)));
 
 /* Declare SYMBOL as weak undefined symbol (resolved to 0 if not defined).  */
 # define weak_extern(symbol) _weak_extern (weak symbol)
@@ -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);
Diagnostic                        Count   Unique    Files
-Wmissing-attributes                 55        8        8
-Wattributes                         34       19       19

-Wattributes Instances:
  ../sysdeps/x86_64/multiarch/memchr.c:29
  ../sysdeps/x86_64/multiarch/memcmp.c:29
  ../sysdeps/x86_64/multiarch/memcpy_chk.c:29
  ../sysdeps/x86_64/multiarch/memmove_chk.c:29
  ../sysdeps/x86_64/multiarch/mempcpy_chk.c:29
  ../sysdeps/x86_64/multiarch/memset.c:29
  ../sysdeps/x86_64/multiarch/memset_chk.c:29
  ../sysdeps/x86_64/multiarch/strcat.c:29
  ../sysdeps/x86_64/multiarch/strchr.c:49
  ../sysdeps/x86_64/multiarch/strcmp.c:53
  ../sysdeps/x86_64/multiarch/strcpy.c:29
  ../sysdeps/x86_64/multiarch/strcspn.c:29
  ../sysdeps/x86_64/multiarch/strlen.c:29
  ../sysdeps/x86_64/multiarch/strncat.c:29
  ../sysdeps/x86_64/multiarch/strncmp.c:54
  ../sysdeps/x86_64/multiarch/strncpy.c:29
  ../sysdeps/x86_64/multiarch/strpbrk.c:29
  ../sysdeps/x86_64/multiarch/strrchr.c:28
  ../sysdeps/x86_64/multiarch/strspn.c:29

-Wmissing-attributes Instances:
  ./../include/libc-symbols.h:534
  iofputs_u.c:42
  putc_u.c:29
  ../sysdeps/x86_64/multiarch/memchr.c:30
  ../sysdeps/x86_64/multiarch/memcmp.c:31
  ../sysdeps/x86_64/multiarch/strchr.c:50
  ../sysdeps/x86_64/multiarch/strncat.c:30
  ../sysdeps/x86_64/multiarch/strrchr.c:29
Joseph Myers Oct. 23, 2018, 9:53 p.m. UTC | #3
On Mon, 22 Oct 2018, Martin Sebor wrote:

> between aliases and ifunc resolvers.  With -Wattribute-alias=1
> that reduced the number of unique instances of the warnings for
> a Glibc build to just 27.  Of those, all but one of
> the -Wattributes instances are of the form:
> 
>   warning: ‘leaf’ attribute has no effect on unit local functions

What do the macro expansions look like there?  All the places where you're 
adding "copy" attributes are for extern declarations, not static ones, 
whereas your list of warnings seems to indicate this is appearing for 
ifunc resolvers (which are static, but should not be copying attributes 
from anywhere).

> All the -Wmissing-attributes instances are due to a missing
> nonnull attribute on the __EI__ kinds of functions, like:
> 
>   warning: ‘__EI_vfprintf’ specifies less restrictive attribute than its
> target ‘vfprintf’: ‘nonnull’

That looks like a bug in the GCC patch to me; you appear to be adding copy 
attributes in the correct place.  Note that __EI_* gets declared twice 
(first with __asm__, second with an alias attribute), so anything related 
to handling of such duplicate declarations might be a cause for such a 
bug (and an indication of what you need to add a test for when fixing such 
a bug).
Martin Sebor Oct. 24, 2018, 1:50 a.m. UTC | #4
On 10/23/2018 03:53 PM, Joseph Myers wrote:
> On Mon, 22 Oct 2018, Martin Sebor wrote:
>
>> between aliases and ifunc resolvers.  With -Wattribute-alias=1
>> that reduced the number of unique instances of the warnings for
>> a Glibc build to just 27.  Of those, all but one of
>> the -Wattributes instances are of the form:
>>
>>   warning: ‘leaf’ attribute has no effect on unit local functions
>
> What do the macro expansions look like there?  All the places where you're
> adding "copy" attributes are for extern declarations, not static ones,
> whereas your list of warnings seems to indicate this is appearing for
> ifunc resolvers (which are static, but should not be copying attributes
> from anywhere).

These must have been caused by the bug in the patch (below).
They have cleared up with it fixed.  I'm down to just 18
instances of a -Wmissing-attributes warning, all for string
functions.  The cause of those is described below.

>
>> All the -Wmissing-attributes instances are due to a missing
>> nonnull attribute on the __EI__ kinds of functions, like:
>>
>>   warning: ‘__EI_vfprintf’ specifies less restrictive attribute than its
>> target ‘vfprintf’: ‘nonnull’
>
> That looks like a bug in the GCC patch to me; you appear to be adding copy
> attributes in the correct place.  Note that __EI_* gets declared twice
> (first with __asm__, second with an alias attribute), so anything related
> to handling of such duplicate declarations might be a cause for such a
> bug (and an indication of what you need to add a test for when fixing such
> a bug).

There was a bug in the patch, but there is also an issue in Glibc
that made it tricky to see the problem.

The tests I had in place were too simple to catch the GCC bug:
the problem there was that when the decl didn't have an attribute
the type of the "template" did the check would fail without also
considering the decl's type.  Tricky stuff!  I've added tests to
exercise this.

The Glibc issue has to do with the use of __hidden_ver1 macro
to declare string functions.  sysdeps/x86_64/multiarch/strcmp.c
for instance has:

   __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
     __attribute__ ((visibility ("hidden")));

and __redirect_strcmp is missing the nonnull attribute because
it's #undefined in include/sys/cdefs.h.  An example of one of
these warnings is attached.

Using strcmp instead of __redirect_strcmp would solve this but
__redirect_strcmp should have all the same attributes as strcmp.
But nonnull is removed from the declaration because the __nonnull
macro that controls it is undefined in include/sys/cdefs.h.  There
is a comment above the #undef in the header that reads:

/* The compiler will optimize based on the knowledge the parameter is
    not NULL.  This will omit tests.  A robust implementation cannot allow
    this so when compiling glibc itself we ignore this attribute.  */
# undef __nonnull
# define __nonnull(params)

I don't think this is actually true for recent versions of GCC.
The nonnull optimization is controlled by
-fisolate-erroneous-paths-attribute and according to the manual
and common.opt the option is disabled by default.

But if you do want to avoid the attribute on declarations of
these functions regardless it should be safe to add it after
the declaration in the .c file, like so:

__hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
   __attribute__ ((visibility ("hidden"), copy (strcmp)));

That should make it straightforward to adopt the enhancement
and experiment with -Wattribute-alias=2 to see if it does what
you had  in mind.

The latest GCC patch with the fix mentioned above is attached.

Martin
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.
	(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.
	(maybe_diag_incompatible_alias): Use OPT_Wattribute_alias_.
	* common.opt (-Wattribute-alias): Take an argument.
	(-Wno-attribute-alias): New option.
	* doc/extend.texi (Common Function Attributes): Document copy.
	(Common Variable Attributes): Same.
	* doc/invoke.texi (-Wmissing-attributes): Document enhancement.
	(-Wattribute-alias): Document new option argument.

libgomp/ChangeLog:

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

gcc/testsuite/ChangeLog:

	PR middle-end/81824
	* gcc.dg/Wattribute-alias.c: New test.
	* 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..dfe13ad 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,192 @@ 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;
+
+	  unsigned kmax = 1 + !!decl_attrs[1];
+	  for (unsigned k = 0; k != kmax; ++k)
+	    {
+	      if (has_attribute (decls[k], decl_attrs[k], blacklist[i]))
+		break;
+
+	      if (!k && kmax > 1)
+		continue;
+
+	      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)
+{
+  /* Do not expect attributes to match between aliases and ifunc
+     resolvers.  There is no obvious correspondence between them.  */
+  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (alias)))
+    return;
+
+  const char* const blacklist[] = {
+    "alloc_align", "alloc_size", "cold", "const", "hot", "leaf", "malloc",
+    "nonnull", "noreturn", "nothrow", "pure", "returns_nonnull",
+    "returns_twice", NULL
+  };
+
+  pretty_printer attrnames;
+  if (warn_attribute_alias > 1)
+    {
+      /* With -Wattribute-alias=2 detect alias declarations that are more
+	 restrictive than their targets first.  Those indicate potential
+	 codegen bugs.  */
+      if (unsigned n = decls_mismatched_attributes (alias, target, NULL_TREE,
+						    blacklist, &attrnames))
+	{
+	  auto_diagnostic_group d;
+	  if (warning_n (DECL_SOURCE_LOCATION (alias),
+			 OPT_Wattribute_alias_, 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 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 *NODE.  */
+  for (tree at = attrs; at; at = TREE_CHAIN (at))
+    decl_attributes (node, 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..f8da813 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1360,7 +1360,7 @@ maybe_diag_incompatible_alias (tree alias, tree target)
 
 	  auto_diagnostic_group d;
 	  if (warning_at (DECL_SOURCE_LOCATION (target),
-			  OPT_Wattribute_alias,
+			  OPT_Wattribute_alias_,
 			  "%<ifunc%> resolver for %qD should return %qT",
 			  alias, funcptr))
 	    inform (DECL_SOURCE_LOCATION (alias),
@@ -1370,11 +1370,11 @@ maybe_diag_incompatible_alias (tree alias, tree target)
 	{
 	  auto_diagnostic_group d;
 	  if (warning_at (DECL_SOURCE_LOCATION (alias),
-			    OPT_Wattribute_alias,
+			    OPT_Wattribute_alias_,
 			    "%qD alias between functions of incompatible "
 			    "types %qT and %qT", alias, altype, targtype))
 	    inform (DECL_SOURCE_LOCATION (target),
-		      "aliased declaration here");
+		    "aliased declaration here");
 	}
     }
 }
@@ -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/common.opt b/gcc/common.opt
index 53aac19..6382e75 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -550,9 +550,13 @@ Wattributes
 Common Var(warn_attributes) Init(1) Warning
 Warn about inappropriate attribute usage.
 
-Wattribute-alias
-Common Var(warn_attributes) Init(1) Warning
-Warn about type safety and similar errors in attribute alias and related.
+Wattribute-alias=
+Common Joined RejectNegative UInteger Var(warn_attribute_alias) Init(1) Warning IntegerRange(0, 2)
+Warn about type safety and similar errors and mismatches in attribute alias and related.
+
+Wno-attribute-alias
+Common Alias(Wattribute_alias=, 0, 0) Warning
+Disable -Wattribute-alias.
 
 Wcannot-profile
 Common Var(warn_cannot_profile) Init(1) Warning
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 syntactic 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 syntactic 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 syntactic 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..c027acd 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -282,7 +282,8 @@ Objective-C and Objective-C++ Dialects}.
 -Walloc-zero  -Walloc-size-larger-than=@var{byte-size}
 -Walloca  -Walloca-larger-than=@var{byte-size} @gol
 -Wno-aggressive-loop-optimizations  -Warray-bounds  -Warray-bounds=@var{n} @gol
--Wno-attributes  -Wbool-compare  -Wbool-operation @gol
+-Wno-attributes -Wno-attribute-alias @gol
+-Wbool-compare  -Wbool-operation @gol
 -Wno-builtin-declaration-mismatch @gol
 -Wno-builtin-macro-redefined  -Wc90-c99-compat  -Wc99-c11-compat @gol
 -Wc++-compat  -Wc++11-compat  -Wc++14-compat  -Wc++17-compat  @gol
@@ -314,7 +315,7 @@ Objective-C and Objective-C++ Dialects}.
 -Winvalid-pch  -Wlarger-than=@var{byte-size} @gol
 -Wlogical-op  -Wlogical-not-parentheses  -Wlong-long @gol
 -Wmain  -Wmaybe-uninitialized  -Wmemset-elt-size  -Wmemset-transposed-args @gol
--Wmisleading-indentation  -Wmissing-attributes -Wmissing-braces @gol
+-Wmisleading-indentation  -Wno-missing-attributes -Wmissing-braces @gol
 -Wmissing-field-initializers  -Wmissing-include-dirs  -Wmissing-profile @gol
 -Wno-multichar  -Wmultistatement-macros  -Wnonnull  -Wnonnull-compare @gol
 -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
@@ -4770,13 +4771,24 @@ about the layout of the file that the directive references.
 
 This warning is enabled by @option{-Wall} in C and C++.
 
-@item -Wmissing-attributes
+@item -Wno-missing-attributes
 @opindex Wmissing-attributes
 @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.
+This typically represents a potential optimization oportunity rather
+than a hidden bug.  The @option{-Wattribute-alias} option controls warnings
+issued for mismatches between declarations of aliases and their targets
+that might be indicative of code generation bugs.
+Attributes 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},
@@ -5786,10 +5798,28 @@ pointers. This warning level may give a larger number of
 false positives and is deactivated by default.
 @end table
 
-@item -Wattribute-alias
+@item -Wattribute-alias=@var{n}
+@itemx -Wno-attribute-alias
+@opindex -Wattribute-alias
+@opindex -Wno-attribute-alias
 Warn about declarations using the @code{alias} and similar attributes whose
-target is incompatible with the type of the alias.  @xref{Function Attributes,
-,Declaring Attributes of Functions}.
+target is incompatible with the type of the alias.
+@xref{Function Attributes,,Declaring Attributes of Functions}.
+The @option{-Wattribute-alias=1} is  enabled by @option{-Wall}.
+
+@table @gcctabopt
+@item -Wattribute-alias=1
+The default warning level of the @option{-Wattribute-alias} option diagnoses
+incompatibilities between the type of the alias declaration and that of its
+target.  Such incompatibilities are typically indicative of bugs.
+
+@item -Wattribute-alias=2
+At this level @option{-Wattribute-alias} also diagnoses mismatches between
+the set of attributes of the alias declaration and the attributes applied
+to its target.  Although in some cases such mismatches may indicate bugs,
+in other cases they may be benign and could be resolved simply by adding
+the missing attribute to the target.
+@end table
 
 @item -Wbool-compare
 @opindex Wno-bool-compare
diff --git a/gcc/testsuite/gcc.dg/Wattribute-alias.c b/gcc/testsuite/gcc.dg/Wattribute-alias.c
new file mode 100644
index 0000000..175e40b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wattribute-alias.c
@@ -0,0 +1,49 @@
+/* PR middle-end/81824 - Warn for missing attributes with function aliases
+   { dg-do compile }
+   { dg-options "-Wall -Wattribute-alias=2" } */
+
+#define ATTR(...)   __attribute__ ((__VA_ARGS__))
+
+
+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." } */
+
+
+/* There is no obvious relationship between the attributes on an ifunc
+   resolver and those on its aliases.  Verify that mismatches between
+   aliases and ifunc resolvers do not trigger warnings.  */
+
+typedef int F (void);
+
+ATTR (pure, leaf) F* resolve_to_const (void)
+{ return alias_const; }
+
+ATTR (ifunc ("resolve_to_const")) F alias_no_const_ifunc;
+ATTR (const, ifunc ("resolve_to_const")) F alias_const_ifunc;
+ATTR (ifunc ("resolve_to_const")) int alias_no_leaf_ifunc (void);
diff --git a/gcc/testsuite/gcc.dg/Wmissing-attributes.c b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
new file mode 100644
index 0000000..2a98182
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wmissing-attributes.c
@@ -0,0 +1,95 @@
+/* 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." } */
+
+
+ATTR ((leaf)) int
+target_leaf (void)              /* { dg-message ".alias_no_leaf. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_leaf"))) int
+alias_no_leaf (void);           /* { dg-warning ".alias_no_leaf. specifies less restrictive attribute than its target .target_leaf.: .leaf." } */
+
+
+/* Verify that attributes noclone, noinline, and noipa on the target
+   don't cause a warning for aliases without the attribute.  */
+
+ATTR ((noclone)) int
+target_noclone (void)
+{ return 0; }
+
+ATTR ((alias ("target_noclone"))) int
+alias_no_noclone (void);
+
+
+ATTR ((noipa)) int
+target_noipa (void)
+{ return 0; }
+
+ATTR ((alias ("target_noipa"))) int
+alias_no_noipa (void);
+
+
+ATTR ((noinline)) int
+target_noinline (void)
+{ return 0; }
+
+ATTR ((alias ("target_noinline"))) int
+alias_no_noinline (void);
+
+
+ATTR ((nothrow)) int
+target_nothrow (void)           /* { dg-message ".alias_no_nothrow. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_nothrow"))) int
+alias_no_nothrow (void);        /* { dg-warning ".alias_no_nothrow. specifies less restrictive attribute than its target .target_nothrow.: .nothrow." } */
+
+
+/* Verify that attribute weak on the target doesn't cause and isn't
+   mentioned in a warning for aliases without the attribute.  */
+
+ATTR ((weak)) int
+target_weak (void)
+{ return 0; }
+
+ATTR ((alias ("target_weak"))) int
+alias_no_weak (void);
+
+
+ATTR ((nothrow, weak)) int
+target_nothrow_weak (void)      /* { dg-message ".alias_nothrow_no_weak. target declared here" } */
+{ return 0; }
+
+ATTR ((alias ("target_nothrow_weak"))) int
+alias_nothrow_no_weak (void);        /* { dg-warning ".alias_nothrow_no_weak. specifies less restrictive attribute than its target .target_nothrow_weak.: .nothrow." } */
+
+
+/* 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..1e6aef2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-copy-2.c
@@ -0,0 +1,191 @@
+/* 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 (); }
+
+
+/* Verify that attribute nonnull (which is part of a function type,
+   even when it's specified on a function declaration) is copied to
+   the alias from its target.  Expect no warning about the alias
+   specifying less restrictive attributes than its target, but do
+   verify that passing a null to the alias triggers -Wnonnull.  */
+
+ATTR ((nonnull))
+void* ftarget_nonnull (void *p) { return p; }
+
+ATTR ((alias ("ftarget_nonnull"), copy (ftarget_nonnull)))
+void* falias_nonnull (void*);
+
+void call_falias_nonnull (void)
+{
+  falias_nonnull (0);         /* { dg-warning "-Wnonnull" } */
+}
+
+/* Same as above but for malloc.  Also verify that the attribute
+   on the alias is used by -Wstringop-overflow.  */
+
+ATTR ((malloc))
+void* ftarget_malloc (void) { return __builtin_malloc (1); }
+
+ATTR ((alias ("ftarget_malloc"), copy (ftarget_malloc)))
+void* falias_malloc (void);
+
+void* call_falias_malloc (void)
+{
+  char *p = falias_malloc ();
+  __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0));   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+  return p;
+}
+
+/* Same as above but for nothrow.  */
+
+ATTR ((nothrow))
+void ftarget_nothrow (void) { }
+
+ATTR ((alias ("ftarget_nothrow"), copy (ftarget_nothrow)))
+void falias_nothrow (void);
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
In file included from <command-line>:
./../include/libc-symbols.h:534:26: warning: ‘__EI___redirect_strcat’ specifies less restrictive attributes than its target ‘strcat’: ‘leaf’, ‘nonnull’ [-Wmissing-attributes]
534 |   extern __typeof (name) __EI_##name \
    |                          ^~~~~
../sysdeps/x86_64/multiarch/strcat.c:32:1: note: in expansion of macro ‘__hidden_ver1’
32 | __hidden_ver1 (strcat, __GI_strcat, __redirect_strcat)
   | ^~~~~~~~~~~~~
In file included from <command-line>:
../sysdeps/x86_64/multiarch/strcat.c:29:43: note: ‘__EI___redirect_strcat’ target declared here
29 | libc_ifunc_redirected (__redirect_strcat, strcat, IFUNC_SELECTOR ());
   |                                           ^~~~~~
./../include/libc-symbols.h:874:31: note: in definition of macro ‘__ifunc’
874 |   extern __typeof (type_name) name __attribute__   \
    |                               ^~~~
../sysdeps/x86_64/multiarch/strcat.c:29:1: note: in expansion of macro ‘libc_ifunc_redirected’
29 | libc_ifunc_redirected (__redirect_strcat, strcat, IFUNC_SELECTOR ());
   | ^~~~~~~~~~~~~~~~~~~~~
Joseph Myers Oct. 24, 2018, 12:22 p.m. UTC | #5
On Wed, 24 Oct 2018, Martin Sebor wrote:

> But if you do want to avoid the attribute on declarations of
> these functions regardless it should be safe to add it after
> the declaration in the .c file, like so:
> 
> __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
>   __attribute__ ((visibility ("hidden"), copy (strcmp)));

The obvious question there is whether the glibc patch should use copy 
(local) as well as copy (name) in the definition of __hidden_ver1.
Joseph Myers Oct. 24, 2018, 12:24 p.m. UTC | #6
On Wed, 24 Oct 2018, Martin Sebor wrote:

> /* The compiler will optimize based on the knowledge the parameter is
>    not NULL.  This will omit tests.  A robust implementation cannot allow
>    this so when compiling glibc itself we ignore this attribute.  */
> # undef __nonnull
> # define __nonnull(params)
> 
> I don't think this is actually true for recent versions of GCC.
> The nonnull optimization is controlled by
> -fisolate-erroneous-paths-attribute and according to the manual
> and common.opt the option is disabled by default.

I think -fisolate-erroneous-paths-attribute controls something different 
(generating tests and traps rather than simply optimizing on the basis of 
a parameter not being NULL).
Martin Sebor Oct. 24, 2018, 2:57 p.m. UTC | #7
On 10/24/2018 06:22 AM, Joseph Myers wrote:
> On Wed, 24 Oct 2018, Martin Sebor wrote:
>
>> But if you do want to avoid the attribute on declarations of
>> these functions regardless it should be safe to add it after
>> the declaration in the .c file, like so:
>>
>> __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
>>   __attribute__ ((visibility ("hidden"), copy (strcmp)));
>
> The obvious question there is whether the glibc patch should use copy
> (local) as well as copy (name) in the definition of __hidden_ver1.

I tried copy(local) but it breaks where local isn't declared.
I think errno_location was the first one I saw where it referred
to __GI__something_or_other that was previously only defined via
an asm.

Martin
Martin Sebor Oct. 24, 2018, 5:24 p.m. UTC | #8
On 10/24/2018 06:24 AM, Joseph Myers wrote:
> On Wed, 24 Oct 2018, Martin Sebor wrote:
>
>> /* The compiler will optimize based on the knowledge the parameter is
>>    not NULL.  This will omit tests.  A robust implementation cannot allow
>>    this so when compiling glibc itself we ignore this attribute.  */
>> # undef __nonnull
>> # define __nonnull(params)
>>
>> I don't think this is actually true for recent versions of GCC.
>> The nonnull optimization is controlled by
>> -fisolate-erroneous-paths-attribute and according to the manual
>> and common.opt the option is disabled by default.
>
> I think -fisolate-erroneous-paths-attribute controls something different
> (generating tests and traps rather than simply optimizing on the basis of
> a parameter not being NULL).

You're right, the option that does what I was thinking of is
-fdelete-null-pointer-checks and that one is enabled by default.
I should update the description of the attribute to mention this.

Martin
Joseph Myers Oct. 24, 2018, 5:27 p.m. UTC | #9
On Wed, 24 Oct 2018, Martin Sebor wrote:

> On 10/24/2018 06:22 AM, Joseph Myers wrote:
> > On Wed, 24 Oct 2018, Martin Sebor wrote:
> > 
> > > But if you do want to avoid the attribute on declarations of
> > > these functions regardless it should be safe to add it after
> > > the declaration in the .c file, like so:
> > > 
> > > __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
> > >   __attribute__ ((visibility ("hidden"), copy (strcmp)));
> > 
> > The obvious question there is whether the glibc patch should use copy
> > (local) as well as copy (name) in the definition of __hidden_ver1.
> 
> I tried copy(local) but it breaks where local isn't declared.
> I think errno_location was the first one I saw where it referred
> to __GI__something_or_other that was previously only defined via
> an asm.

In that case maybe it should go in the .c files (the patch should define 
some common attribute_copy macro in some internal header, to avoid lots of 
places needing to duplicate conditionals for whether it's supported).

Whether nonnull attributes should be disabled when building glibc is a 
separate question which would involve reviewing lots of functions with 
such attributes against 
<https://sourceware.org/glibc/wiki/Style_and_Conventions#Invalid_pointers> 
to see whether there are checks for NULL that are actually appropriate 
under current glibc conventions but might be optimized away given such 
attributes.  So it should clearly be kept separate from fixes to use copy 
attributes to get better attribute consistency fot aliases.
Martin Sebor Oct. 24, 2018, 8:38 p.m. UTC | #10
On 10/24/2018 11:27 AM, Joseph Myers wrote:
> On Wed, 24 Oct 2018, Martin Sebor wrote:
>
>> On 10/24/2018 06:22 AM, Joseph Myers wrote:
>>> On Wed, 24 Oct 2018, Martin Sebor wrote:
>>>
>>>> But if you do want to avoid the attribute on declarations of
>>>> these functions regardless it should be safe to add it after
>>>> the declaration in the .c file, like so:
>>>>
>>>> __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
>>>>   __attribute__ ((visibility ("hidden"), copy (strcmp)));
>>>
>>> The obvious question there is whether the glibc patch should use copy
>>> (local) as well as copy (name) in the definition of __hidden_ver1.
>>
>> I tried copy(local) but it breaks where local isn't declared.
>> I think errno_location was the first one I saw where it referred
>> to __GI__something_or_other that was previously only defined via
>> an asm.
>
> In that case maybe it should go in the .c files (the patch should define
> some common attribute_copy macro in some internal header, to avoid lots of
> places needing to duplicate conditionals for whether it's supported).

I defined __attribute_copy__ in cdefs.h like other attributes
and used it in libc-symbols.h and in the string .c files but
it turns out that cdefs.h isn't always included when the
libc-symbols.h macros are used.  For instance,
sysdeps/unix/sysv/linux/umount.c is compiled without it which
results in errors.  So the way I worked around it pretty hacky.
Attached is what I have.  I'm sure there's a better way that
you will want to adopt for Glibc but this works as a proof of
concept and results in no warnings by default.  With
-Wattribute-alias=2 it gives the attached warnings for aliases
with more restrictive attributes than their targets (alloc_size,
const, leaf, malloc, nonnull, noreturn, nothrow, pure, and
returns_nonnull).

The GCC patch is the same so please let me know if there's
something to change there.

>
> Whether nonnull attributes should be disabled when building glibc is a
> separate question which would involve reviewing lots of functions with
> such attributes against
> <https://sourceware.org/glibc/wiki/Style_and_Conventions#Invalid_pointers>
> to see whether there are checks for NULL that are actually appropriate
> under current glibc conventions but might be optimized away given such
> attributes.  So it should clearly be kept separate from fixes to use copy
> attributes to get better attribute consistency fot aliases.

Agreed.  I certainly don't plan to undertake this project as
part of the GCC attribute enhancement.

Martin
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index 8b9273c..e71a479 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -125,6 +125,11 @@
 # define ASM_LINE_SEP ;
 #endif
 
+#ifndef __attribute_copy__
+/* Provide an empty definition when cdefs.h is not included.  */
+# define __attribute_copy__(arg)
+#endif
+
 #ifndef __ASSEMBLER__
 /* GCC understands weak symbols and aliases; use its interface where
    possible, instead of embedded assembly language.  */
@@ -132,7 +137,8 @@
 /* Define ALIASNAME as a strong alias for NAME.  */
 # define strong_alias(name, aliasname) _strong_alias(name, aliasname)
 # define _strong_alias(name, aliasname) \
-  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
+  extern __typeof (name) aliasname __attribute__ ((alias (#name))) \
+    __attribute_copy__ (name);
 
 /* This comes between the return type and function name in
    a function definition to make that definition weak.  */
@@ -143,14 +149,16 @@
    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))) \
+    __attribute_copy__ (name);
 
 /* Same as WEAK_ALIAS, but mark symbol as hidden.  */
 # define weak_hidden_alias(name, aliasname) \
   _weak_hidden_alias (name, aliasname)
 # define _weak_hidden_alias(name, aliasname) \
   extern __typeof (name) aliasname \
-    __attribute__ ((weak, alias (#name), __visibility__ ("hidden")));
+    __attribute__ ((weak, alias (#name), __visibility__ ("hidden"))) \
+    __attribute_copy__ (name);
 
 /* Declare SYMBOL as weak undefined symbol (resolved to 0 if not defined).  */
 # define weak_extern(symbol) _weak_extern (weak symbol)
@@ -532,7 +540,8 @@ 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))))	\
+    __attribute_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 +554,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)))	\
+    __attribute_copy__ (name);						\
   __hidden_nolink3 (local, internal, #name "@" #version)
 #  define __hidden_nolink3(local, internal, vername) \
   __asm__ (".symver " #internal ", " vername);
diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 3f6fe3c..8d58568 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -431,6 +431,16 @@
 # define __attribute_nonstring__
 #endif
 
+/* Undefine (also defined in libc-symbols.h).  */
+#undef __attribute_copy__
+#if __GNUC_PREREQ (9, 0)
+/* Copies attributes from the declaration or type referenced by
+   the argument.  */
+# define __attribute_copy__(arg) __attribute__ ((__copy__ (arg)))
+#else
+# define __attribute_copy__(arg)
+#endif
+
 #if (!defined _Static_assert && !defined __cplusplus \
      && (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) < 201112 \
      && (!__GNUC_PREREQ (4, 6) || defined __STRICT_ANSI__))
diff --git a/sysdeps/x86_64/multiarch/memchr.c b/sysdeps/x86_64/multiarch/memchr.c
index 016f578..ce2e69c 100644
--- a/sysdeps/x86_64/multiarch/memchr.c
+++ b/sysdeps/x86_64/multiarch/memchr.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_memchr, memchr, IFUNC_SELECTOR ());
 strong_alias (memchr, __memchr)
 # ifdef SHARED
 __hidden_ver1 (memchr, __GI_memchr, __redirect_memchr)
-  __attribute__((visibility ("hidden")));
+__attribute__((visibility ("hidden"))) __attribute_copy__ (memchr);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/memcmp.c b/sysdeps/x86_64/multiarch/memcmp.c
index 6f3ca43..bbd3b01 100644
--- a/sysdeps/x86_64/multiarch/memcmp.c
+++ b/sysdeps/x86_64/multiarch/memcmp.c
@@ -32,6 +32,6 @@ weak_alias (memcmp, bcmp)
 
 # ifdef SHARED
 __hidden_ver1 (memcmp, __GI_memcmp, __redirect_memcmp)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (memcmp);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/mempcpy.c b/sysdeps/x86_64/multiarch/mempcpy.c
index 9fe41dd..d2f7928 100644
--- a/sysdeps/x86_64/multiarch/mempcpy.c
+++ b/sysdeps/x86_64/multiarch/mempcpy.c
@@ -35,8 +35,8 @@ libc_ifunc_redirected (__redirect_mempcpy, __mempcpy, IFUNC_SELECTOR ());
 weak_alias (__mempcpy, mempcpy)
 # ifdef SHARED
 __hidden_ver1 (__mempcpy, __GI___mempcpy, __redirect___mempcpy)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (mempcpy);
 __hidden_ver1 (mempcpy, __GI_mempcpy, __redirect_mempcpy)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (mempcpy);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/memset.c b/sysdeps/x86_64/multiarch/memset.c
index 064841d..87246dd 100644
--- a/sysdeps/x86_64/multiarch/memset.c
+++ b/sysdeps/x86_64/multiarch/memset.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_memset, memset, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (memset, __GI_memset, __redirect_memset)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden")))  __attribute_copy__ (memset);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/stpcpy.c b/sysdeps/x86_64/multiarch/stpcpy.c
index 1e340fc..f74a54b 100644
--- a/sysdeps/x86_64/multiarch/stpcpy.c
+++ b/sysdeps/x86_64/multiarch/stpcpy.c
@@ -35,8 +35,8 @@ libc_ifunc_redirected (__redirect_stpcpy, __stpcpy, IFUNC_SELECTOR ());
 weak_alias (__stpcpy, stpcpy)
 # ifdef SHARED
 __hidden_ver1 (__stpcpy, __GI___stpcpy, __redirect___stpcpy)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (stpcpy);
 __hidden_ver1 (stpcpy, __GI_stpcpy, __redirect_stpcpy)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (stpcpy);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strcat.c b/sysdeps/x86_64/multiarch/strcat.c
index 1f7f626..1922c0a 100644
--- a/sysdeps/x86_64/multiarch/strcat.c
+++ b/sysdeps/x86_64/multiarch/strcat.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_strcat, strcat, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strcat, __GI_strcat, __redirect_strcat)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (strcat);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strchr.c b/sysdeps/x86_64/multiarch/strchr.c
index 76d64fb..87e99ba 100644
--- a/sysdeps/x86_64/multiarch/strchr.c
+++ b/sysdeps/x86_64/multiarch/strchr.c
@@ -50,6 +50,6 @@ libc_ifunc_redirected (__redirect_strchr, strchr, IFUNC_SELECTOR ());
 weak_alias (strchr, index)
 # ifdef SHARED
 __hidden_ver1 (strchr, __GI_strchr, __redirect_strchr)
-  __attribute__((visibility ("hidden")));
+  __attribute__((visibility ("hidden"))) __attribute_copy__ (strchr);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strcmp.c b/sysdeps/x86_64/multiarch/strcmp.c
index b903e41..e3cf39d 100644
--- a/sysdeps/x86_64/multiarch/strcmp.c
+++ b/sysdeps/x86_64/multiarch/strcmp.c
@@ -54,6 +54,6 @@ libc_ifunc_redirected (__redirect_strcmp, strcmp, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (strcmp);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strcpy.c b/sysdeps/x86_64/multiarch/strcpy.c
index 12e0e3f..838d916 100644
--- a/sysdeps/x86_64/multiarch/strcpy.c
+++ b/sysdeps/x86_64/multiarch/strcpy.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_strcpy, strcpy, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strcpy, __GI_strcpy, __redirect_strcpy)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden")))  __attribute_copy__ (strcpy);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strcspn.c b/sysdeps/x86_64/multiarch/strcspn.c
index 9712e84..9d96526 100644
--- a/sysdeps/x86_64/multiarch/strcspn.c
+++ b/sysdeps/x86_64/multiarch/strcspn.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_strcspn, strcspn, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strcspn, __GI_strcspn, __redirect_strcspn)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (strcspn);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strlen.c b/sysdeps/x86_64/multiarch/strlen.c
index 1758d22..0860083 100644
--- a/sysdeps/x86_64/multiarch/strlen.c
+++ b/sysdeps/x86_64/multiarch/strlen.c
@@ -29,6 +29,6 @@
 libc_ifunc_redirected (__redirect_strlen, strlen, IFUNC_SELECTOR ());
 # ifdef SHARED
 __hidden_ver1 (strlen, __GI_strlen, __redirect_strlen)
-  __attribute__((visibility ("hidden")));
+  __attribute__((visibility ("hidden")))  __attribute_copy__ (strlen);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strncmp.c b/sysdeps/x86_64/multiarch/strncmp.c
index 02b6d0b..32f5c6c 100644
--- a/sysdeps/x86_64/multiarch/strncmp.c
+++ b/sysdeps/x86_64/multiarch/strncmp.c
@@ -55,6 +55,6 @@ libc_ifunc_redirected (__redirect_strncmp, strncmp, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strncmp, __GI_strncmp, __redirect_strncmp)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (strncmp);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strncpy.c b/sysdeps/x86_64/multiarch/strncpy.c
index 3c3de8b..3201f0f 100644
--- a/sysdeps/x86_64/multiarch/strncpy.c
+++ b/sysdeps/x86_64/multiarch/strncpy.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_strncpy, strncpy, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strncpy, __GI_strncpy, __redirect_strncpy)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden")))  __attribute_copy__ (strncpy);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strnlen.c b/sysdeps/x86_64/multiarch/strnlen.c
index 3ab94ce..9d64335 100644
--- a/sysdeps/x86_64/multiarch/strnlen.c
+++ b/sysdeps/x86_64/multiarch/strnlen.c
@@ -32,8 +32,8 @@ libc_ifunc_redirected (__redirect_strnlen, __strnlen, IFUNC_SELECTOR ());
 weak_alias (__strnlen, strnlen);
 # ifdef SHARED
 __hidden_ver1 (__strnlen, __GI___strnlen, __redirect___strnlen)
-  __attribute__((visibility ("hidden")));
+  __attribute__((visibility ("hidden"))) __attribute_copy__ (strnlen);
 __hidden_ver1 (strnlen, __GI_strnlen, __redirect_strnlen)
-  __attribute__((weak, visibility ("hidden")));
+  __attribute__((weak, visibility ("hidden"))) __attribute_copy__ (strnlen);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strpbrk.c b/sysdeps/x86_64/multiarch/strpbrk.c
index a0d435a..f110367 100644
--- a/sysdeps/x86_64/multiarch/strpbrk.c
+++ b/sysdeps/x86_64/multiarch/strpbrk.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_strpbrk, strpbrk, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strpbrk, __GI_strpbrk, __redirect_strpbrk)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden"))) __attribute_copy__ (strpbrk);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strrchr.c b/sysdeps/x86_64/multiarch/strrchr.c
index a719edd..ba7458a 100644
--- a/sysdeps/x86_64/multiarch/strrchr.c
+++ b/sysdeps/x86_64/multiarch/strrchr.c
@@ -29,6 +29,6 @@ libc_ifunc_redirected (__redirect_strrchr, strrchr, IFUNC_SELECTOR ());
 weak_alias (strrchr, rindex);
 # ifdef SHARED
 __hidden_ver1 (strrchr, __GI_strrchr, __redirect_strrchr)
-  __attribute__((visibility ("hidden")));
+  __attribute__((visibility ("hidden"))) __attribute_copy__ (strrchr);
 # endif
 #endif
diff --git a/sysdeps/x86_64/multiarch/strspn.c b/sysdeps/x86_64/multiarch/strspn.c
index 56ab4d9..8b80bdc 100644
--- a/sysdeps/x86_64/multiarch/strspn.c
+++ b/sysdeps/x86_64/multiarch/strspn.c
@@ -30,6 +30,6 @@ libc_ifunc_redirected (__redirect_strspn, strspn, IFUNC_SELECTOR ());
 
 # ifdef SHARED
 __hidden_ver1 (strspn, __GI_strspn, __redirect_strspn)
-  __attribute__ ((visibility ("hidden")));
+  __attribute__ ((visibility ("hidden")))  __attribute_copy__ (strspn);
 # endif
 #endif
Diagnostic                        Count   Unique    Files
-Wattribute-alias=                 1652      687      573

-Wattribute-alias Instances:
  asctime.c:79
  asprintf.c:42
  asprintf.c:43
  backtracesyms.c:120
  backtracesymsfd.c:121
  bindtextdom.c:335
  bindtextdom.c:336
  btowc.c:100
  ./cabs_template.c:29
  canonicalize.c:222
  canonicalize.c:247
  ./carg_template.c:29
  ./cimag_template.c:28
  ./conj_template.c:28
  ./creal_template.c:28
  crypt-entry.c:156
  ctype-c99_l.c:27
  ctype_l.c:27
  ctype_l.c:28
  ctype_l.c:29
  ctype_l.c:30
  ctype_l.c:31
  ctype_l.c:32
  ctype_l.c:33
  ctype_l.c:34
  ctype_l.c:35
  ctype_l.c:36
  ctype_l.c:37
  dcgettext.c:52
  dcngettext.c:54
  dgettext.c:55
  difftime.c:121
  dladdr1.c:52
  dladdr.c:42
  dlclose.c:49
  dlerror.c:120
  dlinfo.c:124
  dl-minimal.c:295
  dlmopen.c:105
  dlsym.c:77
  dlvsym.c:81
  dngettext.c:56
  duplocale.c:89
  efgcvt.c:124
  efgcvt.c:125
  efgcvt.c:126
  efgcvt_r.c:261
  efgcvt_r.c:262
  erand48_r.c:47
  execvpe.c:192
  explicit_bzero_chk.c:44
  fileno.c:44
  fileno.c:51
  fmemopen.c:226
  fmtmsg.c:367
  fprintf.c:38
  fprintf_chk.c:44
  freelocale.c:54
  fscanf.c:36
  fstat.c:55
  getaliasent_r.c:24
  getaliasname_r.c:23
  getauxval.c:45
  gettext.c:60
  getttyent.c:58
  getutent.c:45
  getutent_r.c:138
  getutent_r.c:155
  getutent_r.c:172
  getutent_r.c:185
  getutid.c:43
  getutid_r.c:63
  getutline.c:44
  getutline_r.c:46
  glob_pattern_p.c:33
  gmtime.c:29
  group_member.c:49
  hsearch.c:49
  hsearch_r.c:106
  hsearch_r.c:128
  hsearch_r.c:231
  ./../include/libc-symbols.h:542
  inet_addr.c:181
  inet_addr.c:93
  inet_mkadr.c:55
  inet_pton.c:72
  iofopncook.c:217
  iofputs.c:45
  iofwrite.c:53
  ioputs.c:48
  iovsprintf.c:48
  iovsprintf.c:49
  iovsscanf.c:45
  isctype.c:28
  iswctype.c:37
  iswctype_l.c:36
  jrand48_r.c:34
  lckpwdf.c:142
  lckpwdf.c:169
  lcong48_r.c:36
  localeconv.c:68
  localtime.c:32
  ../login/updwtmp.c:35
  lstat.c:55
  malloc.c:3314
  malloc.c:5383
  malloc.c:5578
  malloc.c:5581
  malloc.c:5582
  malloc.c:5583
  malloc.c:5585
  malloc.c:5586
  malloc.c:5587
  malloc.c:5588
  malloc.c:5590
  malloc.c:5591
  malloc.c:5593
  malloc.c:5594
  malloc.c:5595
  ../math/math-narrow.h:346
  ./math-narrow.h:284
  ./math-narrow.h:298
  ./math-narrow.h:336
  ./math-narrow.h:341
  mbrtowc.c:123
  mbsinit.c:38
  mbsnrtowcs.c:143
  mbsrtowcs.c:31
  memccpy.c:42
  memmem.c:83
  ../misc/sbrk.c:62
  mknod.c:55
  mntent_r.c:187
  mntent_r.c:271
  mntent_r.c:297
  mntent_r.c:52
  mntent_r.c:64
  newlocale.c:280
  ngettext.c:62
  ../nptl/pthread_mutex_lock.c:612
  ../nptl/pthread_mutex_timedlock.c:651
  ../nptl/pthread_mutex_trylock.c:417
  ../nptl/sigaction.c:33
  nrand48_r.c:37
  on_exit.c:46
  ../posix/glob.c:1164
  printf.c:40
  printf_chk.c:44
  printf_size.c:218
  pthread_atfork.c:56
  pthread_attr_destroy.c:42
  pthread_attr_getaffinity.c:60
  pthread_attr_getdetachstate.c:34
  pthread_attr_getinheritsched.c:35
  pthread_attr_getschedparam.c:36
  pthread_attr_getschedpolicy.c:34
  pthread_attr_getscope.c:35
  pthread_attr_getstack.c:40
  pthread_attr_getstacksize.c:43
  pthread_attr_init.c:51
  pthread_attr_setaffinity.c:59
  pthread_attr_setdetachstate.c:43
  pthread_attr_setinheritsched.c:42
  pthread_attr_setschedparam.c:43
  pthread_attr_setschedpolicy.c:43
  pthread_attr_setscope.c:46
  pthread_attr_setstack.c:58
  pthread_attr_setstacksize.c:46
  pthread_condattr_destroy.c:28
  pthread_condattr_init.c:36
  pthread_cond_broadcast.c:91
  pthread_cond_destroy.c:62
  pthread_cond_init.c:53
  pthread_cond_signal.c:99
  pthread_create.c:885
  pthread_detach.c:56
  pthread_equal.c:27
  pthread_getschedparam.c:73
  pthread_getspecific.c:66
  pthread_key_create.c:50
  pthread_key_delete.c:42
  pthread_mutexattr_destroy.c:27
  pthread_mutexattr_init.c:40
  pthread_mutexattr_settype.c:43
  pthread_mutex_destroy.c:44
  pthread_mutex_init.c:165
  pthread_mutex_unlock.c:358
  pthread_rwlock_destroy.c:31
  pthread_rwlock_init.c:50
  pthread_rwlock_rdlock.c:32
  pthread_rwlock_tryrdlock.c:112
  pthread_rwlock_trywrlock.c:61
  pthread_rwlock_unlock.c:47
  pthread_rwlock_wrlock.c:32
  pthread_setschedparam.c:72
  pthread_setspecific.c:92
  putc.c:39
  qefgcvt.c:22
  qefgcvt_r.c:23
  quick_exit.c:37
  random.c:215
  random.c:216
  random.c:246
  random.c:273
  random.c:300
  random_r.c:216
  random_r.c:284
  random_r.c:339
  random_r.c:400
  reg-modifier.c:96
  reg-printf.c:77
  reg-type.c:61
  revoke.c:29
  sbrk.c:62
  ./s_cacosh_template.c:94
  ./s_cacos_template.c:55
  scanf.c:38
  ./s_canonicalize_template.c:37
  ./s_casinh_template.c:70
  ./s_casin_template.c:62
  ./s_catanh_template.c:136
  ./s_catan_template.c:142
  ./s_ccosh_template.c:138
  ./s_ccos_template.c:35
  ./s_cexp_template.c:151
  ./s_clog10_template.c:123
  ./s_clog_template.c:116
  ./s_cpow_template.c:29
  ./s_cproj_template.c:40
  ./s_csinh_template.c:157
  ./s_csin_template.c:162
  ./s_csqrt_template.c:161
  ./s_ctanh_template.c:130
  ./s_ctan_template.c:130
  secure-getenv.c:31
  seed48_r.c:39
  sem_destroy.c:32
  sem_getvalue.c:45
  sem_init.c:64
  sem_post.c:79
  sem_wait.c:87
  setenv.c:337
  setenv.c:338
  setenv.c:339
  ../setjmp/longjmp.c:48
  ../setjmp/longjmp.c:49
  ../setjmp/longjmp.c:50
  ./s_fdim_template.c:36
  ./s_fmax_template.c:35
  ./s_fmin_template.c:35
  ../signal/sigreturn.c:29
  ./s_ldexp_template.c:29
  ./s_ldexp_template.c:31
  ./s_nan_template.c:33
  s_nextafter.c:88
  snprintf.c:39
  snprintf_chk.c:39
  sprintf.c:38
  sprintf.c:39
  sprintf_chk.c:36
  srand48_r.c:39
  sscanf.c:38
  sscanf.c:41
  ./s_significand_template.c:33
  stat.c:54
  ../stdlib/strtod_l.c:51
  ../stdlib/strtof_l.c:34
  ../stdlib/strtol.c:108
  ../stdlib/strtol.c:36
  ../stdlib/strtol.c:44
  ../stdlib/strtol.c:54
  ../stdlib/strtol_l.c:66
  ../stdlib/strtol_l.c:72
  ../stdlib/strtol_l.c:80
  ../stdlib/strtol_l.c:86
  strcasestr.c:95
  strcoll_l.c:373
  strdup.c:53
  _strerror.c:73
  strfmon.c:39
  strfmon_l.c:618
  strftime_l.c:1451
  strndup.c:55
  strptime_l.c:1251
  strsep.c:47
  strtod_l.c:55
  strtof_l.c:38
  strverscmp.c:107
  strxfrm_l.c:746
  swprintf.c:36
  swscanf.c:36
  ../sysdeps/i386/fpu/s_atanl.c:22
  ../sysdeps/i386/fpu/s_isinfl.c:32
  ../sysdeps/i386/fpu/s_isnanl.c:43
  ../sysdeps/i386/fpu/s_logbl.c:19
  ../sysdeps/i386/fpu/s_nextafterl.c:125
  ../sysdeps/i386/fpu/s_nextafterl.c:127
  ../sysdeps/i386/fpu/s_nexttoward.c:90
  ../sysdeps/i386/fpu/s_nexttowardf.c:78
  ../sysdeps/i386/fpu/s_rintl.c:19
  ../sysdeps/i386/fpu/s_significandl.c:18
  ../sysdeps/ieee754/dbl-64/s_asinh.c:70
  ../sysdeps/ieee754/dbl-64/s_cbrt.c:72
  ../sysdeps/ieee754/dbl-64/s_erf.c:300
  ../sysdeps/ieee754/dbl-64/s_erf.c:423
  ../sysdeps/ieee754/dbl-64/s_expm1.c:261
  ../sysdeps/ieee754/dbl-64/s_setpayload.c:4
  ../sysdeps/ieee754/dbl-64/s_setpayloadsig.c:4
  ../sysdeps/ieee754/dbl-64/s_sincos.c:104
  ../sysdeps/ieee754/dbl-64/s_tanh.c:96
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_finite.c:32
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_frexp.c:66
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_isnan.c:34
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_llround.c:67
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_llround.c:74
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_logb.c:46
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_modf.c:64
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_nearbyint.c:65
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_remquo.c:111
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_round.c:66
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_totalorder.c:49
  ../sysdeps/ieee754/dbl-64/wordsize-64/s_totalordermag.c:46
  ../sysdeps/ieee754/float128/../ldbl-128/s_ceill.c:68
  ../sysdeps/ieee754/float128/../ldbl-128/s_copysignl.c:40
  ../sysdeps/ieee754/float128/../ldbl-128/s_fabsl.c:35
  ../sysdeps/ieee754/float128/../ldbl-128/s_floorl.c:69
  ../sysdeps/ieee754/float128/../ldbl-128/s_fmal.c:300
  ../sysdeps/ieee754/float128/../ldbl-128/s_nearbyintl.c:69
  ../sysdeps/ieee754/float128/../ldbl-128/s_rintl.c:64
  ../sysdeps/ieee754/float128/../ldbl-128/s_roundl.c:82
  ../sysdeps/ieee754/float128/../ldbl-128/s_totalorderl.c:56
  ../sysdeps/ieee754/float128/../ldbl-128/s_totalordermagl.c:50
  ../sysdeps/ieee754/float128/../ldbl-128/s_truncl.c:58
  ../sysdeps/ieee754/float128/s_setpayloadf128.c:5
  ../sysdeps/ieee754/float128/s_setpayloadsigf128.c:5
  ../sysdeps/ieee754/float128/strtof128_l.c:35
  ../sysdeps/ieee754/float128/strtof128_l.c:39
  ../sysdeps/ieee754/flt-32/s_asinhf.c:52
  ../sysdeps/ieee754/flt-32/s_atanf.c:103
  ../sysdeps/ieee754/flt-32/s_cbrtf.c:63
  ../sysdeps/ieee754/flt-32/s_erff.c:158
  ../sysdeps/ieee754/flt-32/s_erff.c:233
  ../sysdeps/ieee754/flt-32/s_expm1f.c:133
  ../sysdeps/ieee754/flt-32/s_finitef.c:41
  ../sysdeps/ieee754/flt-32/s_frexpf.c:45
  ../sysdeps/ieee754/flt-32/s_isinff.c:29
  ../sysdeps/ieee754/flt-32/s_isnanf.c:38
  ../sysdeps/ieee754/flt-32/s_llroundf.c:74
  ../sysdeps/ieee754/flt-32/s_logbf.c:42
  ../sysdeps/ieee754/flt-32/s_lroundf.c:74
  ../sysdeps/ieee754/flt-32/s_modff.c:55
  ../sysdeps/ieee754/flt-32/s_nearbyintf.c:62
  ../sysdeps/ieee754/flt-32/s_nextafterf.c:75
  ../sysdeps/ieee754/flt-32/s_remquof.c:111
  ../sysdeps/ieee754/flt-32/s_roundf.c:65
  ../sysdeps/ieee754/flt-32/s_setpayloadf.c:4
  ../sysdeps/ieee754/flt-32/s_setpayloadsigf.c:4
  ../sysdeps/ieee754/flt-32/s_tanf.c:77
  ../sysdeps/ieee754/flt-32/s_tanhf.c:64
  ../sysdeps/ieee754/flt-32/s_totalorderf.c:48
  ../sysdeps/ieee754/flt-32/s_totalordermagf.c:46
  ../sysdeps/ieee754/ldbl-96/s_asinhl.c:67
  ../sysdeps/ieee754/ldbl-96/s_cbrtl.c:70
  ../sysdeps/ieee754/ldbl-96/s_cosl.c:89
  ../sysdeps/ieee754/ldbl-96/s_erfl.c:340
  ../sysdeps/ieee754/ldbl-96/s_erfl.c:453
  ../sysdeps/ieee754/ldbl-96/s_fmal.c:297
  ../sysdeps/ieee754/ldbl-96/s_frexpl.c:62
  ../sysdeps/ieee754/ldbl-96/s_llroundl.c:90
  ../sysdeps/ieee754/ldbl-96/s_lroundl.c:112
  ../sysdeps/ieee754/ldbl-96/s_modfl.c:74
  ../sysdeps/ieee754/ldbl-96/s_remquol.c:112
  ../sysdeps/ieee754/ldbl-96/s_roundl.c:94
  ../sysdeps/ieee754/ldbl-96/s_setpayloadl.c:4
  ../sysdeps/ieee754/ldbl-96/s_setpayloadsigl.c:4
  ../sysdeps/ieee754/ldbl-96/s_sincosl.c:77
  ../sysdeps/ieee754/ldbl-96/s_sinl.c:89
  ../sysdeps/ieee754/ldbl-96/s_tanhl.c:92
  ../sysdeps/ieee754/ldbl-96/s_tanl.c:82
  ../sysdeps/ieee754/ldbl-96/s_totalorderl.c:59
  ../sysdeps/ieee754/ldbl-96/s_totalordermagl.c:53
  ../sysdeps/ieee754/ldbl-96/strtold_l.c:26
  ../sysdeps/ieee754/ldbl-96/strtold_l.c:30
  ../sysdeps/nptl/fork.c:159
  ../sysdeps/posix/clock_getres.c:118
  ../sysdeps/posix/dirfd.c:30
  ../sysdeps/posix/euidaccess.c:185
  ../sysdeps/posix/euidaccess.c:186
  ../sysdeps/posix/fpathconf.c:226
  ../sysdeps/posix/getdtsz.c:35
  ../sysdeps/posix/gethostname.c:46
  ../sysdeps/posix/isatty.c:30
  ../sysdeps/posix/pathconf.c:224
  ../sysdeps/posix/profil.c:118
  ../sysdeps/posix/rewinddir.c:41
  ../sysdeps/posix/signal.c:51
  ../sysdeps/posix/signal.c:52
  ../sysdeps/posix/sprofil.c:353
  ../sysdeps/posix/sysconf.c:1204
  ../sysdeps/posix/ulimit.c:92
  ../sysdeps/pthread/aio_misc.c:292
  ../sysdeps/pthread/flockfile.c:30
  ../sysdeps/pthread/flockfile.c:31
  ../sysdeps/pthread/ftrylockfile.c:30
  ../sysdeps/pthread/ftrylockfile.c:31
  ../sysdeps/pthread/funlockfile.c:29
  ../sysdeps/pthread/funlockfile.c:30
  ../sysdeps/unix/bsd/wait3.c:33
  ../sysdeps/unix/clock_gettime.c:135
  ../sysdeps/unix/clock_settime.c:126
  ../sysdeps/unix/sysv/linux/access.c:32
  ../sysdeps/unix/sysv/linux/adjtime.c:91
  ../sysdeps/unix/sysv/linux/alphasort64.c:30
  ../sysdeps/unix/sysv/linux/clock_getcpuclockid.c:48
  ../sysdeps/unix/sysv/linux/ftruncate64.c:33
  ../sysdeps/unix/sysv/linux/ftruncate64.c:37
  ../sysdeps/unix/sysv/linux/futimes.c:51
  ../sysdeps/unix/sysv/linux/getcwd.c:130
  ../sysdeps/unix/sysv/linux/getpagesize.c:32
  ../sysdeps/unix/sysv/linux/getrlimit64.c:51
  ../sysdeps/unix/sysv/linux/getsysstats.c:230
  ../sysdeps/unix/sysv/linux/getsysstats.c:284
  ../sysdeps/unix/sysv/linux/getsysstats.c:326
  ../sysdeps/unix/sysv/linux/getsysstats.c:337
  ../sysdeps/unix/sysv/linux/ifaddrs.c:843
  ../sysdeps/unix/sysv/linux/ifaddrs.c:853
  ../sysdeps/unix/sysv/linux/if_index.c:212
  ../sysdeps/unix/sysv/linux/if_index.c:246
  ../sysdeps/unix/sysv/linux/if_index.c:66
  ../sysdeps/unix/sysv/linux/if_index.c:82
  ../sysdeps/unix/sysv/linux/lseek64.c:41
  ../sysdeps/unix/sysv/linux/lseek64.c:48
  ../sysdeps/unix/sysv/linux/mmap64.c:55
  ../sysdeps/unix/sysv/linux/mmap64.c:59
  ../sysdeps/unix/sysv/linux/mq_open.c:54
  ../sysdeps/unix/sysv/linux/msgctl.c:39
  ../sysdeps/unix/sysv/linux/posix_fadvise64.c:76
  ../sysdeps/unix/sysv/linux/posix_fadvise64.c:77
  ../sysdeps/unix/sysv/linux/pthread_getaffinity.c:47
  ../sysdeps/unix/sysv/linux/pthread_kill.c:59
  ../sysdeps/unix/sysv/linux/pthread_setaffinity.c:47
  ../sysdeps/unix/sysv/linux/ptsname.c:167
  ../sysdeps/unix/sysv/linux/readahead.c:30
  ../sysdeps/unix/sysv/linux/renameat.c:34
  ../sysdeps/unix/sysv/linux/sched_getaffinity.c:47
  ../sysdeps/unix/sysv/linux/semctl.c:70
  ../sysdeps/unix/sysv/linux/setgid.c:33
  ../sysdeps/unix/sysv/linux/setregid.c:33
  ../sysdeps/unix/sysv/linux/setresgid.c:34
  ../sysdeps/unix/sysv/linux/setresuid.c:34
  ../sysdeps/unix/sysv/linux/setreuid.c:33
  ../sysdeps/unix/sysv/linux/setrlimit64.c:43
  ../sysdeps/unix/sysv/linux/setuid.c:32
  ../sysdeps/unix/sysv/linux/shmctl.c:43
  ../sysdeps/unix/sysv/linux/sigqueue.c:42
  ../sysdeps/unix/sysv/linux/sysctl.c:43
  ../sysdeps/unix/sysv/linux/tcgetattr.c:80
  ../sysdeps/unix/sysv/linux/tcsetattr.c:80
  ../sysdeps/unix/sysv/linux/times.c:67
  ../sysdeps/unix/sysv/linux/truncate64.c:33
  ../sysdeps/unix/sysv/linux/truncate64.c:36
  ../sysdeps/unix/sysv/linux/ttyname_r.c:200
  ../sysdeps/unix/sysv/linux/utimes.c:37
  ../sysdeps/unix/sysv/linux/versionsort64.c:30
  ../sysdeps/unix/sysv/linux/wordsize-64/../fstatvfs.c:39
  ../sysdeps/unix/sysv/linux/wordsize-64/../../../../pthread/lio_listio.c:248
  ../sysdeps/unix/sysv/linux/wordsize-64/../statvfs.c:39
  ../sysdeps/unix/sysv/linux/x86_64/brk.c:41
  ../sysdeps/unix/sysv/linux/x86_64/makecontext.c:157
  ../sysdeps/unix/sysv/linux/x86_64/../sched_setaffinity.c:45
  ../sysdeps/unix/sysv/linux/x86_64/sigprocmask.c:39
  ../sysdeps/unix/sysv/linux/x86_64/timer_create.c:28
  ../sysdeps/unix/sysv/linux/x86_64/timer_delete.c:27
  ../sysdeps/unix/sysv/linux/x86_64/timer_getoverr.c:27
  ../sysdeps/unix/sysv/linux/x86_64/timer_gettime.c:27
  ../sysdeps/unix/sysv/linux/x86_64/timer_settime.c:27
  ../sysdeps/unix/sysv/linux/x86/gettimeofday.c:60
  ../sysdeps/x86_64/ffs.c:37
  ../sysdeps/x86_64/fpu/fegetenv.c:34
  ../sysdeps/x86_64/fpu/fegetround.c:34
  ../sysdeps/x86_64/fpu/feholdexcpt.c:40
  ../sysdeps/x86_64/fpu/fesetenv.c:113
  ../sysdeps/x86_64/fpu/fesetround.c:47
  ../sysdeps/x86_64/fpu/feupdateenv.c:52
  ../sysdeps/x86_64/fpu/multiarch/e_exp2f.c:33
  ../sysdeps/x86_64/fpu/multiarch/e_expf.c:36
  ../sysdeps/x86_64/fpu/multiarch/e_log2f.c:36
  ../sysdeps/x86_64/fpu/multiarch/e_logf.c:36
  ../sysdeps/x86_64/fpu/multiarch/e_powf.c:39
  ../sysdeps/x86_64/fpu/multiarch/s_atan.c:27
  ../sysdeps/x86_64/fpu/multiarch/s_ceil.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_ceilf.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_cosf.c:28
  ../sysdeps/x86_64/fpu/multiarch/s_floor.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_floorf.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_fma.c:47
  ../sysdeps/x86_64/fpu/multiarch/s_fmaf.c:46
  ../sysdeps/x86_64/fpu/multiarch/s_nearbyint.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_nearbyintf.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_rint.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_rintf.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_sin.c:28
  ../sysdeps/x86_64/fpu/multiarch/s_sin.c:35
  ../sysdeps/x86_64/fpu/multiarch/s_sincosf.c:28
  ../sysdeps/x86_64/fpu/multiarch/s_sinf.c:28
  ../sysdeps/x86_64/fpu/multiarch/s_tan.c:27
  ../sysdeps/x86_64/fpu/multiarch/s_trunc.c:32
  ../sysdeps/x86_64/fpu/multiarch/s_truncf.c:32
  ../sysdeps/x86_64/fpu/s_fabs.c:27
  ../sysdeps/x86_64/fpu/s_fabsf.c:27
  ../sysdeps/x86_64/multiarch/memcpy.c:38
  ../sysdeps/x86_64/multiarch/memmove.c:32
  ../sysdeps/x86_64/multiarch/mempcpy.c:35
  ../sysdeps/x86_64/multiarch/stpcpy.c:35
  ../sysdeps/x86_64/multiarch/stpncpy.c:33
  ../sysdeps/x86_64/multiarch/strcasecmp.c:34
  ../sysdeps/x86_64/multiarch/strncase.c:34
  ../sysdeps/x86_64/multiarch/strnlen.c:32
  ../sysdeps/x86_64/multiarch/strstr.c:50
  ../sysdeps/x86_64/multiarch/wcslen.c:30
  ../sysdeps/x86_64/multiarch/wcsnlen.c:50
  textdomain.c:124
  towctrans.c:35
  towctrans_l.c:35
  tzset.c:563
  uselocale.c:75
  utmpname.c:76
  version.c:54
  version.c:62
  vfprintf.c:2360
  vfprintf_chk.c:42
  vfscanf.c:3066
  vprintf.c:33
  vprintf_chk.c:41
  vscanf.c:37
  vsnprintf.c:121
  vsnprintf_chk.c:70
  vsprintf_chk.c:89
  w_acos_compat.c:41
  w_acosf_compat.c:41
  w_acosh_compat.c:36
  w_acoshf_compat.c:36
  w_acoshl_compat.c:36
  ./w_acosh_template.c:37
  w_acosl_compat.c:41
  ./w_acos_template.c:37
  w_asin_compat.c:41
  w_asinf_compat.c:41
  w_asinl_compat.c:41
  ./w_asin_template.c:37
  w_atan2_compat.c:44
  w_atan2f_compat.c:44
  w_atan2l_compat.c:44
  ./w_atan2_template.c:37
  w_atanh_compat.c:39
  w_atanhf_compat.c:39
  w_atanhl_compat.c:39
  ./w_atanh_template.c:43
  wcfuncs.c:37
  wcfuncs.c:81
  wcfuncs.c:93
  wcfuncs_l.c:39
  wcfuncs_l.c:64
  wcfuncs_l.c:74
  w_cosh_compat.c:33
  w_coshf_compat.c:37
  w_coshl_compat.c:38
  ./w_cosh_template.c:38
  wcpcpy.c:47
  wcpncpy.c:86
  wcrtomb.c:114
  wcscasecmp.c:66
  wcscasecmp_l.c:23
  wcscat.c:52
  wcschrnul.c:37
  wcscoll_l.c:34
  wcsftime_l.c:25
  wcsncase.c:68
  wcsncase_l.c:23
  wcsncpy.c:87
  wcsnrtombs.c:148
  wcsrtombs.c:145
  wcsxfrm_l.c:34
  wctrans.c:48
  wctrans_l.c:47
  wctype.c:48
  wctype_l.c:49
  w_exp10_compat.c:41
  w_exp10f_compat.c:41
  w_exp10l_compat.c:41
  ./w_exp10_template.c:38
  w_exp2_compat.c:22
  w_exp2l_compat.c:22
  ./w_exp2_template.c:38
  w_exp_compat.c:37
  w_expl_compat.c:45
  ./w_exp_template.c:39
  w_fmod_compat.c:36
  w_fmodf_compat.c:36
  w_fmodl_compat.c:36
  ./w_fmod_template.c:38
  w_hypot_compat.c:34
  w_hypotf_compat.c:38
  w_hypotl_compat.c:39
  ./w_hypot_template.c:38
  ./w_ilogb_template.c:39
  w_j0_compat.c:38
  w_j0_compat.c:67
  w_j0f_compat.c:38
  w_j0f_compat.c:68
  w_j0l_compat.c:38
  w_j0l_compat.c:67
  ./w_j0_template.c:34
  ./w_j0_template.c:50
  w_j1_compat.c:38
  w_j1_compat.c:67
  w_j1f_compat.c:38
  w_j1f_compat.c:68
  w_j1l_compat.c:38
  w_j1l_compat.c:67
  ./w_j1_template.c:34
  ./w_j1_template.c:50
  w_jn_compat.c:38
  w_jn_compat.c:67
  w_jnf_compat.c:38
  w_jnf_compat.c:68
  w_jnl_compat.c:70
  w_jnl_compat.c:94
  ./w_jn_template.c:34
  ./w_jn_template.c:50
  w_lgamma.c:5
  w_lgammaf.c:5
  ./w_lgammaf_main.c:44
  ./w_lgammaf_main.c:45
  w_lgammaf_r_compat.c:40
  w_lgammal.c:5
  ./w_lgammal_main.c:51
  ./w_lgammal_main.c:52
  w_lgammal_r_compat.c:41
  ./w_lgamma_main.c:55
  ./w_lgamma_main.c:56
  w_lgamma_r_compat.c:37
  ./w_lgamma_r_template.c:46
  ./w_lgamma_template.c:41
  w_log10_compat.c:47
  w_log10f_compat.c:47
  w_log10l_compat.c:47
  ./w_log10_template.c:43
  ./w_log1p_template.c:35
  w_log2_compat.c:47
  w_log2l_compat.c:47
  ./w_log2_template.c:43
  w_log_compat.c:47
  w_logl_compat.c:47
  ./w_log_template.c:43
  wmemcpy.c:28
  wmemmove.c:28
  wmempcpy.c:29
  w_pow_compat.c:63
  w_powl_compat.c:63
  ./w_pow_template.c:50
  w_remainder.c:4
  w_remainder_compat.c:37
  w_remainder_compat.c:38
  w_remainderf.c:4
  w_remainderf_compat.c:37
  w_remainderf_compat.c:38
  w_remainderl.c:4
  w_remainderl_compat.c:37
  w_remainderl_compat.c:38
  ./w_remainder_template.c:38
  w_scalb_compat.c:81
  w_scalbf_compat.c:81
  w_scalbl_compat.c:81
  ./w_scalbln_template.c:36
  w_sinh_compat.c:33
  w_sinhf_compat.c:36
  w_sinhl_compat.c:37
  ./w_sinh_template.c:38
  w_sqrt_compat.c:36
  w_sqrtf_compat.c:36
  w_sqrtl_compat.c:36
  ./w_sqrt_template.c:38
  w_tgamma_compat.c:45
  w_tgammaf_compat.c:47
  w_tgammal_compat.c:48
  ./w_tgamma_template.c:53
Martin Sebor Oct. 31, 2018, 4:24 p.m. UTC | #11
Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01481.html

There was quite a bit of discussion between Joseph and me about
the Glibc changes needed to take advantage of the solution but
the GCC patch itself (above) still needs reviewing/approval.

Other than some (minor) changes to the C++ front end the bulk
of the changes for review are to the attribute machinery in
c-family and in the middle-end (attribs.c).

With the Glibc patch below applied, the GCC patch builds Glibc
with no warnings by default.  To find the alias attribute
mismatches as requested, Glibc would enable -Wattribute-alias=2.
Once the GCC patch is committed I will also submit the Glibc
patch.

Glibc patch for reference:
   https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01538.html

On 10/24/2018 02:38 PM, Martin Sebor wrote:
> On 10/24/2018 11:27 AM, Joseph Myers wrote:
>> On Wed, 24 Oct 2018, Martin Sebor wrote:
>>
>>> On 10/24/2018 06:22 AM, Joseph Myers wrote:
>>>> On Wed, 24 Oct 2018, Martin Sebor wrote:
>>>>
>>>>> But if you do want to avoid the attribute on declarations of
>>>>> these functions regardless it should be safe to add it after
>>>>> the declaration in the .c file, like so:
>>>>>
>>>>> __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
>>>>>   __attribute__ ((visibility ("hidden"), copy (strcmp)));
>>>>
>>>> The obvious question there is whether the glibc patch should use copy
>>>> (local) as well as copy (name) in the definition of __hidden_ver1.
>>>
>>> I tried copy(local) but it breaks where local isn't declared.
>>> I think errno_location was the first one I saw where it referred
>>> to __GI__something_or_other that was previously only defined via
>>> an asm.
>>
>> In that case maybe it should go in the .c files (the patch should define
>> some common attribute_copy macro in some internal header, to avoid
>> lots of
>> places needing to duplicate conditionals for whether it's supported).
>
> I defined __attribute_copy__ in cdefs.h like other attributes
> and used it in libc-symbols.h and in the string .c files but
> it turns out that cdefs.h isn't always included when the
> libc-symbols.h macros are used.  For instance,
> sysdeps/unix/sysv/linux/umount.c is compiled without it which
> results in errors.  So the way I worked around it pretty hacky.
> Attached is what I have.  I'm sure there's a better way that
> you will want to adopt for Glibc but this works as a proof of
> concept and results in no warnings by default.  With
> -Wattribute-alias=2 it gives the attached warnings for aliases
> with more restrictive attributes than their targets (alloc_size,
> const, leaf, malloc, nonnull, noreturn, nothrow, pure, and
> returns_nonnull).
>
> The GCC patch is the same so please let me know if there's
> something to change there.
>
>>
>> Whether nonnull attributes should be disabled when building glibc is a
>> separate question which would involve reviewing lots of functions with
>> such attributes against
>> <https://sourceware.org/glibc/wiki/Style_and_Conventions#Invalid_pointers>
>>
>> to see whether there are checks for NULL that are actually appropriate
>> under current glibc conventions but might be optimized away given such
>> attributes.  So it should clearly be kept separate from fixes to use copy
>> attributes to get better attribute consistency fot aliases.
>
> Agreed.  I certainly don't plan to undertake this project as
> part of the GCC attribute enhancement.
>
> Martin
>
Jeff Law Nov. 7, 2018, 9:59 p.m. UTC | #12
On 10/23/18 7:50 PM, Martin Sebor wrote:
> On 10/23/2018 03:53 PM, Joseph Myers wrote:
>> On Mon, 22 Oct 2018, Martin Sebor wrote:
>>
>>> between aliases and ifunc resolvers.  With -Wattribute-alias=1
>>> that reduced the number of unique instances of the warnings for
>>> a Glibc build to just 27.  Of those, all but one of
>>> the -Wattributes instances are of the form:
>>>
>>>   warning: ‘leaf’ attribute has no effect on unit local functions
>>
>> What do the macro expansions look like there?  All the places where you're
>>
>> adding "copy" attributes are for extern declarations, not static ones,
>> whereas your list of warnings seems to indicate this is appearing for
>> ifunc resolvers (which are static, but should not be copying attributes
>> from anywhere).
> 
> These must have been caused by the bug in the patch (below).
> They have cleared up with it fixed.  I'm down to just 18
> instances of a -Wmissing-attributes warning, all for string
> functions.  The cause of those is described below.
> 
>>
>>> All the -Wmissing-attributes instances are due to a missing
>>> nonnull attribute on the __EI__ kinds of functions, like:
>>>
>>>   warning: ‘__EI_vfprintf’ specifies less restrictive attribute than its
>>> target ‘vfprintf’: ‘nonnull’
>>
>> That looks like a bug in the GCC patch to me; you appear to be adding copy
>>
>> attributes in the correct place.  Note that __EI_* gets declared twice
>> (first with __asm__, second with an alias attribute), so anything related
>> to handling of such duplicate declarations might be a cause for such a
>> bug (and an indication of what you need to add a test for when fixing such
>>
>> a bug).
> 
> There was a bug in the patch, but there is also an issue in Glibc
> that made it tricky to see the problem.
> 
> The tests I had in place were too simple to catch the GCC bug:
> the problem there was that when the decl didn't have an attribute
> the type of the "template" did the check would fail without also
> considering the decl's type.  Tricky stuff!  I've added tests to
> exercise this.
> 
> The Glibc issue has to do with the use of __hidden_ver1 macro
> to declare string functions.  sysdeps/x86_64/multiarch/strcmp.c
> for instance has:
> 
>   __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
>     __attribute__ ((visibility ("hidden")));
> 
> and __redirect_strcmp is missing the nonnull attribute because
> it's #undefined in include/sys/cdefs.h.  An example of one of
> these warnings is attached.
> 
> Using strcmp instead of __redirect_strcmp would solve this but
> __redirect_strcmp should have all the same attributes as strcmp.
> But nonnull is removed from the declaration because the __nonnull
> macro that controls it is undefined in include/sys/cdefs.h.  There
> is a comment above the #undef in the header that reads:
> 
> /* The compiler will optimize based on the knowledge the parameter is
>    not NULL.  This will omit tests.  A robust implementation cannot allow
>    this so when compiling glibc itself we ignore this attribute.  */
> # undef __nonnull
> # define __nonnull(params)
> 
> I don't think this is actually true for recent versions of GCC.
> The nonnull optimization is controlled by
> -fisolate-erroneous-paths-attribute and according to the manual
> and common.opt the option is disabled by default.
> 
> But if you do want to avoid the attribute on declarations of
> these functions regardless it should be safe to add it after
> the declaration in the .c file, like so:
> 
> __hidden_ver1 (strcmp, __GI_strcmp, __redirect_strcmp)
>   __attribute__ ((visibility ("hidden"), copy (strcmp)));
> 
> That should make it straightforward to adopt the enhancement
> and experiment with -Wattribute-alias=2 to see if it does what
> you had  in mind.
> 
> The latest GCC patch with the fix mentioned above is attached.
> 
> Martin
> 
> gcc-81824.diff
> 
> 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.
> 	(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.
> 	(maybe_diag_incompatible_alias): Use OPT_Wattribute_alias_.
> 	* common.opt (-Wattribute-alias): Take an argument.
> 	(-Wno-attribute-alias): New option.
> 	* doc/extend.texi (Common Function Attributes): Document copy.
> 	(Common Variable Attributes): Same.
> 	* doc/invoke.texi (-Wmissing-attributes): Document enhancement.
> 	(-Wattribute-alias): Document new option argument.
> 
> libgomp/ChangeLog:
> 
> 	PR c/81824
> 	* libgomp.h (strong_alias, ialias, ialias_redirect): Use attribute
> 	copy.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR middle-end/81824
> 	* gcc.dg/Wattribute-alias.c: New test.
> 	* 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.
> 
[ snip ]


> +}
> +
> +/* 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;
Does this really need to be static?



> 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
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 5c95f67..c027acd 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
[ ... ]

> +
> +In C++, the warning is issued when an explicitcspecialization of a primary
"explicitcspecialization" ? :-)


Looks pretty good.  There's the explicit specialization nit and the
static vs auto question for attr_copy_visited.  Otherwise it's OK.

Jeff
Martin Sebor Nov. 9, 2018, 5:33 p.m. UTC | #13
>> +/* 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;
> Does this really need to be static?

The variable was intended to break cycles in recursive calls to
the function for self-referential applications of attribute copy
but since the attribute itself is not applied (anymore) such cycles
can no longer form.  I have removed the variable and simplified
the handlers (there are tests to verify this works correctly).

>> 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
>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>> index 5c95f67..c027acd 100644
>> --- a/gcc/doc/invoke.texi
>> +++ b/gcc/doc/invoke.texi
> [ ... ]
>
>> +
>> +In C++, the warning is issued when an explicitcspecialization of a primary
> "explicitcspecialization" ? :-)
>

Fixed.

>
> Looks pretty good.  There's the explicit specialization nit and the
> static vs auto question for attr_copy_visited.  Otherwise it's OK.

Thanks.  I've retested a revision with the changes discussed here
and committed it as r265980.

Martin
Matthew Malcomson Nov. 12, 2018, 6:29 p.m. UTC | #14
Hello Martin,

The new testcase Wattribute-alias.c fails on targets without ifunc 
support (e.g. aarch64-none-elf cross-build).

It seems that just adding a directive `{ dg-require-ifunc "" }` to the 
test file changes the test to unsupported instead of having a fail.

I don't know much about this patch so I don't know if the non-ifunc 
checks would still be useful on such targets.

Would the simple change be OK? or would it be best to split the test 
file into multiple parts to still run the other checks?

Regards,
Matthew


On 09/11/18 17:33, Martin Sebor wrote:
>>> +/* 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;
>> Does this really need to be static?
>
> The variable was intended to break cycles in recursive calls to
> the function for self-referential applications of attribute copy
> but since the attribute itself is not applied (anymore) such cycles
> can no longer form.  I have removed the variable and simplified
> the handlers (there are tests to verify this works correctly).
>
>>> 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
>>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>>> index 5c95f67..c027acd 100644
>>> --- a/gcc/doc/invoke.texi
>>> +++ b/gcc/doc/invoke.texi
>> [ ... ]
>>
>>> +
>>> +In C++, the warning is issued when an explicitcspecialization of a 
>>> primary
>> "explicitcspecialization" ? :-)
>>
>
> Fixed.
>
>>
>> Looks pretty good.  There's the explicit specialization nit and the
>> static vs auto question for attr_copy_visited.  Otherwise it's OK.
>
> Thanks.  I've retested a revision with the changes discussed here
> and committed it as r265980.
>
> Martin
Martin Sebor Nov. 12, 2018, 6:38 p.m. UTC | #15
On 11/12/2018 11:29 AM, Matthew Malcomson wrote:
> Hello Martin,
>
> The new testcase Wattribute-alias.c fails on targets without ifunc
> support (e.g. aarch64-none-elf cross-build).
>
> It seems that just adding a directive `{ dg-require-ifunc "" }` to the
> test file changes the test to unsupported instead of having a fail.
>
> I don't know much about this patch so I don't know if the non-ifunc
> checks would still be useful on such targets.
>
> Would the simple change be OK? or would it be best to split the test
> file into multiple parts to still run the other checks?

I just committed the former change earlier today but splitting
the test would have probably been a better way to go.  Thanks
for reporting it just the same!  If you would prefer to split
the test that would be fine with me.

Martin

> Regards,
> Matthew
>
>
> On 09/11/18 17:33, Martin Sebor wrote:
>>>> +/* 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;
>>> Does this really need to be static?
>>
>> The variable was intended to break cycles in recursive calls to
>> the function for self-referential applications of attribute copy
>> but since the attribute itself is not applied (anymore) such cycles
>> can no longer form.  I have removed the variable and simplified
>> the handlers (there are tests to verify this works correctly).
>>
>>>> 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
>>>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>>>> index 5c95f67..c027acd 100644
>>>> --- a/gcc/doc/invoke.texi
>>>> +++ b/gcc/doc/invoke.texi
>>> [ ... ]
>>>
>>>> +
>>>> +In C++, the warning is issued when an explicitcspecialization of a
>>>> primary
>>> "explicitcspecialization" ? :-)
>>>
>>
>> Fixed.
>>
>>>
>>> Looks pretty good.  There's the explicit specialization nit and the
>>> static vs auto question for attr_copy_visited.  Otherwise it's OK.
>>
>> Thanks.  I've retested a revision with the changes discussed here
>> and committed it as r265980.
>>
>> Martin
>
diff mbox series

Patch

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