[0/2] arm: "noinit" and "persistent" attributes

Message ID 20201027114033.2mkyhcylsaljzh5v@jozef-acer-manjaro
State Superseded
Headers show

Commit Message

Jozef Lawrynowicz Oct. 27, 2020, 11:40 a.m.
This patch series fixes behavior related to the "noinit" attribute, and
makes the MSP430 "persistent" attribute generic, so it can be used for
ARM.
These attributes are related because they are both used to mark
variables that should not be initialized by the target's runtime
startup code.

The "noinit" attribute is used for variables that are not initialized
to any value by the program loader, or the runtime startup code.
This attribute was made generic for GCC 10, whilst previously it was
only supported for MSP430.
There are a couple of issues when using it for arm-eabi:
- It does not work at -O0.
  The test for it is in the torture directory but only runs at -O2,
  which is why this bug was undetected.
- It does not work with -fdata-sections.
Patch 1 fixes these issues.

The "persistent" attribute is used for variables that *are* initialized
by the program loader, but are not initialized by the runtime startup
code. "persistent" variables are placed in a non-volatile area of
memory, which allows their value to "persist" between processor resets.

The "persistent" attribute is already implemented for msp430-elf, but
patch 2 makes it generic so it can be leveraged by ARM targets. The
".persistent" section is pervasive in linker scripts distributed ARM
devices by manufacturers such as ST and TI.

I've attached a Binutils patch that adds the ".persistent" section to
the default ARM linker script. I'll apply it alongside this GCC patch.

Side note: There is handling of a ".persistent.bss" section, however
this is Ada-specific and unrelated to the "noinit" and "persistent"
attributes. The handling of the "noinit" and "persistent" attributes
does not interfere with it.

Successfully bootstrapped/regtested x86_64-pc-linux-gnu and regtested
for arm-none-eabi.

Ok for trunk?

Jozef Lawrynowicz (2):
  Fix "noinit" attribute being ignored for -O0 and -fdata-sections
  Implement the "persistent" attribute

 gcc/c-family/c-attribs.c                      | 146 ++++++++++++------
 gcc/cgraph.h                                  |   6 +-
 gcc/cgraphunit.c                              |   2 +
 gcc/doc/extend.texi                           |  20 ++-
 gcc/lto-cgraph.c                              |   2 +
 .../c-c++-common/torture/attr-noinit-1.c      |   7 +
 .../c-c++-common/torture/attr-noinit-2.c      |   8 +
 .../c-c++-common/torture/attr-noinit-3.c      |  11 ++
 .../torture/attr-noinit-invalid.c             |  12 ++
 .../torture/attr-noinit-main.inc}             |  37 ++---
 .../c-c++-common/torture/attr-persistent-1.c  |   8 +
 .../c-c++-common/torture/attr-persistent-2.c  |   8 +
 .../c-c++-common/torture/attr-persistent-3.c  |  10 ++
 .../torture/attr-persistent-invalid.c         |  11 ++
 .../torture/attr-persistent-main.inc          |  58 +++++++
 gcc/testsuite/lib/target-supports.exp         |  15 +-
 gcc/tree-core.h                               |   1 +
 gcc/tree.h                                    |   7 +
 gcc/varasm.c                                  |  30 +++-
 19 files changed, 325 insertions(+), 74 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c
 rename gcc/testsuite/{gcc.c-torture/execute/noinit-attribute.c => c-c++-common/torture/attr-noinit-main.inc} (56%)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc

-- 
2.28.0
From 965de1985a21ef449d1b1477be566efcf3405f7e Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

Date: Mon, 26 Oct 2020 14:11:08 +0000
Subject: [PATCH 1/2] Fix "noinit" attribute being ignored for -O0 and
 -fdata-sections

Variables with the "noinit" attribute are ignored at -O0 because they
are treated like a regular .bss variable and placed in the .bss section.

With -fdata-sections they are ignored because they are not handled in
resolve_unique_section.

gcc/c-family/ChangeLog:

	* c-attribs.c (handle_noinit_attribute): Set DECL_NOINIT_P.

gcc/ChangeLog:

	* cgraph.h (symtab_node): Add noinit flag.
	* cgraphunit.c (process_function_and_variable_attributes): Set
	noinit flag of varpool node for DECL_NOINIT_P decls.
	* lto-cgraph.c (lto_output_varpool_node): Pack noinit flag
	value.
	(input_varpool_node): Unpack noinit flag value.
	* tree-core.h (struct tree_decl_common): Add noinit_flag.
	* tree.h (DECL_NOINIT_P): Define DECL_NOINIT_P.
	* varasm.c (get_variable_section): Set DECL_NOINIT_P from
	varpool node noinit flag.
	(default_elf_select_section): Check DECL_NOINIT_P instead of
	looking up attribute for .noinit section selection.
	(default_unique_section): Check DECL_NOINIT_P for .noinit
	section selection.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/noinit-attribute.c: Don't override
	optimization options set by torture test harness.
	* lib/target-supports.exp (check_effective_target_noinit): Adjust
	comment formatting.
---
 gcc/c-family/c-attribs.c                              |  4 ++++
 gcc/cgraph.h                                          |  6 +++++-
 gcc/cgraphunit.c                                      |  2 ++
 gcc/lto-cgraph.c                                      |  2 ++
 .../gcc.c-torture/execute/noinit-attribute.c          |  2 +-
 gcc/testsuite/lib/target-supports.exp                 |  2 +-
 gcc/tree-core.h                                       |  1 +
 gcc/tree.h                                            |  6 ++++++
 gcc/varasm.c                                          | 11 ++++++++---
 9 files changed, 30 insertions(+), 6 deletions(-)

