diff mbox series

Implement the "retain" attribute

Message ID 20201022172405.r7pshjvqsmu5wbne@jozef-acer-manjaro
State New
Headers show
Series Implement the "retain" attribute | expand

Commit Message

Jozef Lawrynowicz Oct. 22, 2020, 5:24 p.m. UTC
Hi,

The attached patch adds support for the new "retain" attribute, which
can be applied to function and variable declarations, to protect them
from linker garbage collection.

Declarations with this attribute will be placed in a new, unique
section, and the ".section" assembler directive that gets output for
that new section will have the "R" flag applied. The "R" flag indicates
to GAS that the SHF_GNU_RETAIN flag should be applied to the section.
SHF_GNU_RETAIN is a GNU OSABI ELF extension.

SHF_GNU_RETAIN was discussed on the GNU gABI mailing list here:
https://sourceware.org/pipermail/gnu-gabi/2020q3/000429.html

The Binutils patch for SHF_GNU_RETAIN was discussed in the following
threads:
https://sourceware.org/pipermail/binutils/2020-September/113406.html
https://sourceware.org/pipermail/binutils/2020-September/113499.html

The latest Binutils patch is posted here:
https://sourceware.org/pipermail/binutils/2020-October/113769.html
It is awaiting final approval, but in the meantime I'm posting the GCC
part of the implementation for review.

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

Ok for trunk?

Thanks,
Jozef
From 82c4f86cde2155dd8b89ba01a2da88761586787b Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 22 Oct 2020 14:23:40 +0100
Subject: [PATCH] Implement the "retain" attribute

The "retain" attribute is used to protect the function or variable
declaration it is applied to from linker garbage collection.

Declarations with this attribute will be placed in a new, unique
section, and the ".section" assembler directive that gets output for
that new section will have the "R" flag applied. The "R" flag indicates
to GAS that the SHF_GNU_RETAIN flag should be applied to the section.
SHF_GNU_RETAIN is a GNU OSABI ELF extension.

gcc/c-family/ChangeLog:

	* c-attribs.c (handle_retain_attribute): New.

gcc/ChangeLog:

	* config.in: Regenerate.
	* configure: Regenerate.
	* configure.ac: Define HAVE_GAS_SECTION_RETAIN if the assembler
	.section directive supports the "R" flag.
	* doc/extend.texi: Document the "retain" function and variable
	attribute.
	* doc/sourcebuild.texi: Document the "retain" DejaGNU effective target
	keyword.
	* output.h (SECTION_RETAIN): Define.
	(SECTION_MACH_DEP): Adjust value.
	* tree-core.h (struct tree_decl_common): Add "retain_flag" member.
	* tree.h (DECL_RETAIN_P): New.
	* varasm.c (resolve_unique_section): Create unique section for
	DECL_RETAIN_P decls.
	(default_section_type_flags): Set the SECTION_RETAIN flag for
	sections created for DECL_RETAIN_P decls.
	(default_elf_asm_named_section): Emit "R" flag in .section directive
	for SECTION_RETAIN sections.

gcc/testsuite/ChangeLog:

	* lib/target-supports.exp (check_effective_target_retain): New.
	* c-c++-common/attr-retain-1.c: New test.
	* c-c++-common/attr-retain-2.c: New test.
	* c-c++-common/attr-retain-3.c: New test.
	* c-c++-common/attr-retain-main.inc: New test.
---
 gcc/c-family/c-attribs.c                      | 42 +++++++++++++++++++
 gcc/config.in                                 |  6 +++
 gcc/configure                                 | 36 ++++++++++++++++
 gcc/configure.ac                              |  9 ++++
 gcc/doc/extend.texi                           | 23 ++++++++++
 gcc/doc/sourcebuild.texi                      |  3 ++
 gcc/output.h                                  |  3 +-
 gcc/testsuite/c-c++-common/attr-retain-1.c    | 19 +++++++++
 gcc/testsuite/c-c++-common/attr-retain-2.c    | 21 ++++++++++
 gcc/testsuite/c-c++-common/attr-retain-3.c    | 10 +++++
 .../c-c++-common/attr-retain-main.inc         | 26 ++++++++++++
 gcc/testsuite/lib/target-supports.exp         |  9 ++++
 gcc/tree-core.h                               |  1 +
 gcc/tree.h                                    |  7 ++++
 gcc/varasm.c                                  |  8 +++-
 15 files changed, 221 insertions(+), 2 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-main.inc
