From 0521e87ee76cbcfc560289c78ae683764da0d964 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Mon, 15 Feb 2021 11:31:12 -0800
Subject: [PATCH v4] Add retain attribute to place symbols in SHF_GNU_RETAIN
section
When building Linux kernel, ld in bninutils 2.36 with GCC 11 generates
thousands of
ld: warning: orphan section `.data.event_initcall_finish' from `init/main.o' being placed in section `.data.event_initcall_finish'
ld: warning: orphan section `.data.event_initcall_start' from `init/main.o' being placed in section `.data.event_initcall_start'
ld: warning: orphan section `.data.event_initcall_level' from `init/main.o' being placed in section `.data.event_initcall_level'
Since these sections are marked with SHF_GNU_RETAIN, they are placed in
separate sections. They become orphan sections since they aren't expected
in the Linux kernel linker script. But orphan sections normally don't work
well with the Linux kernel linker script and the resulting kernel crashed.
1. Add the "retain" attribute to place symbols in separate SHF_GNU_RETAIN
sections. Issue a warning if the configured assembler/linker doesn't
support SHF_GNU_RETAIN.
2. Add -fretain-used-symbols to treat symbols with the "used" attribute
the same as the @code{retain} attribute.
gcc/
PR target/99113
* attribs.c (decl_attributes): Imply "retain" attribute for
"used" attribute when -fretain-used-symbols is used.
* common.opt: Add -fretain-used-symbols.
* toplev.c (process_options): Issue a warning for
-fretain-used-symbols without SUPPORTS_SHF_GNU_RETAIN.
* varasm.c (get_section): Replace SUPPORTS_SHF_GNU_RETAIN with
looking up the retain attribute.
(resolve_unique_section): Likewise.
(get_variable_section): Likewise.
(switch_to_section): Likewise.
* doc/extend.texi: Document the "retain" attribute.
* doc/invoke.texi: Document -fretain-used-symbols.
gcc/c-family/
PR target/99113
* c-attribs.c (c_common_attribute_table): Add the "retain"
attribute.
(handle_retain_attribute): New function.
gcc/testsuite/
PR target/99113
* c-c++-common/attr-retain-1.c: New test.
* c-c++-common/attr-retain-2.c: Likewise.
* c-c++-common/attr-retain-3.c: Likewise.
* c-c++-common/attr-retain-4.c: Likewise.
* c-c++-common/pr99113.c: Likewise.
* gcc.c-torture/compile/attr-retain-1.c: Likewise.
* gcc.c-torture/compile/attr-retain-2.c: Likewise.
* c-c++-common/attr-used.c: Pass -fretain-used-symbols if supported.
* c-c++-common/attr-used-2.c: Likewise.
* c-c++-common/attr-used-3.c: Likewise.
* c-c++-common/attr-used-4.c: Likewise.
* c-c++-common/attr-used-5.c: Likewise.
* c-c++-common/attr-used-6.c: Likewise.
* c-c++-common/attr-used-7.c: Likewise.
* c-c++-common/attr-used-8.c: Likewise.
* c-c++-common/attr-used-9.c: Likewise.
* gcc.c-torture/compile/attr-used-retain-1.c: Pass
-fretain-used-symbols.
* gcc.c-torture/compile/attr-used-retain-2.c: Likewise.
---
gcc/attribs.c | 14 ++++++++
gcc/c-family/c-attribs.c | 26 ++++++++++++++
gcc/common.opt | 4 +++
gcc/doc/extend.texi | 8 +++--
gcc/doc/invoke.texi | 5 +++
gcc/testsuite/c-c++-common/attr-retain-1.c | 16 +++++++++
gcc/testsuite/c-c++-common/attr-retain-2.c | 12 +++++++
gcc/testsuite/c-c++-common/attr-retain-3.c | 7 ++++
gcc/testsuite/c-c++-common/attr-retain-4.c | 7 ++++
gcc/testsuite/c-c++-common/attr-used-2.c | 1 +
gcc/testsuite/c-c++-common/attr-used-3.c | 1 +
gcc/testsuite/c-c++-common/attr-used-4.c | 1 +
gcc/testsuite/c-c++-common/attr-used-5.c | 1 +
gcc/testsuite/c-c++-common/attr-used-6.c | 1 +
gcc/testsuite/c-c++-common/attr-used-7.c | 1 +
gcc/testsuite/c-c++-common/attr-used-8.c | 1 +
gcc/testsuite/c-c++-common/attr-used-9.c | 1 +
gcc/testsuite/c-c++-common/attr-used.c | 1 +
gcc/testsuite/c-c++-common/pr99113.c | 7 ++++
.../gcc.c-torture/compile/attr-retain-1.c | 34 +++++++++++++++++++
.../gcc.c-torture/compile/attr-retain-2.c | 15 ++++++++
.../compile/attr-used-retain-1.c | 1 +
.../compile/attr-used-retain-2.c | 2 +-
gcc/toplev.c | 7 ++++
gcc/varasm.c | 20 ++++++-----
25 files changed, 183 insertions(+), 11 deletions(-)
create mode 100644 gcc/testsuite/c-c++-common/attr-retain-1.c
create mode 100644 gcc/testsuite/c-c++-common/attr-retain-2.c
create mode 100644 gcc/testsuite/c-c++-common/attr-retain-3.c
create mode 100644 gcc/testsuite/c-c++-common/attr-retain-4.c
create mode 100644 gcc/testsuite/c-c++-common/pr99113.c
create mode 100644 gcc/testsuite/gcc.c-torture/compile/attr-retain-1.c
create mode 100644 gcc/testsuite/gcc.c-torture/compile/attr-retain-2.c
@@ -546,6 +546,20 @@ decl_attributes (tree *node, tree attributes, int flags,
attributes = tree_cons (get_identifier ("no_icf"), NULL, attributes);
}
+ /* For -fretain-used-symbol, a "used" attribute also implies "retain". */
+ if (flag_retain_used_symbols
+ && attributes
+ && (TREE_CODE (*node) == FUNCTION_DECL
+ || (VAR_P (*node) && TREE_STATIC (*node))
+ || (TREE_CODE (*node) == TYPE_DECL))
+ && lookup_attribute ("used", attributes) != NULL
+ && lookup_attribute_spec (get_identifier ("used")))
+ {
+ if (lookup_attribute ("retain", attributes) == NULL)
+ attributes = tree_cons (get_identifier ("retain"), NULL,
+ attributes);
+ }
+
targetm.insert_attributes (*node, &attributes);
/* Note that attributes on the same declaration are not necessarily
@@ -163,6 +163,7 @@ static tree handle_objc_root_class_attribute (tree *, tree, tree, int, bool *);
static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *);
static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int,
bool *);
+static tree handle_retain_attribute (tree *, tree, tree, int, bool *);
/* Helper to define attribute exclusions. */
#define ATTR_EXCL(name, function, type, variable) \
@@ -328,6 +329,8 @@ const struct attribute_spec c_common_attribute_table[] =
handle_used_attribute, NULL },
{ "unused", 0, 0, false, false, false, false,
handle_unused_attribute, NULL },
+ { "retain", 0, 0, true, false, false, false,
+ handle_retain_attribute, NULL },
{ "externally_visible", 0, 0, true, false, false, false,
handle_externally_visible_attribute, NULL },
{ "no_reorder", 0, 0, true, false, false, false,
@@ -1564,6 +1567,29 @@ handle_unused_attribute (tree *node, tree name, tree ARG_UNUSED (args),
return NULL_TREE;
}
+/* Handle a "retain" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_retain_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
+ int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+ tree node = *pnode;
+
+ if (SUPPORTS_SHF_GNU_RETAIN
+ && (TREE_CODE (node) == FUNCTION_DECL
+ || (VAR_P (node) && TREE_STATIC (node))
+ || (TREE_CODE (node) == TYPE_DECL)))
+ ;
+ else
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored", name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
/* Handle a "externally_visible" attribute; arguments as in
struct attribute_spec.handler. */
@@ -2404,6 +2404,10 @@ frerun-loop-opt
Common Ignore
Does nothing. Preserved for backward compatibility.
+fretain-used-symbols
+Common Var(flag_retain_used_symbols)
+Make the used attribute to imply the retain attribute if supported.
+
frounding-math
Common Var(flag_rounding_math) Optimization SetByCombined
Disable optimizations that assume default FP rounding behavior.
@@ -3913,8 +3913,10 @@ When applied to a member function of a C++ class template, the
attribute also means that the function is instantiated if the
class itself is instantiated.
+@item retain
+@cindex @code{retain} function attribute
For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
-will also save the function from linker garbage collection. To support
+will save the function from linker garbage collection. To support
this behavior, functions that have not been placed in specific sections
(e.g. by the @code{section} attribute, or the @code{-ffunction-sections}
option), will be placed in new, unique sections.
@@ -7504,8 +7506,10 @@ When applied to a static data member of a C++ class template, the
attribute also means that the member is instantiated if the
class itself is instantiated.
+@item retain
+@cindex @code{retain} variable attribute
For ELF targets that support the GNU or FreeBSD OSABIs, this attribute
-will also save the variable from linker garbage collection. To support
+will save the variable from linker garbage collection. To support
this behavior, variables that have not been placed in specific sections
(e.g. by the @code{section} attribute, or the @code{-fdata-sections} option),
will be placed in new, unique sections.
@@ -16168,6 +16168,11 @@ DSOs; if your program relies on reinitialization of a DSO via
@code{dlclose} and @code{dlopen}, you can use
@option{-fno-gnu-unique}.
+@item -fretain-used-symbols
+@opindex fretain-used-symbols
+On systems with recent GNU assembler and linker, the compiler makes
+the @code{used} attribute to imply the @code{retain} attribute.
+
@item -fpcc-struct-return
@opindex fpcc-struct-return
Return ``short'' @code{struct} and @code{union} values in memory like
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-O3" } */
+
+static void function_declaration_before(void)
+ __attribute__((__used__, __retain__));
+
+static void function_declaration_before(void) {}
+
+static void function_declaration_after(void) {}
+
+static void function_declaration_after(void)
+ __attribute__((__used__, __retain__));
+
+/* { dg-final { scan-assembler "function_declaration_before" } } */
+/* { dg-final { scan-assembler "function_declaration_after" } } */
+/* { dg-final { scan-assembler "\.text.*,\"axR\"" { target R_flag_in_section } } } */
new file mode 100644
@@ -0,0 +1,12 @@
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-Wall -O2" } */
+
+static int xyzzy __attribute__((__used__, __retain__)) = 1;
+
+void foo()
+{
+ int x __attribute__((__retain__)); /* { dg-warning "attribute ignored|unused variable" } */
+}
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler "\.data.*,\"awR\"" { target R_flag_in_section } } } */
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-Wall -O2 -fcommon" } */
+
+static int xyzzy __attribute__((__used__, __retain__));
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler ",\"awR\"" { target R_flag_in_section } } } */
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-options "-Wall -O2 -fcommon" } */
+
+int xyzzy __attribute__((__used__, __retain__));
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler ",\"awR\"" { target R_flag_in_section } } } */
@@ -1,5 +1,6 @@
/* { dg-do compile } */
/* { dg-options "-Wall -O2" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
static int xyzzy __attribute__((__used__)) = 1;
@@ -1,5 +1,6 @@
/* { dg-do compile } */
/* { dg-options "-Wall -O2 -fcommon" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
static int xyzzy __attribute__((__used__));
@@ -1,5 +1,6 @@
/* { dg-do compile } */
/* { dg-options "-Wall -O2 -fcommon" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
int xyzzy __attribute__((__used__));
@@ -1,6 +1,7 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
/* { dg-options "-Wall -O2" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
struct dtv_slotinfo_list
{
@@ -1,6 +1,7 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
/* { dg-options "-Wall -O2" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
struct dtv_slotinfo_list
{
@@ -1,6 +1,7 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
/* { dg-options "-Wall -O2" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
int __attribute__((used,section(".data.foo"))) foo2 = 2;
int __attribute__((section(".data.foo"))) foo1 = 1;
@@ -1,6 +1,7 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
/* { dg-options "-Wall -O2" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
int __attribute__((section(".data.foo"))) foo1 = 1;
/* { dg-warning "'.*' without 'used' attribute and '.*' with 'used' attribute are placed in a section with the same name" "" { target R_flag_in_section } .-1 } */
@@ -1,6 +1,7 @@
/* { dg-do compile } */
/* { dg-skip-if "non-ELF target" { *-*-darwin* } } */
/* { dg-options "-Wall -O2" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
struct dtv_slotinfo_list
{
@@ -1,5 +1,6 @@
/* { dg-do compile } */
/* { dg-options "-O3" } */
+/* { dg-additional-options "-fretain-used-symbols" { target R_flag_in_section } } */
static void function_declaration_before(void) __attribute__((__used__));
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-Wall -O2" } */
+
+static int xyzzy __attribute__((__used__)) = 1;
+
+/* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler-not "\.data.*,\"awR\"" { target R_flag_in_section } } } */
new file mode 100644
@@ -0,0 +1,34 @@
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-final { scan-assembler ".text.*,\"axR\"" } } */
+/* { dg-final { scan-assembler ".bss.*,\"awR\"" } } */
+/* { dg-final { scan-assembler ".data.*,\"awR\"" } } */
+/* { dg-final { scan-assembler ".rodata.*,\"aR\"" } } */
+/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
+
+void __attribute__((used,retain)) used_fn (void) { }
+void unused_fn (void) { }
+void __attribute__((hot,used,retain)) used_hot_fn (void) { }
+void __attribute__((hot)) unused_hot_fn (void) { }
+void __attribute__((cold,used,retain)) used_cold_fn (void) { }
+void __attribute__((cold)) unused_cold_fn (void) { }
+int __attribute__((used,retain)) used_bss = 0;
+int __attribute__((used,retain)) used_data = 1;
+const int __attribute__((used,retain)) used_rodata = 2;
+int __attribute__((used,retain)) used_comm;
+static int __attribute__((used,retain)) used_lcomm;
+
+int unused_bss = 0;
+int unused_data = 1;
+const int unused_rodata = 2;
+int unused_comm;
+static int unused_lcomm;
+
+/* Test switching back to the used,retained sections. */
+void __attribute__((used,retain)) used_fn2 (void) { }
+int __attribute__((used,retain)) used_bss2 = 0;
+int __attribute__((used,retain)) used_data2 = 1;
+const int __attribute__((used,retain)) used_rodata2 = 2;
+int __attribute__((used,retain)) used_comm2;
+static int __attribute__((used,retain)) used_lcomm2;
+
+int __attribute__((used,retain,section(".data.used_foo_sec"))) used_foo = 2;
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-do compile { target R_flag_in_section } } */
+/* { dg-final { scan-assembler ".text.used_fn,\"axR\"" } } */
+/* { dg-final { scan-assembler ".text.used_fn2,\"axR\"" } } */
+/* { dg-final { scan-assembler ".bss.used_bss,\"awR\"" } } */
+/* { dg-final { scan-assembler ".bss.used_bss2,\"awR\"" } } */
+/* { dg-final { scan-assembler ".data.used_data,\"awR\"" } } */
+/* { dg-final { scan-assembler ".data.used_data2,\"awR\"" } } */
+/* { dg-final { scan-assembler ".rodata.used_rodata,\"aR\"" } } */
+/* { dg-final { scan-assembler ".rodata.used_rodata2,\"aR\"" } } */
+/* { dg-final { scan-assembler ".bss.used_lcomm,\"awR\"" { target arm-*-* } } } */
+/* { dg-final { scan-assembler ".bss.used_lcomm2,\"awR\"" { target arm-*-* } } } */
+/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
+/* { dg-options "-ffunction-sections -fdata-sections" } */
+
+#include "attr-retain-1.c"
@@ -5,6 +5,7 @@
/* { dg-final { scan-assembler ".data.*,\"awR\"" } } */
/* { dg-final { scan-assembler ".rodata.*,\"aR\"" } } */
/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
+/* { dg-options "-fretain-used-symbols" } */
void __attribute__((used)) used_fn (void) { }
void unused_fn (void) { }
@@ -11,6 +11,6 @@
/* { dg-final { scan-assembler ".bss.used_lcomm,\"awR\"" { target arm-*-* } } } */
/* { dg-final { scan-assembler ".bss.used_lcomm2,\"awR\"" { target arm-*-* } } } */
/* { dg-final { scan-assembler ".data.used_foo_sec,\"awR\"" } } */
-/* { dg-options "-ffunction-sections -fdata-sections" } */
+/* { dg-options "-ffunction-sections -fdata-sections -fretain-used-symbols" } */
#include "attr-used-retain-1.c"
@@ -1761,6 +1761,13 @@ process_options (void)
if (flag_large_source_files)
line_table->default_range_bits = 0;
+ if (flag_retain_used_symbols && !SUPPORTS_SHF_GNU_RETAIN)
+ {
+ warning_at (UNKNOWN_LOCATION, 0, "%qs is not supported for this target",
+ "-fretain-used-symbols");
+ flag_retain_used_symbols = 0;
+ }
+
/* Please don't change global_options after this point, those changes won't
be reflected in optimization_{default,current}_node. */
}
@@ -297,10 +297,10 @@ get_section (const char *name, unsigned int flags, tree decl,
slot = section_htab->find_slot_with_hash (name, htab_hash_string (name),
INSERT);
flags |= SECTION_NAMED;
- if (SUPPORTS_SHF_GNU_RETAIN
- && decl != nullptr
+ if (decl != nullptr
&& DECL_P (decl)
- && DECL_PRESERVE_P (decl))
+ && DECL_PRESERVE_P (decl)
+ && lookup_attribute ("retain", DECL_ATTRIBUTES (decl)))
flags |= SECTION_RETAIN;
if (*slot == NULL)
{
@@ -487,7 +487,8 @@ resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED,
if (DECL_SECTION_NAME (decl) == NULL
&& targetm_common.have_named_sections
&& (flag_function_or_data_sections
- || (SUPPORTS_SHF_GNU_RETAIN && DECL_PRESERVE_P (decl))
+ || (DECL_PRESERVE_P (decl)
+ && lookup_attribute ("retain", DECL_ATTRIBUTES (decl)))
|| DECL_COMDAT_GROUP (decl)))
{
targetm.asm_out.unique_section (decl, reloc);
@@ -1227,7 +1228,8 @@ get_variable_section (tree decl, bool prefer_noswitch_p)
vnode->get_constructor ();
if (DECL_COMMON (decl)
- && !(SUPPORTS_SHF_GNU_RETAIN && DECL_PRESERVE_P (decl)))
+ && !(DECL_PRESERVE_P (decl)
+ && lookup_attribute ("retain", DECL_ATTRIBUTES (decl))))
{
/* If the decl has been given an explicit section name, or it resides
in a non-generic address space, then it isn't common, and shouldn't
@@ -7761,12 +7763,14 @@ switch_to_section (section *new_section, tree decl)
{
if (in_section == new_section)
{
- if (SUPPORTS_SHF_GNU_RETAIN
- && (new_section->common.flags & SECTION_NAMED)
+ if ((new_section->common.flags & SECTION_NAMED)
&& decl != nullptr
&& DECL_P (decl)
&& (!!DECL_PRESERVE_P (decl)
- != !!(new_section->common.flags & SECTION_RETAIN)))
+ != !!(new_section->common.flags & SECTION_RETAIN))
+ && (lookup_attribute ("retain",
+ DECL_ATTRIBUTES (new_section->named.decl))
+ || lookup_attribute ("retain", DECL_ATTRIBUTES (decl))))
{
/* If the SECTION_RETAIN bit doesn't match, switch to a new
section. */
--
2.29.2