-- 
2.28.0
From ccd84e8c8b1ce5e2b496d5a550b24dbdae617327 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

Date: Mon, 26 Oct 2020 17:00:31 +0000
Subject: [PATCH 2/2] Implement the "persistent" attribute

The "persistent" attribute is used for variables that are initialized
by the program loader, but are not initialized by the runtime startup
code. "persistent" variables are placed in a non-volatile area of
memory, which allows their value to "persist" between processor resets.

gcc/c-family/ChangeLog:

	* c-attribs.c (handle_special_var_sec_attribute): New.
	(handle_noinit_attribute): Remove.
	(attr_noinit_exclusions): Rename to...
	(attr_section_exclusions): ...this, and add "persistent" attribute
	exclusion.
	(c_common_attribute_table): Add "persistent" attribute.

gcc/ChangeLog:

	* cgraph.h (symtab_node): Adjust comment for noinit flag.
	* doc/extend.texi: Document the "persistent" variable attribute.
	* tree.h (DECL_NOINIT_P): Adjust comment.
	* varasm.c (bss_initializer_p): Return false for a DECL_NOINIT_P decl
	initialized to zero.
	(default_section_type_flags): Handle the ".persistent" section.
	(default_elf_select_section): Likewise.
	(default_unique_section): Likewise.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/noinit-attribute.c: Moved to...
	* c-c++-common/torture/attr-noinit-main.inc: ...here.
	* lib/target-supports.exp (check_effective_target_persistent): New.
	* c-c++-common/torture/attr-noinit-1.c: New test.
	* c-c++-common/torture/attr-noinit-2.c: New test.
	* c-c++-common/torture/attr-noinit-3.c: New test.
	* c-c++-common/torture/attr-noinit-invalid.c: New test.
	* c-c++-common/torture/attr-persistent-1.c: New test.
	* c-c++-common/torture/attr-persistent-2.c: New test.
	* c-c++-common/torture/attr-persistent-3.c: New test.
	* c-c++-common/torture/attr-persistent-invalid.c: New test.
	* c-c++-common/torture/attr-persistent-main.inc: New test.
---
 gcc/c-family/c-attribs.c                      | 150 ++++++++++++------
 gcc/cgraph.h                                  |   4 +-
 gcc/doc/extend.texi                           |  20 ++-
 .../c-c++-common/torture/attr-noinit-1.c      |   7 +
 .../c-c++-common/torture/attr-noinit-2.c      |   8 +
 .../c-c++-common/torture/attr-noinit-3.c      |  11 ++
 .../torture/attr-noinit-invalid.c             |  12 ++
 .../torture/attr-noinit-main.inc}             |  37 ++---
 .../c-c++-common/torture/attr-persistent-1.c  |   8 +
 .../c-c++-common/torture/attr-persistent-2.c  |   8 +
 .../c-c++-common/torture/attr-persistent-3.c  |  10 ++
 .../torture/attr-persistent-invalid.c         |  11 ++
 .../torture/attr-persistent-main.inc          |  58 +++++++
 gcc/testsuite/lib/target-supports.exp         |  13 ++
 gcc/tree.h                                    |   5 +-
 gcc/varasm.c                                  |  19 ++-
 16 files changed, 304 insertions(+), 77 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c
 rename gcc/testsuite/{gcc.c-torture/execute/noinit-attribute.c => c-c++-common/torture/attr-noinit-main.inc} (55%)
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-1.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-2.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-3.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c
 create mode 100644 gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 6f8288326ee..d96bd3d87c9 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -92,10 +92,10 @@ static tree handle_constructor_attribute (tree *, tree, tree, int, bool *);
 static tree handle_destructor_attribute (tree *, tree, tree, int, bool *);
 static tree handle_mode_attribute (tree *, tree, tree, int, bool *);
 static tree handle_section_attribute (tree *, tree, tree, int, bool *);
+static tree handle_special_var_sec_attribute (tree *, tree, tree, int, bool *);
 static tree handle_aligned_attribute (tree *, tree, tree, int, bool *);
 static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree,
 						  int, bool *);
-static tree handle_noinit_attribute (tree *, tree, tree, int, bool *);
 static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
 static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ;
 static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
@@ -241,9 +241,12 @@ static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
   ATTR_EXCL (NULL, false, false, false)
 };
 
-static const struct attribute_spec::exclusions attr_noinit_exclusions[] =
+/* Exclusions that apply to attributes that put declarations in specific
+   sections.  */
+static const struct attribute_spec::exclusions attr_section_exclusions[] =
 {
   ATTR_EXCL ("noinit", true, true, true),
+  ATTR_EXCL ("persistent", true, true, true),
   ATTR_EXCL ("section", true, true, true),
   ATTR_EXCL (NULL, false, false, false),
 };
@@ -320,7 +323,7 @@ const struct attribute_spec c_common_attribute_table[] =
   { "mode",                   1, 1, false,  true, false, false,
 			      handle_mode_attribute, NULL },
   { "section",                1, 1, true,  false, false, false,
-			      handle_section_attribute, attr_noinit_exclusions },
+			      handle_section_attribute, attr_section_exclusions },
   { "aligned",                0, 1, false, false, false, false,
 			      handle_aligned_attribute,
 	                      attr_aligned_exclusions },