diff mbox series

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index a3b2b3d58bd..72c0c61993e 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -155,6 +155,7 @@  static tree handle_designated_init_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 *);
+static tree handle_retain_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
@@ -505,6 +506,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_noinit_attribute, attr_noinit_exclusions },
   { "access",		      1, 3, false, true, true, false,
 			      handle_access_attribute, NULL },
+  { "retain",		      0, 0, true, false, false, false,
+			      handle_retain_attribute, NULL },
   { NULL,                     0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -2592,6 +2595,45 @@  handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
 	       decl, is_alias ? "alias" : "ifunc");
     }
 
+  return NULL_TREE;
+}
+
+/* Handle a "retain" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+handle_retain_attribute (tree *pnode ATTRIBUTE_UNUSED,
+			 tree   name,
+			 tree   args ATTRIBUTE_UNUSED,
+			 int    flags ATTRIBUTE_UNUSED,
+			 bool *no_add_attrs)
+{
+#if HAVE_GAS_SECTION_RETAIN
+  tree node = *pnode;
+
+  if (!targetm_common.have_named_sections)
+    {
+      warning (OPT_Wattributes, "%qE attribute not supported by this target",
+	       name);
+      *no_add_attrs = true;
+    }
+  else if (TREE_CODE (node) == FUNCTION_DECL
+	   || (VAR_P (node) && TREE_STATIC (node)))
+    {
+      TREE_USED (node) = 1;
+      DECL_PRESERVE_P (node) = 1;
+      DECL_RETAIN_P (node) = 1;
+      if (VAR_P (node))
+	DECL_READ_P (node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+#else
+  warning (OPT_Wattributes, "%qE attribute not supported by this target", name);
+  *no_add_attrs = true;
+#endif
 
   return NULL_TREE;
 }
diff --git a/gcc/config.in b/gcc/config.in
index 3657c46f349..343f2929588 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1352,6 +1352,12 @@ 
 #endif
 
 
+/* Define if your assembler supports specifying the retain section flag. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_GAS_SECTION_RETAIN
+#endif
+
+
 /* Define 0/1 if your assembler supports marking sections with SHF_MERGE flag.
    */
 #ifndef USED_FOR_TARGET
diff --git a/gcc/configure b/gcc/configure
index abff47d30eb..7abda5c13ab 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -24223,6 +24223,42 @@  cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+# Test if the assembler supports the section flag 'R' for specifying a
+# section which should not be garbage collected by the linker.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for section retain flag" >&5
+$as_echo_n "checking assembler for section retain flag... " >&6; }
+if ${gcc_cv_as_section_retain_r+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_section_retain_r=no
+  if test x$gcc_cv_as != x; then
+    $as_echo '.section foo1,"aR"
+  .byte 0' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	gcc_cv_as_section_retain_r=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_section_retain_r" >&5
+$as_echo "$gcc_cv_as_section_retain_r" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_GAS_SECTION_RETAIN `if test $gcc_cv_as_section_retain_r = yes; then echo 1; else echo 0; fi`
+_ACEOF
+
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for section merging support" >&5
 $as_echo_n "checking assembler for section merging support... " >&6; }
 if ${gcc_cv_as_shf_merge+:} false; then :
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 26a5d8e3619..035df137cf6 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -3216,6 +3216,15 @@  AC_DEFINE_UNQUOTED(HAVE_GAS_SECTION_EXCLUDE,
   [`if test $gcc_cv_as_section_exclude_e = yes || test $gcc_cv_as_section_exclude_hash = yes; then echo 1; else echo 0; fi`],
 [Define if your assembler supports specifying the exclude section flag.])
 
+# Test if the assembler supports the section flag 'R' for specifying a
+# section which should not be garbage collected by the linker.
+gcc_GAS_CHECK_FEATURE([section retain flag], gcc_cv_as_section_retain_r,,,
+ [.section foo1,"aR"
+  .byte 0])
+AC_DEFINE_UNQUOTED(HAVE_GAS_SECTION_RETAIN,
+  [`if test $gcc_cv_as_section_retain_r = yes; then echo 1; else echo 0; fi`],
+[Define if your assembler supports specifying the retain section flag.])
+
 gcc_GAS_CHECK_FEATURE(section merging support, gcc_cv_as_shf_merge,
  [elf,2,12,0], [--fatal-warnings],
  [.section .rodata.str, "aMS", @progbits, 1])
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 4a7c85822a7..25d49382fbb 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3578,6 +3578,17 @@  diagnosed.  Because a pure function cannot have any observable side
 effects it does not make sense for such a function to return @code{void}.
 Declaring such a function is diagnosed.
 