@@ -488,7 +491,9 @@ const struct attribute_spec c_common_attribute_table[] =
   { "copy",                   1, 1, false, false, false, false,
 			      handle_copy_attribute, NULL },
   { "noinit",		      0, 0, true,  false, false, false,
-			      handle_noinit_attribute, attr_noinit_exclusions },
+			      handle_special_var_sec_attribute, attr_section_exclusions },
+  { "persistent",	      0, 0, true,  false, false, false,
+			      handle_special_var_sec_attribute, attr_section_exclusions },
   { "access",		      1, 3, false, true, true, false,
 			      handle_access_attribute, NULL },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
@@ -2343,68 +2348,117 @@ handle_weak_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
-/* Handle a "noinit" attribute; arguments as in struct
-   attribute_spec.handler.  Check whether the attribute is allowed
-   here and add the attribute to the variable decl tree or otherwise
-   issue a diagnostic.  This function checks NODE is of the expected
-   type and issues diagnostics otherwise using NAME.  If it is not of
-   the expected type *NO_ADD_ATTRS will be set to true.  */
-
+/* Handle a "noinit" or "persistent" attribute; arguments as in
+   struct attribute_spec.handler.
+   This generic handler is used for "special variable sections" that allow the
+   section name to be set using a dedicated attribute.  Additional validation
+   is performed for the specific properties of the section corresponding to the
+   attribute.
+   The ".noinit" section *is not* loaded by the program loader, and is not
+   initialized by the runtime startup code.
+   The ".persistent" section *is* loaded by the program loader, but is not
+   initialized by the runtime startup code.  */
 static tree
-handle_noinit_attribute (tree * node,
-		  tree   name,
-		  tree   args,
-		  int    flags ATTRIBUTE_UNUSED,
-		  bool *no_add_attrs)
+handle_special_var_sec_attribute (tree *node, tree name, tree args,
+				  int flags, bool *no_add_attrs)
 {
-  const char *message = NULL;
+  tree decl = *node;
   tree res = NULL_TREE;
 
-  gcc_assert (DECL_P (*node));
-  gcc_assert (args == NULL);
+  /* First perform generic validation common to "noinit" and "persistent"
+     attributes.  */
+  if (!targetm_common.have_named_sections)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"section attributes are not supported for this target");
+      goto fail;
+    }
+
+  if (!VAR_P (decl))
+    {
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		  "ignoring %qE attribute not set on a variable",
+		  name);
+      goto fail;
+    }
+
+  if (VAR_P (decl)
+      && current_function_decl != NULL_TREE
+      && !TREE_STATIC (decl))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute cannot be specified for local variables",
+		name);
+      goto fail;
+    }
 
-  if (TREE_CODE (*node) != VAR_DECL)
-    message = G_("%qE attribute only applies to variables");
+  if (VAR_P (decl)
+      && !targetm.have_tls && targetm.emutls.tmpl_section
+      && DECL_THREAD_LOCAL_P (decl))
+    {
+      error ("section of %q+D cannot be overridden", decl);
+      goto fail;
+    }
 
-  /* Check that it's possible for the variable to have a section.  */
-  else if ((TREE_STATIC (*node) || DECL_EXTERNAL (*node) || in_lto_p)
-	   && DECL_SECTION_NAME (*node))
-    message = G_("%qE attribute cannot be applied to variables "
-		 "with specific sections");
+  if (!targetm.have_switchable_bss_sections)
+    {
+      error ("%qE attribute is specific to ELF targets", name);
+      goto fail;
+    }
 
-  else if (!targetm.have_switchable_bss_sections)
-    message = G_("%qE attribute is specific to ELF targets");
+  if (TREE_READONLY (decl))
+    {
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		  "ignoring %qE attribute set on const variable",
+		  name);
+      goto fail;
+    }
 
-  if (message)
+  /* Now validate noinit/persistent individually.  */
+  if (strcmp (IDENTIFIER_POINTER (name), "noinit") == 0)
     {
-      warning (OPT_Wattributes, message, name);
-      *no_add_attrs = true;
+      if (DECL_INITIAL (decl))
+	{
+	  warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		      "ignoring %qE attribute set on initialized variable",
+		      name);
+	  goto fail;
+	}
+      /* If this var is thought to be common, then change this.  "noinit"
+	 variables must be placed in an explicit ".noinit" section.  */
+      DECL_COMMON (decl) = 0;
     }
-  else
+  else if (strcmp (IDENTIFIER_POINTER (name), "persistent") == 0)
     {
-      res = targetm.handle_generic_attribute (node, name, args, flags,
-					      no_add_attrs);
-      /* If the back end confirms the attribute can be added then continue onto
-	 final processing.  */
-      if (!(*no_add_attrs))
+      if (DECL_COMMON (decl) || DECL_INITIAL (decl) == NULL_TREE)
 	{
-	  /* If this var is thought to be common, then change this.  Common
-	     variables are assigned to sections before the backend has a
-	     chance to process them.  Do this only if the attribute is
-	     valid.  */
-	  if (DECL_COMMON (*node))
-	    DECL_COMMON (*node) = 0;
-
-	  /* Set DECL_NOINIT_P to indicate the decaration should not be
-	     initialized by the startup code.  */
-	  DECL_NOINIT_P (*node) = 1;
+	  warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		      "ignoring %qE attribute set on uninitialized variable",
+		      name);
+	  goto fail;
 	}
     }
+  else
+    gcc_unreachable ();
 
+  /* Set DECL_NOINIT_P to indicate the declaration should not be initialized
+     by the startup code.  "noinit" and "persistent" decls can be later
+     differentiated between attributes by examining DECL_INITIAL.  */
+  DECL_NOINIT_P (decl) = 1;
+
+  res = targetm.handle_generic_attribute (node, name, args, flags,
+					  no_add_attrs);
+
+  /* If the back end confirms the attribute can be added then continue onto
+     final processing.  */
+  if (!(*no_add_attrs))
+    return res;
+
+fail:
+  *no_add_attrs = true;
   return res;
 }
 
-
 /* Handle a "noplt" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 4176f761482..e127a31ce0c 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -577,8 +577,8 @@ public:
   /* Set when symbol is an IFUNC resolver.  */
   unsigned ifunc_resolver : 1;
 
-  /* Set when the symbol is decorated with the "noinit" attribute,
-     which indicates it should not be initialized by the runtime
+  /* Set when the symbol is decorated with the "noinit" or "persistent"
+     attributes, which indicate it should not be initialized by the runtime
      startup code.  */
   unsigned noinit : 1;
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 62549b02452..61ba1e0c601 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7318,9 +7318,23 @@ The @code{weak} attribute is described in
 @cindex @code{noinit} variable attribute
 Any data with the @code{noinit} attribute will not be initialized by
 the C runtime startup code, or the program loader.  Not initializing
-data in this way can reduce program startup times.  This attribute is
-specific to ELF targets and relies on the linker to place such data in
-the right location
+data in this way can reduce program startup times.
+
+This attribute is specific to ELF targets and relies on the linker
+script to place sections with the @code{.noinit} prefix in the right
+location.
+
+@item persistent
+@cindex @code{persistent} variable attribute
+Any data with the @code{persistent} attribute will not be initialized by
+the C runtime startup code, but will be initialized by the program
+loader.  This enables the value of the variable to @samp{persist}
+between processor resets.
+
+This attribute is specific to ELF targets and relies on the linker
+script to place the sections with the @code{.persistent} prefix in the
+right location.  Specifically, some type of non-volatile, writeable
+memory is required.
 
 @end table
 
diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-1.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-1.c
new file mode 100644
index 00000000000..877e7647ac9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-1.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { dg-require-effective-target noinit } */
+/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
+/* { dg-options "-save-temps" } */
+/* { dg-final { scan-assembler ".section\t.noinit,\"aw\"\n" } } */
+
+#include "attr-noinit-main.inc"
diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-2.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-2.c
new file mode 100644
index 00000000000..befa2a0bd52
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-2.c
@@ -0,0 +1,8 @@
+/* { dg-do run } */
+/* { dg-require-effective-target noinit } */
+/* { dg-options "-fdata-sections -save-temps" } */
+/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
+/* { dg-final { scan-assembler ".section\t.noinit.var_noinit,\"aw\"\n" } } */
+
+/* Test the "noinit" attribute with -fdata-sections.  */
+#include "attr-noinit-main.inc"
diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-3.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-3.c
new file mode 100644
index 00000000000..519e88a59a6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-3.c
@@ -0,0 +1,11 @@
+/* { dg-do run } */
+/* { dg-require-effective-target noinit } */
+/* { dg-options "-flto -save-temps" } */
+/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
+/* { dg-final { scan-file attr-noinit-3.ltrans0.ltrans.s ".section\t\.noinit,\"aw\"\n" } } */
+
+/* Test the "noinit" attribute with -flto.  Specifically examine the
+   final LTO assembly file, to ensure the "noinit" setting on the variable
+   hasn't been lost.  */
+#include "attr-noinit-main.inc"
+
diff --git a/gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c
new file mode 100644
index 00000000000..c3b5fffd166
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-invalid.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target noinit } */
+/* { dg-options "-Wattributes" } */
+
+/* Check warning/error messages for "noinit" attribute misuse.  */
+int __attribute__((noinit)) noinit_fn (void); /* { dg-warning "ignoring 'noinit' attribute not set on a variable" } */
+int __attribute__((section ("mysection"), noinit)) noinit_section1; /* { dg-warning "because it conflicts with attribute" } */
+int __attribute__((noinit, section ("mysection"))) noinit_section2; /* { dg-warning "because it conflicts with attribute" } */
+const int __attribute__((noinit)) noinit_const; /* { dg-warning "ignoring 'noinit' attribute set on const variable" } */
+/* { dg-error "uninitialized 'const noinit_const'" "" { target c++ } .-1 } */
+int __attribute__((noinit)) noinit_init = 42; /* { dg-warning "ignoring 'noinit' attribute set on initialized variable" } */
+void foo (void) { int __attribute__((noinit)) local_noinit; } /* { dg-error "'noinit' attribute cannot be specified for local variables" } */
diff --git a/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c b/gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc
similarity index 55%
rename from gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c
rename to gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc
index c8fa22bf38b..92cdb9b8534 100644
--- a/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c
+++ b/gcc/testsuite/c-c++-common/torture/attr-noinit-main.inc
@@ -1,16 +1,16 @@
-/* { dg-do run } */
-/* { dg-require-effective-target noinit } */
-/* { dg-options "-Wattributes" } */
-/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
-
-/* This test checks that noinit data is handled correctly.
+/* This test checks that data marked with the "noinit" attribute is handled
+   correctly.
    If data LMA != VMA (e.g. for simulating the copy of data from ROM to RAM),
    then var_init will always be re-initialized to 2 and this test will loop
-   forever.  */
+   forever, so it must be skipped for those targets.  */
 
+#ifdef __cplusplus
+extern "C" {
+#endif
 extern void _start (void) __attribute__ ((noreturn));
-extern void abort (void) __attribute__ ((noreturn));
-extern void exit (int) __attribute__ ((noreturn));
+#ifdef __cplusplus
+}
+#endif
 
 int var_common;
 int var_zero = 0;