+@item retain
+@cindex @code{retain} function attribute
+The @code{retain} attribute, attached to a function, means that function must
+not be garbage collected by the linker, even if it appears unused.
+
+The function is put in a new, unique section marked with the
+@code{SHF_GNU_RETAIN} flag.  This flag is a GNU extension to the ELF format.
+
+This attribute implies, and has the same restrictions as, the @code{used}
+attribute.
+
 @item returns_nonnull
 @cindex @code{returns_nonnull} function attribute
 The @code{returns_nonnull} attribute specifies that the function
@@ -7197,6 +7208,18 @@  been fixed in GCC 4.4 but the change can lead to differences in the
 structure layout.  See the documentation of
 @option{-Wpacked-bitfield-compat} for more information.
 
+@item retain
+@cindex @code{retain} variable attribute
+The @code{retain} attribute, attached to a variable with static storage, means
+that variable must not be garbage collected by the linker, even if it appears
+unused.
+
+The variable is put in a new, unique section marked with the
+@code{SHF_GNU_RETAIN} flag.  This flag is a GNU extension to the ELF format.
+
+This attribute implies, and has the same restrictions as, the @code{used}
+attribute.
+
 @item section ("@var{section-name}")
 @cindex @code{section} variable attribute
 Normally, the compiler places the objects it generates in sections like
diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index 49316a5d0ff..85f13483d40 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -2551,6 +2551,9 @@  Target supports @option{-pie}, @option{-fpie} and @option{-fPIE}.
 @item rdynamic
 Target supports @option{-rdynamic}.
 
+@item retain
+Target supports the @code{retain} function/variable attribute.
+
 @item scalar_all_fma
 Target supports all four fused multiply-add optabs for both @code{float}
 and @code{double}.  These optabs are: @code{fma_optab}, @code{fms_optab},
diff --git a/gcc/output.h b/gcc/output.h
index eb253c50329..2a723969bd9 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -381,7 +381,8 @@  extern void no_asm_to_stream (FILE *);
 #define SECTION_COMMON   0x800000	/* contains common data */
 #define SECTION_RELRO	 0x1000000	/* data is readonly after relocation processing */
 #define SECTION_EXCLUDE  0x2000000	/* discarded by the linker */
-#define SECTION_MACH_DEP 0x4000000	/* subsequent bits reserved for target */
+#define SECTION_RETAIN   0x4000000	/* retained by the linker, SHF_GNU_RETAIN */
+#define SECTION_MACH_DEP 0x8000000	/* subsequent bits reserved for target */
 
 /* This SECTION_STYLE is used for unnamed sections that we can switch
    to using a special assembler directive.  */