@@ -18,24 +18,19 @@ int var_one = 1;
 int __attribute__((noinit)) var_noinit;
 int var_init = 2;
 
-int __attribute__((noinit)) func(); /* { dg-warning "attribute only applies to variables" } */
-int __attribute__((section ("mysection"), noinit)) var_section1; /* { dg-warning "because it conflicts with attribute" } */
-int __attribute__((noinit, section ("mysection"))) var_section2; /* { dg-warning "because it conflicts with attribute" } */
-
-
 int
 main (void)
 {
   /* Make sure that the C startup code has correctly initialized the ordinary variables.  */
   if (var_common != 0)
-    abort ();
+    __builtin_abort ();
 
   /* Initialized variables are not re-initialized during startup, so
      check their original values only during the first run of this
      test.  */
   if (var_init == 2)
     if (var_zero != 0 || var_one != 1)
-      abort ();
+      __builtin_abort ();
 
   switch (var_init)
     {
@@ -45,19 +40,19 @@ main (void)
       break;
 
     case 3:
-      /* Second time through - make sure that d has not been reset.  */
+      /* Second time through - make sure that var_noinit has not been reset.  */
       if (var_noinit != 3)
-	abort ();
-      exit (0);
+	__builtin_abort ();
+      __builtin_exit (0);
 
     default:
       /* Any other value for var_init is an error.  */
-      abort ();
+      __builtin_abort ();
     }
 
   /* Simulate a processor reset by calling the C startup code.  */
   _start ();
 
   /* Should never reach here.  */
-  abort ();
+  __builtin_abort ();
 }
diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-1.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-1.c
new file mode 100644
index 00000000000..72dc3c27192
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-1.c
@@ -0,0 +1,8 @@
+/* { dg-do run } */
+/* { dg-require-effective-target persistent } */
+/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
+/* { dg-options "-save-temps" } */
+/* { dg-final { scan-assembler ".section\t.persistent,\"aw\"\n" } } */
+
+/* Test the "persistent" attribute.  */
+#include "attr-persistent-main.inc"
diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-2.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-2.c
new file mode 100644
index 00000000000..a7de0d5d38b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target persistent } */
+/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
+/* { dg-options "-fdata-sections -save-temps" } */
+/* { dg-final { scan-assembler ".section\t.persistent.var_persistent,\"aw\"\n" } } */
+
+/* Test the "persistent" attribute with -fdata-sections.  */
+#include "attr-persistent-main.inc"
diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-3.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-3.c
new file mode 100644
index 00000000000..3e4fd28618d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-3.c
@@ -0,0 +1,10 @@
+/* { dg-do run } */
+/* { dg-require-effective-target persistent } */
+/* { dg-options "-flto -save-temps" } */
+/* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
+/* { dg-final { scan-file attr-persistent-3.ltrans0.ltrans.s ".section\t\.persistent,\"aw\"\n" } } */
+
+/* Test the "persistent" attribute with -flto.  Specifically examine the
+   final LTO assembly file, to ensure the "persistent" setting on the variable
+   hasn't been lost.  */
+#include "attr-persistent-main.inc"
diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c b/gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c
new file mode 100644
index 00000000000..06d9f353629
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-invalid.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target persistent } */
+/* { dg-options "-Wattributes" } */
+
+/* Check warning/error messages for "persistent" attribute misuse.  */
+int __attribute__((persistent)) persistent_fn (void); /* { dg-warning "ignoring 'persistent' attribute not set on a variable" } */
+int __attribute__((section ("mysection"), persistent)) persistent_section1 = 1; /* { dg-warning "because it conflicts with attribute" } */
+int __attribute__((persistent, section ("mysection"))) persistent_section2 = 2; /* { dg-warning "because it conflicts with attribute" } */
+const int __attribute__((persistent)) persistent_const = 3; /* { dg-warning "ignoring 'persistent' attribute set on const variable" } */
+int __attribute__((persistent)) persistent_init; /* { dg-warning "ignoring 'persistent' attribute set on uninitialized variable" } */
+void foo (void) { int __attribute__((persistent)) local_persistent = 4; } /* { dg-error "'persistent' attribute cannot be specified for local variables" } */
diff --git a/gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc b/gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc
new file mode 100644
index 00000000000..a442141e55c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/attr-persistent-main.inc
@@ -0,0 +1,58 @@
+/* This test checks that data marked with the "persistent" attribute is handled
+   correctly.
+   If data LMA != VMA (e.g. for simulating the copy of data from ROM to RAM),
+   then var_init will always be re-initialized to 2 and this test will loop
+   forever, so it must be skipped for those targets.  */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void _start (void) __attribute__ ((noreturn));
+#ifdef __cplusplus
+}
+#endif
+
+int var_common;
+int var_zero = 0;
+int var_one = 1;
+int __attribute__((persistent)) var_persistent = 2;
+int var_init = 2;
+
+int
+main (void)
+{
+  /* Make sure that the C startup code has correctly initialized the ordinary variables.  */
+  if (var_common != 0)
+    __builtin_abort ();
+
+  /* Initialized variables are not re-initialized during startup, so
+     check their original values only during the first run of this
+     test.  */
+  if (var_init == 2)
+    if (var_zero != 0 || var_one != 1 || var_persistent != 2)
+      __builtin_abort ();
+
+  switch (var_init)
+    {
+    case 2:
+      /* First time through - change all the values.  */
+      var_common = var_zero = var_one = var_persistent = var_init = 3;
+      break;
+
+    case 3:
+      /* Second time through - make sure that var_persistent has not been reset.  */
+      if (var_persistent != 3)
+	__builtin_abort ();
+      __builtin_exit (0);
+
+    default:
+      /* Any other value for var_init is an error.  */
+      __builtin_abort ();
+    }
+
+  /* Simulate a processor reset by calling the C startup code.  */
+  _start ();
+
+  /* Should never reach here.  */
+  __builtin_abort ();
+}
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 4122285cc1d..6988fa162e5 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -380,6 +380,19 @@ proc check_effective_target_noinit { } {
     return 0
 }
 
+# The "persistent" attribute is only supported by some targets.
+# This proc returns 1 if it's supported, 0 if it's not.
+
+proc check_effective_target_persistent { } {
+    if { [istarget arm*-*-eabi]
+	 || [istarget msp430-*-*] } {
+	return 1
+    }
+
+    return 0
+}
+
+
 ###############################
 # proc check_visibility_available { what_kind }
 ###############################
diff --git a/gcc/tree.h b/gcc/tree.h
index ac73f3c3af9..a06a24a009a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2648,8 +2648,9 @@ extern tree vector_element_bits_tree (const_tree);
 #define DECL_PRESERVE_P(DECL) \
   DECL_COMMON_CHECK (DECL)->decl_common.preserve_flag
 
-/* Nonzero for a decl that is decorated with the "noinit" attribute.
-   decls with this attribute are placed into a special section so they are not
+/* Nonzero for a decl that is decorated with the "noinit" or "persistent"
+   attributes.
+   decls with these attributes are placed into special sections so they are not
    initialized by the target's startup code.  */
 #define DECL_NOINIT_P(DECL) \
   DECL_COMMON_CHECK (DECL)->decl_common.noinit_flag
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 0d44342e813..64de7a7faab 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -1019,7 +1019,11 @@ bss_initializer_p (const_tree decl, bool named)
 	      || (DECL_INITIAL (decl) == error_mark_node
 	          && !in_lto_p)
 	      || (flag_zero_initialized_in_bss
-	          && initializer_zerop (DECL_INITIAL (decl)))));
+		  && initializer_zerop (DECL_INITIAL (decl))
+		  /* A decl with the "persistent" attribute applied and
+		     explicitly initialized to 0 should not be treated as a BSS
+		     variable.  */
+		  && !DECL_NOINIT_P (decl))));
 }
 
 /* Compute the alignment of variable specified by DECL.
@@ -6643,6 +6647,9 @@ default_section_type_flags (tree decl, const char *name, int reloc)
   if (strcmp (name, ".noinit") == 0)
     flags |= SECTION_WRITE | SECTION_BSS | SECTION_NOTYPE;
 
+  if (strcmp (name, ".persistent") == 0)
+    flags |= SECTION_WRITE | SECTION_NOTYPE;
+
   /* Various sections have special ELF types that the assembler will
      assign by default based on the name.  They are neither SHT_PROGBITS
      nor SHT_NOBITS, so when changing sections we don't want to print a
@@ -6986,6 +6993,11 @@ default_elf_select_section (tree decl, int reloc,
       sname = ".sdata2";
       break;
     case SECCAT_DATA:
+      if (DECL_P (decl) && DECL_NOINIT_P (decl))
+	{
+	  sname = ".persistent";
+	  break;
+	}
       return data_section;
     case SECCAT_DATA_REL:
       sname = ".data.rel";
@@ -7056,6 +7068,11 @@ default_unique_section (tree decl, int reloc)
       break;
     case SECCAT_DATA:
       prefix = one_only ? ".d" : ".data";
+      if (DECL_P (decl) && DECL_NOINIT_P (decl))
+	{
+	  prefix = one_only ? ".p" : ".persistent";
+	  break;
+	}
       break;
     case SECCAT_DATA_REL:
       prefix = one_only ? ".d.rel" : ".data.rel";
-- 
2.28.0
From 870479a3d2e7dc07441df07b4b8947d111ffb2f9 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

Date: Mon, 26 Oct 2020 17:55:16 +0000
Subject: [PATCH] support .noinit and .persistent in arm linker script

---
 ld/emulparams/armelf.sh                 | 16 +++++++++++++---
 ld/testsuite/ld-elf/noinit-sections-1.d | 11 +++++++++++
 ld/testsuite/ld-elf/noinit-sections-2.d | 12 ++++++++++++
 ld/testsuite/ld-elf/noinit-sections-2.l |  5 +++++
 ld/testsuite/ld-elf/noinit-sections.s   | 16 ++++++++++++++++
 5 files changed, 57 insertions(+), 3 deletions(-)
 create mode 100644 ld/testsuite/ld-elf/noinit-sections-1.d
 create mode 100644 ld/testsuite/ld-elf/noinit-sections-2.d
 create mode 100644 ld/testsuite/ld-elf/noinit-sections-2.l
 create mode 100644 ld/testsuite/ld-elf/noinit-sections.s

diff --git a/ld/emulparams/armelf.sh b/ld/emulparams/armelf.sh
index 24ca8ed59d..f9ccc499cc 100644
--- a/ld/emulparams/armelf.sh
+++ b/ld/emulparams/armelf.sh
@@ -10,7 +10,7 @@ OTHER_TEXT_SECTIONS='*(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)'
 OTHER_BSS_SYMBOLS="${CREATE_SHLIB+PROVIDE (}__bss_start__ = .${CREATE_SHLIB+)};"
 OTHER_BSS_END_SYMBOLS="${CREATE_SHLIB+PROVIDE (}_bss_end__ = .${CREATE_SHLIB+)}; ${CREATE_SHLIB+PROVIDE (}__bss_end__ = .${CREATE_SHLIB+)};"
 OTHER_END_SYMBOLS="${CREATE_SHLIB+PROVIDE (}__end__ = .${CREATE_SHLIB+)};"
-OTHER_SECTIONS='
+OTHER_SECTIONS="
 .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
 /* This section contains data that is not initialised during load
    *or* application reset.  */