diff --git a/gcc/testsuite/c-c++-common/attr-retain-1.c b/gcc/testsuite/c-c++-common/attr-retain-1.c
new file mode 100644
index 00000000000..c264eb654fc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-retain-1.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target retain } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain0\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain1\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain2\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain3\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*sretain0\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*sretain1\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*sretain3\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*fretain0\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retained_var\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retained_fn\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain4\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain5\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain6\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain7\[^,\]*,\"\\w+R" } } */
+
+/* Test the retain attribute.  */
+#include "attr-retain-main.inc"
diff --git a/gcc/testsuite/c-c++-common/attr-retain-2.c b/gcc/testsuite/c-c++-common/attr-retain-2.c
new file mode 100644
index 00000000000..a69516547ae
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-retain-2.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target retain } */
+/* { dg-options "-ffunction-sections -fdata-sections" } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain0\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain1\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain2\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain3\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*sretain0\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*sretain1\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*sretain3\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*fretain0\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retained_var\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retained_fn\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain4\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain5\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain6\[^,\]*,\"\\w+R" } } */
+/* { dg-final { scan-assembler "\\.section\t\[^,\]*retain7\[^,\]*,\"\\w+R" } } */
+
+/* Test the retain attribute when -ffunction-sections and -fdata-sections
+   options are in use.  */
+#include "attr-retain-main.inc"
diff --git a/gcc/testsuite/c-c++-common/attr-retain-3.c b/gcc/testsuite/c-c++-common/attr-retain-3.c
new file mode 100644
index 00000000000..a9f2b20183c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-retain-3.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target retain } */
+
+typedef int int_retain __attribute__((retain)); /* { dg-warning "'retain' attribute ignored" } */
+
+void
+foo (void)
+{
+  int __attribute__((retain)) a; /* { dg-warning "'retain' attribute ignored" } */
+}
diff --git a/gcc/testsuite/c-c++-common/attr-retain-main.inc b/gcc/testsuite/c-c++-common/attr-retain-main.inc
new file mode 100644
index 00000000000..0ae3b71b9ce
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-retain-main.inc
@@ -0,0 +1,26 @@ 
+/* The "retain" attribute set on this group of functions and data should protect
+   them from linker garbage collection.  */
+int __attribute__((retain)) retain0;
+int __attribute__((retain)) retain1 = 0;
+int __attribute__((retain)) retain2 = 1;
+const int __attribute__((retain)) retain3 = 2;
+static int __attribute__((retain)) sretain0;
+static int __attribute__((retain)) sretain1 = 0;
+static int __attribute__((retain)) sretain2 = 1;
+static const int __attribute__((retain)) sretain3 = 2;
+
+void __attribute__((retain)) fretain0 (void) {}
+
+int __attribute__((retain,section(".data.retained_var"))) var_retain_sec = 42;
+void __attribute__((retain,section(".text.retained_fn"))) fn_retain_sec (void) {}
+
+/* The section containing fdiscard2 should be garbage collected, but the static
+   variables defined inside it with the retain attribute applied should not.  */
+void
+fdiscard2 (void)
+{
+  static int __attribute__((retain)) retain4;
+  static int __attribute__((retain)) retain5 = 0;
+  static int __attribute__((retain)) retain6 = 1;
+  static const int __attribute__((retain)) retain7 = 2;
+}
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 8439720baea..af38d7e1155 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -380,6 +380,15 @@  proc check_effective_target_noinit { } {
     return 0
 }
 
+# The retain attribute is only supported by some targets.
+# This proc returns 1 if it's supported, 0 if it's not.
+
+proc check_effective_target_retain { } {
+    return [check_no_compiler_messages retain_available assembly {
+	int __attribute__((retain)) a = 1;
+    }]
+}
+
 ###############################
 # proc check_visibility_available { what_kind }
 ###############################
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 752bec31c3f..42e6e4b82f5 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 retain_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..731d6d8c558 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2648,6 +2648,13 @@  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 using attribute retain.
+   This indicates to compiler tools that this decl needs to be preserved,
+   and to the link editor that the section containing the decl should not be
+   garbage collected.  */
+#define DECL_RETAIN_P(DECL) \
+  DECL_COMMON_CHECK (DECL)->decl_common.retain_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..dc7c7176a10 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -464,7 +464,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
-	  || DECL_COMDAT_GROUP (decl)))
+	  || DECL_COMDAT_GROUP (decl)
+	  || DECL_RETAIN_P (decl)))
     {
       targetm.asm_out.unique_section (decl, reloc);
       if (DECL_SECTION_NAME (decl))
@@ -6619,6 +6620,9 @@  default_section_type_flags (tree decl, const char *name, int reloc)
   if (decl && VAR_P (decl) && DECL_THREAD_LOCAL_P (decl))
     flags |= SECTION_TLS | SECTION_WRITE;
 
+  if (decl && DECL_RETAIN_P (decl))
+    flags |= SECTION_RETAIN;
+
   if (strcmp (name, ".bss") == 0
       || strncmp (name, ".bss.", 5) == 0
       || strncmp (name, ".gnu.linkonce.b.", 16) == 0
@@ -6738,6 +6742,8 @@  default_elf_asm_named_section (const char *name, unsigned int flags,
       if (flags & SECTION_MACH_DEP)
 	*f++ = MACH_DEP_SECTION_ASM_FLAG;
 #endif
+      if (flags & SECTION_RETAIN)
+	*f++ = 'R';
       *f = '\0';
     }