@@ -18,11 +18,21 @@ OTHER_SECTIONS='
  {
    . = ALIGN(2);
    PROVIDE (__noinit_start = .);
-   *(.noinit)
+   *(.noinit${RELOCATING+ .noinit.* .gnu.linkonce.n.*})
    . = ALIGN(2);
    PROVIDE (__noinit_end = .);
  }
-'
+/* This section contains data that *is* initialized during load,
+   but *not* during application reset.  */
+ .persistent :
+ {
+   . = ALIGN(2);
+   PROVIDE (__persistent_start = .);
+   *(.persistent${RELOCATING+ .persistent.* .gnu.linkonce.p.*})
+   . = ALIGN(2);
+   PROVIDE (__persistent_end = .);
+ }
+"
 ATTRS_SECTIONS='.ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) }'
 OTHER_READONLY_SECTIONS="
   .ARM.extab ${RELOCATING-0} : { *(.ARM.extab${RELOCATING+* .gnu.linkonce.armextab.*}) }
diff --git a/ld/testsuite/ld-elf/noinit-sections-1.d b/ld/testsuite/ld-elf/noinit-sections-1.d
new file mode 100644
index 0000000000..7e35cd2a82
--- /dev/null
+++ b/ld/testsuite/ld-elf/noinit-sections-1.d
@@ -0,0 +1,11 @@
+#name: .noinit and .persistent sections
+#ld: --orphan-handling=warn -e _start
+#source: noinit-sections.s
+#target: arm-*-* msp430-*-elf
+#readelf: -SW
+
+#...
+ +\[ *[0-9]+\] \.noinit +NOBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +WA .*
+#...
+ +\[ *[0-9]+\] \.persistent +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +WA .*
+#pass
diff --git a/ld/testsuite/ld-elf/noinit-sections-2.d b/ld/testsuite/ld-elf/noinit-sections-2.d
new file mode 100644
index 0000000000..190c46fcf3
--- /dev/null
+++ b/ld/testsuite/ld-elf/noinit-sections-2.d
@@ -0,0 +1,12 @@
+#name: .noinit and .persistent sections (ld -r)
+#ld: --orphan-handling=warn -e _start -r
+#source: noinit-sections.s
+#target: arm-*-* msp430-*-elf
+#readelf: -SW
+#warning_output: noinit-sections-2.l
+
+#...
+ +\[ *[0-9]+\] \.noinit +NOBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +WA .*
+#...
+ +\[ *[0-9]+\] \.persistent +PROGBITS +[0-9a-f]+ +[0-9a-f]+ [0-9a-f]+ +00 +WA .*
+#pass
diff --git a/ld/testsuite/ld-elf/noinit-sections-2.l b/ld/testsuite/ld-elf/noinit-sections-2.l
new file mode 100644
index 0000000000..41ce6de01c
--- /dev/null
+++ b/ld/testsuite/ld-elf/noinit-sections-2.l
@@ -0,0 +1,5 @@
+#...
+[^:]*: warning: orphan section `.noinit.var_noinit' from \S+ being placed in section `.noinit.var_noinit'
+#...
+[^:]*: warning: orphan section `.persistent.var_persistent' from \S+ being placed in section `.persistent.var_persistent'
+#pass
diff --git a/ld/testsuite/ld-elf/noinit-sections.s b/ld/testsuite/ld-elf/noinit-sections.s
new file mode 100644
index 0000000000..793f71769e
--- /dev/null
+++ b/ld/testsuite/ld-elf/noinit-sections.s
@@ -0,0 +1,16 @@
+.section	.noinit,"aw",%nobits
+.word 0
+
+.section	.noinit.var_noinit,"aw",%nobits
+.word 0
+
+.section	.persistent,"aw"
+.word 1
+
+.section	.persistent.var_persistent,"aw"
+.word 2
+
+.text
+.global _start
+_start:
+.word 0
-- 
2.28.0

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 8283e959c89..6f8288326ee 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -2394,6 +2394,10 @@  handle_noinit_attribute (tree * node,
 	     valid.  */
 	  if (DECL_COMMON (*node))
 	    DECL_COMMON (*node) = 0;
+
+	  /* Set DECL_NOINIT_P to indicate the decaration should not be
+	     initialized by the startup code.  */
+	  DECL_NOINIT_P (*node) = 1;
 	}
     }
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 96d6cf609fe..4176f761482 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -120,7 +120,7 @@  public:
       used_from_other_partition (false), in_other_partition (false),
       address_taken (false), in_init_priority_hash (false),
       need_lto_streaming (false), offloadable (false), ifunc_resolver (false),
-      order (false), next_sharing_asm_name (NULL),
+      noinit (false), order (false), next_sharing_asm_name (NULL),
       previous_sharing_asm_name (NULL), same_comdat_group (NULL), ref_list (),
       alias_target (NULL), lto_file_data (NULL), aux (NULL),
       x_comdat_group (NULL_TREE), x_section (NULL)
@@ -577,6 +577,10 @@  public:
   /* Set when symbol is an IFUNC resolver.  */
   unsigned ifunc_resolver : 1;
 
+  /* Set when the symbol is decorated with the "noinit" attribute,
+     which indicates it should not be initialized by the runtime
+     startup code.  */
+  unsigned noinit : 1;
 
   /* Ordering of all symtab entries.  */
   int order;
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 19ae8763373..9437e7b719e 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -915,6 +915,8 @@  process_function_and_variable_attributes (cgraph_node *first,
       if (DECL_EXTERNAL (decl)
 	  && DECL_INITIAL (decl))
 	varpool_node::finalize_decl (decl);
+      if (DECL_NOINIT_P (decl))
+	vnode->noinit = true;
       if (DECL_PRESERVE_P (decl))
 	vnode->force_output = true;
       else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES (decl)))
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 93a99f3465b..8d6ba74dcad 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -631,6 +631,7 @@  lto_output_varpool_node (struct lto_simple_output_block *ob, varpool_node *node,
   bp_pack_value (&bp, node->tls_model, 3);
   bp_pack_value (&bp, node->used_by_single_function, 1);
   bp_pack_value (&bp, node->dynamically_initialized, 1);
+  bp_pack_value (&bp, node->noinit, 1);
   streamer_write_bitpack (&bp);
 
   group = node->get_comdat_group ();
@@ -1395,6 +1396,7 @@  input_varpool_node (struct lto_file_decl_data *file_data,
   node->tls_model = (enum tls_model)bp_unpack_value (&bp, 3);
   node->used_by_single_function = (enum tls_model)bp_unpack_value (&bp, 1);
   node->dynamically_initialized = bp_unpack_value (&bp, 1);
+  node->noinit = bp_unpack_value (&bp, 1);
   group = read_identifier (ib);
   if (group)
     {
diff --git a/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c b/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c
index 20a2a452e79..c8fa22bf38b 100644
--- a/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c
+++ b/gcc/testsuite/gcc.c-torture/execute/noinit-attribute.c
@@ -1,6 +1,6 @@ 
 /* { dg-do run } */
 /* { dg-require-effective-target noinit } */
-/* { dg-options "-O2" } */
+/* { dg-options "-Wattributes" } */
 /* { dg-skip-if "data LMA != VMA" { msp430-*-* } { "-mlarge" } } */
 
 /* This test checks that noinit data is handled correctly.
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 8439720baea..4122285cc1d 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -368,7 +368,7 @@  proc check_weak_override_available { } {
     return [check_weak_available]
 }
 
-# The noinit attribute is only supported by some targets.
+# The "noinit" attribute is only supported by some targets.
 # This proc returns 1 if it's supported, 0 if it's not.
 
 proc check_effective_target_noinit { } {
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 752bec31c3f..b2deb332ce5 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1691,6 +1691,7 @@  struct GTY(()) tree_decl_common {
   unsigned abstract_flag : 1;
   unsigned artificial_flag : 1;
   unsigned preserve_flag: 1;
+  unsigned noinit_flag: 1;
   unsigned debug_expr_is_from : 1;
 
   unsigned lang_flag_0 : 1;
diff --git a/gcc/tree.h b/gcc/tree.h
index f43ac9f1942..ac73f3c3af9 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2648,6 +2648,12 @@  extern tree vector_element_bits_tree (const_tree);
 #define DECL_PRESERVE_P(DECL) \
   DECL_COMMON_CHECK (DECL)->decl_common.preserve_flag
 
+/* Nonzero for a decl that is decorated with the "noinit" attribute.
+   decls with this attribute are placed into a special section so they are not
+   initialized by the target's startup code.  */
+#define DECL_NOINIT_P(DECL) \
+  DECL_COMMON_CHECK (DECL)->decl_common.noinit_flag
+
 /* For function local variables of COMPLEX and VECTOR types,
    indicates that the variable is not aliased, and that all
    modifications to the variable have been adjusted so that
diff --git a/gcc/varasm.c b/gcc/varasm.c
index ea0b59cf44a..0d44342e813 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -1156,6 +1156,7 @@  get_variable_section (tree decl, bool prefer_noswitch_p)
     {
       vnode = vnode->ultimate_alias_target ();
       decl = vnode->decl;
+      DECL_NOINIT_P (decl) = vnode->noinit;
     }
 
   if (TREE_TYPE (decl) != error_mark_node)
@@ -1203,6 +1204,7 @@  get_variable_section (tree decl, bool prefer_noswitch_p)
 
   if (ADDR_SPACE_GENERIC_P (as)
       && !DECL_THREAD_LOCAL_P (decl)
+      && !DECL_NOINIT_P (decl)
       && !(prefer_noswitch_p && targetm.have_switchable_bss_sections)
       && bss_initializer_p (decl))
     {
@@ -7004,13 +7006,11 @@  default_elf_select_section (tree decl, int reloc,
       sname = ".tdata";
       break;
     case SECCAT_BSS:
-      if (DECL_P (decl)
-	  && lookup_attribute ("noinit", DECL_ATTRIBUTES (decl)) != NULL_TREE)
+      if (DECL_P (decl) && DECL_NOINIT_P (decl))
 	{
 	  sname = ".noinit";
 	  break;
 	}
-
       if (bss_section)
 	return bss_section;
       sname = ".bss";
@@ -7073,6 +7073,11 @@  default_unique_section (tree decl, int reloc)
       prefix = one_only ? ".s" : ".sdata";
       break;
     case SECCAT_BSS:
+      if (DECL_P (decl) && DECL_NOINIT_P (decl))
+	{
+	  prefix = one_only ? ".n" : ".noinit";
+	  break;
+	}
       prefix = one_only ? ".b" : ".bss";
       break;
     case SECCAT_SBSS: