public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Support SHF_GNU_RETAIN ELF section flag
@ 2020-09-22 20:29 Jozef Lawrynowicz
  2020-09-22 23:24 ` Hans-Peter Nilsson
                   ` (4 more replies)
  0 siblings, 5 replies; 37+ messages in thread
From: Jozef Lawrynowicz @ 2020-09-22 20:29 UTC (permalink / raw)
  To: binutils

[-- Attachment #1: Type: text/plain, Size: 2653 bytes --]

The attached patch adds support for the new SHF_GNU_RETAIN ELF section
flag, which was discussed on the GNU gABI mailing list here:
https://sourceware.org/pipermail/gnu-gabi/2020q3/000429.html

The flag is GNU-specific so uses a bit in the SHF_MASKOS mask.
Its precise definition is as follows:

=======================================================================
Section Attribute Flags
+-------------------------------------+
| Name           | Value              |
+-------------------------------------+
| SHF_GNU_RETAIN | 0x200000 (1 << 21) |
+-------------------------------------+

SHF_GNU_RETAIN
  The link editor should not garbage collect the section if it is
  unused.

=======================================================================

The overall intention for this new flag is to enable a new "retain"
attribute to be applied to declarations of functions and data in the
source code. This attribute can be used to ensure the definition
associated with the declaration is present in the linked output file,
even if linker garbage collection would normally remove the containing
section because it is unused.

The new ".retain" assembler directive can be used to apply
SHF_GNU_RETAIN to a section. GCC will emit this directive when
assembling definitions of functions and data that have had the "retain"
attribute applied.

Note that there is *not* a direct mapping of SHF_GNU_RETAIN to the BFD
section flag SEC_KEEP. SEC_KEEP would prevent the user being able to
explicitly remove an SHF_GNU_RETAIN section by placing it in /DISCARD/,
which could be necessary in some situations.

I successfully regtested the patch for the Binutils, GAS and LD
testsuites for the following CPUs, applying the "-elf" suffix when
configuring:
  aarch64 alpha arc arm avr bfin bpf cr16 cris crx csky d10v d30v dlx
  epiphany fr30 frv ft32 h8300 hppa i386 ia64 ia64-vms ip2k iq2000 lm32
  m32c m32r m68hc11 m68hc12 m68k mcore mep metag microblaze mips mmix
  moxie msp430 mt nds32 nfp nios2 or1k pj ppc pru riscv rl78 rx s12z
  s390 score sh sparc spu tic6x tilegx tilepro v850 vax visium wasm32
  x86-64 x86 xc16x xgate xstormy16 xtensa z80

The new tests are passing for all targets except mmix-elf. This target
has a lot of LD failures, particularly --gc-sections doesn't appear to
have any effect. I don't know anything about the target, but I wonder if
it should be added to the hard-coded list of targets that doesn't
support --gc-sections.  I have therefore XFAIL'd the new LD tests for
this target.

I also regtested for i386-pe, to ensure there was no spill-over of the
new functionality into any non-ELF areas.

Ok to apply?

Thanks,
Jozef

[-- Attachment #2: 0001-Support-SHF_GNU_RETAIN-ELF-section-flag.patch --]
[-- Type: text/plain, Size: 41226 bytes --]

From d7f92cd05883567cf07cb8878aa60b666894197d Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Tue, 22 Sep 2020 21:00:35 +0100
Subject: [PATCH] Support SHF_GNU_RETAIN ELF section flag

The GNU-specific SHF_GNU_RETAIN ELF section flag is defined as follows:

=======================================================================
Section Attribute Flags
+-------------------------------------+
| Name           | Value              |
+-------------------------------------+
| SHF_GNU_RETAIN | 0x200000 (1 << 21) |
+-------------------------------------+

SHF_GNU_RETAIN
  The link editor should not garbage collect the section if it is
  unused.

=======================================================================

The new ".retain" assembler directive can be used to apply
SHF_GNU_RETAIN to a section.

Note that there is not a direct mapping of SHF_GNU_RETAIN to the BFD
section flag SEC_KEEP. This would prevent the user being able to
explicitly remove an SHF_GNU_RETAIN section by placing it in /DISCARD/.

bfd/ChangeLog:

2020-09-22  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* elflink.c (bfd_elf_gc_sections): gc_mark the section if
	SHF_GNU_RETAIN is set.

binutils/ChangeLog:

2020-09-22  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* NEWS: Announce SHF_GNU_RETAIN.
	* readelf.c (get_elf_section_flags): Handle SHF_GNU_RETAIN.
	* testsuite/binutils-all/readelf.exp: Run new test.
	Don't run run_dump_test when there isn't an assembler available.
	* testsuite/binutils-all/retain1.d: New test.
	* testsuite/binutils-all/retain1.s: New test.

gas/ChangeLog:

2020-09-22  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* NEWS: Announce .retain directive and SHF_GNU_RETAIN.
	* config/obj-elf.c (elf_pseudo_table): Add "retain".
	(obj_elf_retain): New.
	(obj_elf_parse_section_letters): Handle 'R' flag.
	* doc/as.texi: Document .retain directive.
	(Section): Document 'R' flag.
	* testsuite/gas/elf/elf.exp: Run new tests.
	* testsuite/gas/elf/retain1.d: New test.
	* testsuite/gas/elf/retain1.s: New test.
	* testsuite/gas/elf/retain2.d: New test.
	* testsuite/gas/elf/retain2.l: New test.
	* testsuite/gas/elf/retain2.s: New test.
	* testsuite/gas/elf/retain3.d: New test.
	* testsuite/gas/elf/retain3.s: New test.
	* testsuite/gas/elf/section10.d: Adjust test.

include/ChangeLog:

2020-09-22  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* elf/common.h (SHF_GNU_RETAIN): Define.

ld/ChangeLog:

2020-09-22  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* NEWS: Announce SHF_GNU_RETAIN.
	* ld.texi (garbage collection): Document SHF_GNU_RETAIN.
	(Output Section Discarding): Likewise.
	* testsuite/ld-elf/elf.exp: Run new tests.
	* testsuite/ld-elf/retain1.msg: New test.
	* testsuite/ld-elf/retain1.s: New test.
	* testsuite/ld-elf/retain2.d: New test.
	* testsuite/ld-elf/retain2.ld: New test.
	* testsuite/ld-elf/retain2.map: New test.
	* testsuite/ld-elf/retain3.msg: New test.
	* testsuite/ld-elf/retain3.s: New test.
	* testsuite/ld-elf/retain4.s: New test.
	* testsuite/ld-elf/retain5.s: New test.
	* testsuite/ld-elf/retain6lib.s: New test.
	* testsuite/ld-elf/retain6main.s: New test.
	* testsuite/ld-elf/retain7.msg: New test.
	* testsuite/ld-elf/retain7lib.s: New test.
	* testsuite/ld-elf/retain7main.s: New test.
---
 bfd/elflink.c                               |   3 +-
 binutils/NEWS                               |   4 +
 binutils/readelf.c                          |   4 +
 binutils/testsuite/binutils-all/readelf.exp |   6 +-
 binutils/testsuite/binutils-all/retain1.d   |  17 +++
 binutils/testsuite/binutils-all/retain1.s   | 114 ++++++++++++++++++++
 gas/NEWS                                    |   4 +
 gas/config/obj-elf.c                        |  59 ++++++++++
 gas/doc/as.texi                             |  20 ++++
 gas/testsuite/gas/elf/elf.exp               |   5 +
 gas/testsuite/gas/elf/retain1.d             |  24 +++++
 gas/testsuite/gas/elf/retain1.s             | 114 ++++++++++++++++++++
 gas/testsuite/gas/elf/retain2.d             |   3 +
 gas/testsuite/gas/elf/retain2.l             |   3 +
 gas/testsuite/gas/elf/retain2.s             |   7 ++
 gas/testsuite/gas/elf/retain3.d             |  24 +++++
 gas/testsuite/gas/elf/retain3.s             | 104 ++++++++++++++++++
 gas/testsuite/gas/elf/section10.d           |   4 +-
 include/elf/common.h                        |   1 +
 ld/NEWS                                     |   4 +
 ld/ld.texi                                  |   8 ++
 ld/testsuite/ld-elf/elf.exp                 |  38 ++++++-
 ld/testsuite/ld-elf/retain1.msg             |   9 ++
 ld/testsuite/ld-elf/retain1.s               | 114 ++++++++++++++++++++
 ld/testsuite/ld-elf/retain2.d               |   7 ++
 ld/testsuite/ld-elf/retain2.ld              |   7 ++
 ld/testsuite/ld-elf/retain2.map             |  32 ++++++
 ld/testsuite/ld-elf/retain3.msg             |   9 ++
 ld/testsuite/ld-elf/retain3.s               | 104 ++++++++++++++++++
 ld/testsuite/ld-elf/retain4.s               |  19 ++++
 ld/testsuite/ld-elf/retain5.s               |  13 +++
 ld/testsuite/ld-elf/retain6lib.s            |   6 ++
 ld/testsuite/ld-elf/retain6main.s           |   5 +
 ld/testsuite/ld-elf/retain7.msg             |   1 +
 ld/testsuite/ld-elf/retain7lib.s            |  17 +++
 ld/testsuite/ld-elf/retain7main.s           |  13 +++
 36 files changed, 921 insertions(+), 5 deletions(-)
 create mode 100644 binutils/testsuite/binutils-all/retain1.d
 create mode 100644 binutils/testsuite/binutils-all/retain1.s
 create mode 100644 gas/testsuite/gas/elf/retain1.d
 create mode 100644 gas/testsuite/gas/elf/retain1.s
 create mode 100644 gas/testsuite/gas/elf/retain2.d
 create mode 100644 gas/testsuite/gas/elf/retain2.l
 create mode 100644 gas/testsuite/gas/elf/retain2.s
 create mode 100644 gas/testsuite/gas/elf/retain3.d
 create mode 100644 gas/testsuite/gas/elf/retain3.s
 create mode 100644 ld/testsuite/ld-elf/retain1.msg
 create mode 100644 ld/testsuite/ld-elf/retain1.s
 create mode 100644 ld/testsuite/ld-elf/retain2.d
 create mode 100644 ld/testsuite/ld-elf/retain2.ld
 create mode 100644 ld/testsuite/ld-elf/retain2.map
 create mode 100644 ld/testsuite/ld-elf/retain3.msg
 create mode 100644 ld/testsuite/ld-elf/retain3.s
 create mode 100644 ld/testsuite/ld-elf/retain4.s
 create mode 100644 ld/testsuite/ld-elf/retain5.s
 create mode 100644 ld/testsuite/ld-elf/retain6lib.s
 create mode 100644 ld/testsuite/ld-elf/retain6main.s
 create mode 100644 ld/testsuite/ld-elf/retain7.msg
 create mode 100644 ld/testsuite/ld-elf/retain7lib.s
 create mode 100644 ld/testsuite/ld-elf/retain7main.s

diff --git a/bfd/elflink.c b/bfd/elflink.c
index 0e339f3c1e..6d1a1c5105 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -13977,7 +13977,8 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 			|| (elf_section_data (o)->this_hdr.sh_type
 			    == SHT_FINI_ARRAY)))
 		|| (elf_section_data (o)->this_hdr.sh_type == SHT_NOTE
-		    && elf_next_in_group (o) == NULL )))
+		    && elf_next_in_group (o) == NULL)
+		|| (elf_section_flags (o) & SHF_GNU_RETAIN)))
 	  {
 	    if (!_bfd_elf_gc_mark (info, o, gc_mark_hook))
 	      return FALSE;
diff --git a/binutils/NEWS b/binutils/NEWS
index c0dc73d7d8..6c7d3f3953 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -4,6 +4,10 @@
   symbol names.  In addition the --demangle=<style>, --no-demangle,
   --recurse-limit and --no-recurse-limit options are also now availale.
 
+* Add support for the SHF_GNU_RETAIN ELF section flag.
+  This flag specifies that the section should not be garbage collected by the
+  linker if it is unused.
+
 Changes in 2.35:
 
 * Changed readelf's display of symbol names when wide mode is not enabled.
diff --git a/binutils/readelf.c b/binutils/readelf.c
index cb4208f7b9..00502f7058 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -5977,6 +5977,8 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
       /* 24 */ { STRING_COMMA_LEN ("GNU_MBIND") },
       /* VLE specific.  */
       /* 25 */ { STRING_COMMA_LEN ("VLE") },
+      /* GNU specific.  */
+      /* 26 */ { STRING_COMMA_LEN ("GNU_RETAIN") },
     };
 
   if (do_section_details)
@@ -6010,6 +6012,7 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
 	    case SHF_EXCLUDE:		sindex = 18; break;
 	    case SHF_COMPRESSED:	sindex = 20; break;
 	    case SHF_GNU_MBIND:		sindex = 24; break;
+	    case SHF_GNU_RETAIN:	sindex = 26; break;
 
 	    default:
 	      sindex = -1;
@@ -6108,6 +6111,7 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
 	    case SHF_EXCLUDE:		*p = 'E'; break;
 	    case SHF_COMPRESSED:	*p = 'C'; break;
 	    case SHF_GNU_MBIND:		*p = 'D'; break;
+	    case SHF_GNU_RETAIN:	*p = 'R'; break;
 
 	    default:
 	      if ((filedata->file_header.e_machine == EM_X86_64
diff --git a/binutils/testsuite/binutils-all/readelf.exp b/binutils/testsuite/binutils-all/readelf.exp
index 1fb36ae5c4..6dea09e305 100644
--- a/binutils/testsuite/binutils-all/readelf.exp
+++ b/binutils/testsuite/binutils-all/readelf.exp
@@ -364,8 +364,12 @@ readelf_wi_test
 readelf_compressed_wa_test
 
 readelf_dump_test
-run_dump_test "pr25543"
 
+# These dump tests require an assembler.
+if {[which $AS] != 0} then {
+    run_dump_test "pr25543"
+    run_dump_test "retain1"
+}
 
 # PR 13482 - Check for off-by-one errors when dumping .note sections.
 if {![binutils_assemble $srcdir/$subdir/version.s tmpdir/version.o]} then {
diff --git a/binutils/testsuite/binutils-all/retain1.d b/binutils/testsuite/binutils-all/retain1.d
new file mode 100644
index 0000000000..35d7373cc1
--- /dev/null
+++ b/binutils/testsuite/binutils-all/retain1.d
@@ -0,0 +1,17 @@
+#source: retain1.s
+#readelf: -S --wide
+#name: readelf SHF_GNU_RETAIN
+
+#...
+  \[[ 0-9]+\] .bss.retain0.*WAR.*
+  \[[ 0-9]+\] .bss.retain1.*WAR.*
+  \[[ 0-9]+\] .data.retain2.*WAR.*
+  \[[ 0-9]+\] .bss.sretain0.*WAR.*
+  \[[ 0-9]+\] .bss.sretain1.*WAR.*
+  \[[ 0-9]+\] .data.sretain2.*WAR.*
+  \[[ 0-9]+\] .text.fnretain1.*AXR.*
+#...
+  \[[ 0-9]+\] .bss.lsretain0.*WAR.*
+  \[[ 0-9]+\] .bss.lsretain1.*WAR.*
+  \[[ 0-9]+\] .data.lsretain2.*WAR.*
+#pass
diff --git a/binutils/testsuite/binutils-all/retain1.s b/binutils/testsuite/binutils-all/retain1.s
new file mode 100644
index 0000000000..e799ff72ec
--- /dev/null
+++ b/binutils/testsuite/binutils-all/retain1.s
@@ -0,0 +1,114 @@
+	.global	discard0
+	.section	.bss.discard0,"aw"
+	.type	discard0, STT_OBJECT
+discard0:
+	.zero	2
+
+	.global	discard1
+	.section	.bss.discard1,"aw"
+	.type	discard1, STT_OBJECT
+discard1:
+	.zero	2
+
+	.global	discard2
+	.section	.data.discard2,"aw"
+	.type	discard2, STT_OBJECT
+discard2:
+	.word	1
+
+	.section	.bss.sdiscard0,"aw"
+	.type	sdiscard0, STT_OBJECT
+sdiscard0:
+	.zero	2
+
+	.section	.bss.sdiscard1,"aw"
+	.type	sdiscard1, STT_OBJECT
+sdiscard1:
+	.zero	2
+
+	.section	.data.sdiscard2,"aw"
+	.type	sdiscard2, STT_OBJECT
+sdiscard2:
+	.word	1
+
+	.section	.text.fndiscard0,"ax"
+	.global	fndiscard0
+	.type	fndiscard0, STT_FUNC
+fndiscard0:
+	.word 0
+
+	.global	retain0
+	.section	.bss.retain0,"aw"
+	.type	retain0, STT_OBJECT
+	.retain	.bss.retain0
+retain0:
+	.zero	2
+
+	.global	retain1
+	.section	.bss.retain1,"aw"
+	.type	retain1, STT_OBJECT
+	.retain	.bss.retain1
+retain1:
+	.zero	2
+
+	.global	retain2
+	.section	.data.retain2,"aw"
+	.type	retain2, STT_OBJECT
+	.retain	.data.retain2
+retain2:
+	.word	1
+
+	.section	.bss.sretain0,"aw"
+	.type	sretain0, STT_OBJECT
+	.retain	.bss.sretain0
+sretain0:
+	.zero	2
+
+	.section	.bss.sretain1,"aw"
+	.type	sretain1, STT_OBJECT
+	.retain	.bss.sretain1
+sretain1:
+	.zero	2
+
+	.section	.data.sretain2,"aw"
+	.type	sretain2, STT_OBJECT
+	.retain	.data.sretain2
+sretain2:
+	.word	1
+
+	.section	.text.fnretain1,"ax"
+	.global	fnretain1
+	.type	fnretain1, STT_FUNC
+	.retain	.text.fnretain1
+fnretain1:
+	.word	0
+
+	.section	.text.fndiscard2,"ax"
+	.global	fndiscard2
+	.type	fndiscard2, STT_FUNC
+fndiscard2:
+	.word	0
+
+	.section	.bss.lsretain0,"aw"
+	.type	lsretain0.2, STT_OBJECT
+	.retain	.bss.lsretain0
+lsretain0.2:
+	.zero	2
+
+	.section	.bss.lsretain1,"aw"
+	.type	lsretain1.1, STT_OBJECT
+	.retain	.bss.lsretain1
+lsretain1.1:
+	.zero	2
+
+	.section	.data.lsretain2,"aw"
+	.type	lsretain2.0, STT_OBJECT
+	.retain	.data.lsretain2
+lsretain2.0:
+	.word	1
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/gas/NEWS b/gas/NEWS
index 66afd0357b..0d63fad7b3 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -9,6 +9,10 @@
 
 * Configure with --enable-x86-used-note by default for Linux/x86.
 
+* Add support for the .retain directive, which applies the ELF SHF_GNU_RETAIN
+  flag to the specified section.  This flag specifies the section should not be
+  garbage collected by the linker if it is unused.
+
 Changes in 2.35:
 
 * X86 NaCl target support is removed.
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 9e39707801..1469a46039 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -78,6 +78,7 @@ static void obj_elf_gnu_attribute (int);
 static void obj_elf_tls_common (int);
 static void obj_elf_lcomm (int);
 static void obj_elf_struct (int);
+static void obj_elf_retain (int);
 
 static const pseudo_typeS elf_pseudo_table[] =
 {
@@ -119,6 +120,9 @@ static const pseudo_typeS elf_pseudo_table[] =
   /* A GNU extension for object attributes.  */
   {"gnu_attribute", obj_elf_gnu_attribute, 0},
 
+  /* A GNU extension for preventing linker garbage collection of sections.  */
+  {"retain", obj_elf_retain, 0},
+
   /* These are used for dwarf.  */
   {"2byte", cons, 2},
   {"4byte", cons, 4},
@@ -857,6 +861,9 @@ obj_elf_parse_section_letters (char *str, size_t len,
 	case 'd':
 	  *gnu_attr |= SHF_GNU_MBIND;
 	  break;
+	case 'R':
+	  *gnu_attr |= SHF_GNU_RETAIN;
+	  break;
 	case '?':
 	  *is_clone = TRUE;
 	  break;
@@ -1986,6 +1993,58 @@ obj_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
   obj_elf_vendor_attribute (OBJ_ATTR_GNU);
 }
 
+/* Parse a ".retain [name]" directive.
+   The SHF_GNU_RETAIN flag should be applied to the section named "name".
+   If "name" is omitted, apply the flag to the current section being
+   assembled.  */
+static void
+obj_elf_retain (int ignored ATTRIBUTE_UNUSED)
+{
+  const char *name;
+  symbolS *secsym;
+  asection *bfdsec;
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer == '\n')
+    {
+      /* Handle the case when "name" isn't specified.  */
+      name = now_seg->name;
+      if (name == NULL)
+	{
+	  as_bad (_("\".retain\" directive not within a section"));
+	  ignore_rest_of_line ();
+	  return;
+	}
+    }
+  else
+    {
+      /* The "name" argument has been given, validate it.  */
+      name = obj_elf_section_name ();
+      secsym = symbol_find (name);
+      if (secsym == NULL)
+	{
+	  as_bad (_("section '%s' has not been declared"), name);
+	  ignore_rest_of_line ();
+	  return;
+	}
+      else if (secsym != NULL
+	       && !symbol_section_p (secsym))
+	{
+	  as_bad (_("'%s' is not a section name, expected "
+		    "\".retain [name]\""), name);
+	  ignore_rest_of_line ();
+	  return;
+	}
+    }
+  demand_empty_rest_of_line ();
+
+  bfdsec = bfd_get_section_by_name (stdoutput, name);
+  if (bfdsec != NULL)
+    elf_section_flags (bfdsec) |= SHF_GNU_RETAIN;
+  else
+    as_bad (_("Couldn't find BFD section for %s\n"), name);
+}
+
 void
 elf_obj_read_begin_hook (void)
 {
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 112eaf810c..68afaa6aae 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -4468,6 +4468,7 @@ Some machine configurations provide additional directives.
 * Quad::                        @code{.quad @var{bignums}}
 * Reloc::			@code{.reloc @var{offset}, @var{reloc_name}[, @var{expression}]}
 * Rept::			@code{.rept @var{count}}
+* Retain::			@code{.retain [@var{name}]}
 * Sbttl::                       @code{.sbttl "@var{subheading}"}
 @ifset COFF
 * Scl::                         @code{.scl @var{class}}
@@ -6468,6 +6469,22 @@ is equivalent to assembling
 A count of zero is allowed, but nothing is generated.  Negative counts are not
 allowed and if encountered will be treated as if they were zero.
 
+@ifset ELF
+@node Retain
+@section @code{.retain [@var{name}]}
+
+@cindex @code{retain} directive
+@cindex SHF_GNU_RETAIN
+
+Apply the @code{SHF_GNU_RETAIN} flag to the section named @var{name}.
+If @var{name} is omitted, apply the flag to the current section being
+assembled.
+
+The @code{SHF_GNU_RETAIN} flag specifies that the section should not be
+garbage collected by the linker if it is unused.
+
+@end ifset
+
 @node Sbttl
 @section @code{.sbttl "@var{subheading}"}
 
@@ -6546,6 +6563,9 @@ ignored.  (For compatibility with the ELF version)
 section is not readable (meaningful for PE targets)
 @item 0-9
 single-digit power-of-two section alignment (GNU extension)
+@item R
+retained section (apply SHF_GNU_RETAIN to prevent linker garbage
+collection, GNU ELF extension)
 @end table
 
 If no flags are specified, the default flags depend upon the section name.  If
diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp
index 8520421ba3..3a4879e348 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -307,6 +307,11 @@ if { [is_elf_format] } then {
 
     run_dump_test "strtab"
 
+    # Tests for the .retain directive/SHF_GNU_RETAIN.
+    run_dump_test "retain1"
+    run_dump_test "retain2"
+    run_dump_test "retain3"
+
     run_dump_test "bignums"
     run_dump_test "section-symbol-redef"
     
diff --git a/gas/testsuite/gas/elf/retain1.d b/gas/testsuite/gas/elf/retain1.d
new file mode 100644
index 0000000000..439a60ca21
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain1.d
@@ -0,0 +1,24 @@
+#readelf: -S --wide
+#name: SHF_GNU_RETAIN 1
+
+#...
+  \[..\] .bss.discard0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .bss.discard1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .data.discard2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .bss.sdiscard0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .bss.sdiscard1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .data.sdiscard2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .text.fndiscard0[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  AX.*
+  \[..\] .bss.retain0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.retain1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .data.retain2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.sretain0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.sretain1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .data.sretain2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .text.fnretain1[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AXR.*
+  \[..\] .text.fndiscard2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  AX.*
+#...
+  \[..\] .bss.lsretain0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.lsretain1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .data.lsretain2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+#pass
diff --git a/gas/testsuite/gas/elf/retain1.s b/gas/testsuite/gas/elf/retain1.s
new file mode 100644
index 0000000000..e799ff72ec
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain1.s
@@ -0,0 +1,114 @@
+	.global	discard0
+	.section	.bss.discard0,"aw"
+	.type	discard0, STT_OBJECT
+discard0:
+	.zero	2
+
+	.global	discard1
+	.section	.bss.discard1,"aw"
+	.type	discard1, STT_OBJECT
+discard1:
+	.zero	2
+
+	.global	discard2
+	.section	.data.discard2,"aw"
+	.type	discard2, STT_OBJECT
+discard2:
+	.word	1
+
+	.section	.bss.sdiscard0,"aw"
+	.type	sdiscard0, STT_OBJECT
+sdiscard0:
+	.zero	2
+
+	.section	.bss.sdiscard1,"aw"
+	.type	sdiscard1, STT_OBJECT
+sdiscard1:
+	.zero	2
+
+	.section	.data.sdiscard2,"aw"
+	.type	sdiscard2, STT_OBJECT
+sdiscard2:
+	.word	1
+
+	.section	.text.fndiscard0,"ax"
+	.global	fndiscard0
+	.type	fndiscard0, STT_FUNC
+fndiscard0:
+	.word 0
+
+	.global	retain0
+	.section	.bss.retain0,"aw"
+	.type	retain0, STT_OBJECT
+	.retain	.bss.retain0
+retain0:
+	.zero	2
+
+	.global	retain1
+	.section	.bss.retain1,"aw"
+	.type	retain1, STT_OBJECT
+	.retain	.bss.retain1
+retain1:
+	.zero	2
+
+	.global	retain2
+	.section	.data.retain2,"aw"
+	.type	retain2, STT_OBJECT
+	.retain	.data.retain2
+retain2:
+	.word	1
+
+	.section	.bss.sretain0,"aw"
+	.type	sretain0, STT_OBJECT
+	.retain	.bss.sretain0
+sretain0:
+	.zero	2
+
+	.section	.bss.sretain1,"aw"
+	.type	sretain1, STT_OBJECT
+	.retain	.bss.sretain1
+sretain1:
+	.zero	2
+
+	.section	.data.sretain2,"aw"
+	.type	sretain2, STT_OBJECT
+	.retain	.data.sretain2
+sretain2:
+	.word	1
+
+	.section	.text.fnretain1,"ax"
+	.global	fnretain1
+	.type	fnretain1, STT_FUNC
+	.retain	.text.fnretain1
+fnretain1:
+	.word	0
+
+	.section	.text.fndiscard2,"ax"
+	.global	fndiscard2
+	.type	fndiscard2, STT_FUNC
+fndiscard2:
+	.word	0
+
+	.section	.bss.lsretain0,"aw"
+	.type	lsretain0.2, STT_OBJECT
+	.retain	.bss.lsretain0
+lsretain0.2:
+	.zero	2
+
+	.section	.bss.lsretain1,"aw"
+	.type	lsretain1.1, STT_OBJECT
+	.retain	.bss.lsretain1
+lsretain1.1:
+	.zero	2
+
+	.section	.data.lsretain2,"aw"
+	.type	lsretain2.0, STT_OBJECT
+	.retain	.data.lsretain2
+lsretain2.0:
+	.word	1
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/gas/testsuite/gas/elf/retain2.d b/gas/testsuite/gas/elf/retain2.d
new file mode 100644
index 0000000000..ca58b250b6
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain2.d
@@ -0,0 +1,3 @@
+#name: SHF_GNU_RETAIN 2
+#error_output: retain2.l
+#skip: hppa-*-*
diff --git a/gas/testsuite/gas/elf/retain2.l b/gas/testsuite/gas/elf/retain2.l
new file mode 100644
index 0000000000..5a44012567
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain2.l
@@ -0,0 +1,3 @@
+[^:]*: Assembler messages:
+[^:]*:1: Error: section '.data.foo' has not been declared
+[^:]*:7: Error: 'myvar' is not a section name, expected ".retain \[name\]"
diff --git a/gas/testsuite/gas/elf/retain2.s b/gas/testsuite/gas/elf/retain2.s
new file mode 100644
index 0000000000..75eb26d7f7
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain2.s
@@ -0,0 +1,7 @@
+.retain ".data.foo"
+.section .data,"aw"
+.global myvar
+.type myvar, STT_OBJECT
+myvar:
+	.byte 2
+.retain "myvar"
diff --git a/gas/testsuite/gas/elf/retain3.d b/gas/testsuite/gas/elf/retain3.d
new file mode 100644
index 0000000000..2d5ca68086
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain3.d
@@ -0,0 +1,24 @@
+#readelf: -S --wide
+#name: SHF_GNU_RETAIN 3 (use flags set on .section directive)
+
+#...
+  \[..\] .bss.discard0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .bss.discard1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .data.discard2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .bss.sdiscard0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .bss.sdiscard1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .data.sdiscard2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  WA.*
+  \[..\] .text.fndiscard0[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  AX.*
+  \[..\] .bss.retain0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.retain1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .data.retain2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.sretain0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.sretain1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .data.sretain2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .text.fnretain1[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AXR.*
+  \[..\] .text.fndiscard2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00  AX.*
+#...
+  \[..\] .bss.lsretain0[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .bss.lsretain1[ 	]+NOBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+  \[..\] .data.lsretain2[ 	]+PROGBITS[ 	]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.*
+#pass
diff --git a/gas/testsuite/gas/elf/retain3.s b/gas/testsuite/gas/elf/retain3.s
new file mode 100644
index 0000000000..065399ec6f
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain3.s
@@ -0,0 +1,104 @@
+	.global	discard0
+	.section	.bss.discard0,"aw"
+	.type	discard0, STT_OBJECT
+discard0:
+	.zero	2
+
+	.global	discard1
+	.section	.bss.discard1,"aw"
+	.type	discard1, STT_OBJECT
+discard1:
+	.zero	2
+
+	.global	discard2
+	.section	.data.discard2,"aw"
+	.type	discard2, STT_OBJECT
+discard2:
+	.word	1
+
+	.section	.bss.sdiscard0,"aw"
+	.type	sdiscard0, STT_OBJECT
+sdiscard0:
+	.zero	2
+
+	.section	.bss.sdiscard1,"aw"
+	.type	sdiscard1, STT_OBJECT
+sdiscard1:
+	.zero	2
+
+	.section	.data.sdiscard2,"aw"
+	.type	sdiscard2, STT_OBJECT
+sdiscard2:
+	.word	1
+
+	.section	.text.fndiscard0,"ax"
+	.global	fndiscard0
+	.type	fndiscard0, STT_FUNC
+fndiscard0:
+	.word 0
+
+	.global	retain0
+	.section	.bss.retain0,"awR"
+	.type	retain0, STT_OBJECT
+retain0:
+	.zero	2
+
+	.global	retain1
+	.section	.bss.retain1,"awR"
+	.type	retain1, STT_OBJECT
+retain1:
+	.zero	2
+
+	.global	retain2
+	.section	.data.retain2,"awR"
+	.type	retain2, STT_OBJECT
+retain2:
+	.word	1
+
+	.section	.bss.sretain0,"awR"
+	.type	sretain0, STT_OBJECT
+sretain0:
+	.zero	2
+
+	.section	.bss.sretain1,"awR"
+	.type	sretain1, STT_OBJECT
+sretain1:
+	.zero	2
+
+	.section	.data.sretain2,"aRw"
+	.type	sretain2, STT_OBJECT
+sretain2:
+	.word	1
+
+	.section	.text.fnretain1,"Rax"
+	.global	fnretain1
+	.type	fnretain1, STT_FUNC
+fnretain1:
+	.word	0
+
+	.section	.text.fndiscard2,"ax"
+	.global	fndiscard2
+	.type	fndiscard2, STT_FUNC
+fndiscard2:
+	.word	0
+
+	.section	.bss.lsretain0,"awR"
+	.type	lsretain0.2, STT_OBJECT
+lsretain0.2:
+	.zero	2
+
+	.section	.bss.lsretain1,"aRw"
+	.type	lsretain1.1, STT_OBJECT
+lsretain1.1:
+	.zero	2
+
+	.section	.data.lsretain2,"aRw"
+	.type	lsretain2.0, STT_OBJECT
+lsretain2.0:
+	.word	1
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/gas/testsuite/gas/elf/section10.d b/gas/testsuite/gas/elf/section10.d
index 554a791f1d..ef91d7d086 100644
--- a/gas/testsuite/gas/elf/section10.d
+++ b/gas/testsuite/gas/elf/section10.d
@@ -18,7 +18,7 @@
 #...
 [ 	]*\[.*\][ 	]+sec3
 [ 	]*PROGBITS.*
-[ 	]*\[.*fefff030\]: MERGE, STRINGS,.* EXCLUDE, OS \(.*ef00000\), PROC \(.*[3467]0000000\), UNKNOWN \(0+0ff000\)
+[ 	]*\[.*fefff030\]: MERGE, STRINGS,.* EXCLUDE, OS \(.*ed00000\), PROC \(.*[3467]0000000\), UNKNOWN \(0+0ff000\)
 #...
 [ 	]*\[.*\][ 	]+sec4
 [ 	]*LOOS\+0x11[ 	].*
@@ -26,7 +26,7 @@
 #...
 [ 	]*\[.*\][ 	]+sec5
 [ 	]*LOUSER\+0x9[ 	].*
-[ 	]*\[.*feff0000\]:.* EXCLUDE, OS \(.*ef00000\), PROC \(.*[3467]0000000\), UNKNOWN \(.*f0000\)
+[ 	]*\[.*feff0000\]:.* EXCLUDE, OS \(.*ed00000\), PROC \(.*[3467]0000000\), UNKNOWN \(.*f0000\)
 [ 	]*\[.*\][ 	]+.data.foo
 [ 	]*LOUSER\+0x7f000000[ 	].*
 [ 	]*\[0+003\]: WRITE, ALLOC
diff --git a/include/elf/common.h b/include/elf/common.h
index 805058146a..364c58a7de 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -554,6 +554,7 @@
 /* #define SHF_MASKOS	0x0F000000    *//* OS-specific semantics */
 #define SHF_MASKOS	0x0FF00000	/* New value, Oct 4, 1999 Draft */
 #define SHF_GNU_BUILD_NOTE    (1 << 20)	/* Section contains GNU BUILD ATTRIBUTE notes.  */
+#define SHF_GNU_RETAIN	      (1 << 21)	/* Section should not be garbage collected by the linker.  */
 #define SHF_MASKPROC	0xF0000000	/* Processor-specific semantics */
 
 /* This used to be implemented as a processor specific section flag.
diff --git a/ld/NEWS b/ld/NEWS
index 695348141b..1c3cc9d20a 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -13,6 +13,10 @@
   unless you are working on a project that has its own analogue
   of symbol tables that are not reflected in the ELF symtabs.
 
+* Add support for the SHF_GNU_RETAIN ELF section flag.
+  This flag specifies that the section should not be garbage collected by the
+  linker if it is unused.
+
 Changes in 2.35:
 
 * X86 NaCl target support is removed.
diff --git a/ld/ld.texi b/ld/ld.texi
index 7d961c3bb8..5daffbeb5f 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1781,6 +1781,10 @@ specified either by one of the options @samp{--entry},
 @samp{--undefined}, or @samp{--gc-keep-exported} or by a @code{ENTRY}
 command in the linker script.
 
+As a GNU extension, ELF input sections marked with the
+@code{SHF_GNU_RETAIN} flag will not be garbage collected if they are
+unused.
+
 @kindex --print-gc-sections
 @kindex --no-print-gc-sections
 @cindex garbage collection
@@ -5226,6 +5230,10 @@ The special output section name @samp{/DISCARD/} may be used to discard
 input sections.  Any input sections which are assigned to an output
 section named @samp{/DISCARD/} are not included in the output file.
 
+This can be used to discard input sections marked with the ELF flag
+@code{SHF_GNU_RETAIN}, which would otherwise have been saved from linker
+garbage collection when they are unused.
+
 Note, sections that match the @samp{/DISCARD/} output section will be
 discarded even if they are in an ELF section group which has other
 members which are not being discarded.  This is deliberate.
diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp
index c0d67d80d2..bf39e0a586 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -188,6 +188,8 @@ if { [istarget *-*-*linux*]
 	]
 }
 
+# mmix is supposed to support --gc-sections, but the option has no effect,
+# so these tests are XFAIL'd for the target.
 if [check_gc_sections_available] {
     run_ld_link_tests [list \
 	[list "__patchable_function_entries section 2" \
@@ -225,7 +227,41 @@ if [check_gc_sections_available] {
 	    {pr25490-6.s} \
 	    [list [list "readelf" {-SW} $pr25490_6_exp]] \
 	    "pr25490-6.exe"] \
-	]
+	[list "SHF_GNU_RETAIN 1" \
+	    "--gc-sections -e _start --print-gc-sections" "" "" \
+	    {retain1.s} \
+	    {{ ld retain1.msg }} \
+	    "retain1.exe"] \
+	[list "SHF_GNU_RETAIN 3 (use flags set on .section directive)" \
+	    "--gc-sections -e _start --print-gc-sections" "" "" \
+	    {retain3.s} \
+	    {{ ld retain3.msg }} \
+	    "retain3.exe"] \
+	[list "SHF_GNU_RETAIN 4 (keep sections referenced by retained sections)" \
+	    "--gc-sections -e _start --print-gc-sections" "" "" \
+	    {retain4.s} {} \
+	    "retain4.exe"] \
+	[list "SHF_GNU_RETAIN 5 (keep orphaned sections when not discarding)" \
+	    "--gc-sections -e _start --print-gc-sections --orphan-handling=place" "" "" \
+	    {retain5.s} {} \
+	    "retain5.exe"] \
+	[list "Build libretain6.a" \
+	    "" "" "" \
+	    {retain6lib.s} {} "libretain6.a"] \
+	[list "SHF_GNU_RETAIN 6 (don't pull SHF_GNU_RETAIN section out of lib)" \
+	    "--gc-sections -e _start --print-gc-sections" "-Ltmpdir -lretain6" "" \
+	    {retain6main.s} {} \
+	    "retain6.exe"] \
+	[list "Build libretain7.a" \
+	    "" "" "" \
+	    {retain7lib.s} {} "libretain7.a"] \
+	[list "SHF_GNU_RETAIN 7 (pull section out of lib required by SHF_GNU_RETAIN section)" \
+	    "--gc-sections -e _start --print-gc-sections" "-Ltmpdir -lretain7" "" \
+	    {retain7main.s} \
+	    {{ ld retain7.msg }} \
+	    "retain7.exe"] \
+	] \
+    "mmix-*-*"
 }
 
 set LDFLAGS $old_ldflags
diff --git a/ld/testsuite/ld-elf/retain1.msg b/ld/testsuite/ld-elf/retain1.msg
new file mode 100644
index 0000000000..9a265c980f
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain1.msg
@@ -0,0 +1,9 @@
+.*: removing unused section '.bss.discard0' in file.*
+.*: removing unused section '.bss.discard1' in file.*
+.*: removing unused section '.data.discard2' in file.*
+.*: removing unused section '.bss.sdiscard0' in file.*
+.*: removing unused section '.bss.sdiscard1' in file.*
+.*: removing unused section '.data.sdiscard2' in file.*
+.*: removing unused section '.text.fndiscard0' in file.*
+.*: removing unused section '.text.fndiscard2' in file.*
+#pass
diff --git a/ld/testsuite/ld-elf/retain1.s b/ld/testsuite/ld-elf/retain1.s
new file mode 100644
index 0000000000..e799ff72ec
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain1.s
@@ -0,0 +1,114 @@
+	.global	discard0
+	.section	.bss.discard0,"aw"
+	.type	discard0, STT_OBJECT
+discard0:
+	.zero	2
+
+	.global	discard1
+	.section	.bss.discard1,"aw"
+	.type	discard1, STT_OBJECT
+discard1:
+	.zero	2
+
+	.global	discard2
+	.section	.data.discard2,"aw"
+	.type	discard2, STT_OBJECT
+discard2:
+	.word	1
+
+	.section	.bss.sdiscard0,"aw"
+	.type	sdiscard0, STT_OBJECT
+sdiscard0:
+	.zero	2
+
+	.section	.bss.sdiscard1,"aw"
+	.type	sdiscard1, STT_OBJECT
+sdiscard1:
+	.zero	2
+
+	.section	.data.sdiscard2,"aw"
+	.type	sdiscard2, STT_OBJECT
+sdiscard2:
+	.word	1
+
+	.section	.text.fndiscard0,"ax"
+	.global	fndiscard0
+	.type	fndiscard0, STT_FUNC
+fndiscard0:
+	.word 0
+
+	.global	retain0
+	.section	.bss.retain0,"aw"
+	.type	retain0, STT_OBJECT
+	.retain	.bss.retain0
+retain0:
+	.zero	2
+
+	.global	retain1
+	.section	.bss.retain1,"aw"
+	.type	retain1, STT_OBJECT
+	.retain	.bss.retain1
+retain1:
+	.zero	2
+
+	.global	retain2
+	.section	.data.retain2,"aw"
+	.type	retain2, STT_OBJECT
+	.retain	.data.retain2
+retain2:
+	.word	1
+
+	.section	.bss.sretain0,"aw"
+	.type	sretain0, STT_OBJECT
+	.retain	.bss.sretain0
+sretain0:
+	.zero	2
+
+	.section	.bss.sretain1,"aw"
+	.type	sretain1, STT_OBJECT
+	.retain	.bss.sretain1
+sretain1:
+	.zero	2
+
+	.section	.data.sretain2,"aw"
+	.type	sretain2, STT_OBJECT
+	.retain	.data.sretain2
+sretain2:
+	.word	1
+
+	.section	.text.fnretain1,"ax"
+	.global	fnretain1
+	.type	fnretain1, STT_FUNC
+	.retain	.text.fnretain1
+fnretain1:
+	.word	0
+
+	.section	.text.fndiscard2,"ax"
+	.global	fndiscard2
+	.type	fndiscard2, STT_FUNC
+fndiscard2:
+	.word	0
+
+	.section	.bss.lsretain0,"aw"
+	.type	lsretain0.2, STT_OBJECT
+	.retain	.bss.lsretain0
+lsretain0.2:
+	.zero	2
+
+	.section	.bss.lsretain1,"aw"
+	.type	lsretain1.1, STT_OBJECT
+	.retain	.bss.lsretain1
+lsretain1.1:
+	.zero	2
+
+	.section	.data.lsretain2,"aw"
+	.type	lsretain2.0, STT_OBJECT
+	.retain	.data.lsretain2
+lsretain2.0:
+	.word	1
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/ld/testsuite/ld-elf/retain2.d b/ld/testsuite/ld-elf/retain2.d
new file mode 100644
index 0000000000..941b002948
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain2.d
@@ -0,0 +1,7 @@
+# Test that sections marked with SHF_GNU_RETAIN can be removed by placing them
+# in /DISCARD/.
+# source: retain1.s
+# ld: -e _start -Map=retain2.map --gc-sections --script=retain2.ld
+# map: retain2.map
+# skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-*
+# xfail: mmix-*-*
diff --git a/ld/testsuite/ld-elf/retain2.ld b/ld/testsuite/ld-elf/retain2.ld
new file mode 100644
index 0000000000..8ef982753c
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain2.ld
@@ -0,0 +1,7 @@
+SECTIONS
+{
+  /DISCARD/ :
+  {
+    *(.text.fnretain1)
+  }
+}
diff --git a/ld/testsuite/ld-elf/retain2.map b/ld/testsuite/ld-elf/retain2.map
new file mode 100644
index 0000000000..4028aa1f58
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain2.map
@@ -0,0 +1,32 @@
+# Test that .text.fnretain1, which has the SHF_GNU_RETAIN flag, can still be
+# explicitly discarded from the output file.
+
+#...
+Discarded input sections
+
+ .text.*
+#...
+ .data.*
+#...
+ .bss.*
+#...
+ .bss.discard0.*
+#...
+ .bss.discard1.*
+#...
+ .data.discard2.*
+#...
+ .bss.sdiscard0.*
+#...
+ .bss.sdiscard1.*
+#...
+ .data.sdiscard2.*
+#...
+ .text.fndiscard0.*
+#...
+ .text.fnretain1.*
+#...
+ .text.fndiscard2.*
+#...
+Memory Configuration
+#pass
diff --git a/ld/testsuite/ld-elf/retain3.msg b/ld/testsuite/ld-elf/retain3.msg
new file mode 100644
index 0000000000..9a265c980f
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain3.msg
@@ -0,0 +1,9 @@
+.*: removing unused section '.bss.discard0' in file.*
+.*: removing unused section '.bss.discard1' in file.*
+.*: removing unused section '.data.discard2' in file.*
+.*: removing unused section '.bss.sdiscard0' in file.*
+.*: removing unused section '.bss.sdiscard1' in file.*
+.*: removing unused section '.data.sdiscard2' in file.*
+.*: removing unused section '.text.fndiscard0' in file.*
+.*: removing unused section '.text.fndiscard2' in file.*
+#pass
diff --git a/ld/testsuite/ld-elf/retain3.s b/ld/testsuite/ld-elf/retain3.s
new file mode 100644
index 0000000000..065399ec6f
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain3.s
@@ -0,0 +1,104 @@
+	.global	discard0
+	.section	.bss.discard0,"aw"
+	.type	discard0, STT_OBJECT
+discard0:
+	.zero	2
+
+	.global	discard1
+	.section	.bss.discard1,"aw"
+	.type	discard1, STT_OBJECT
+discard1:
+	.zero	2
+
+	.global	discard2
+	.section	.data.discard2,"aw"
+	.type	discard2, STT_OBJECT
+discard2:
+	.word	1
+
+	.section	.bss.sdiscard0,"aw"
+	.type	sdiscard0, STT_OBJECT
+sdiscard0:
+	.zero	2
+
+	.section	.bss.sdiscard1,"aw"
+	.type	sdiscard1, STT_OBJECT
+sdiscard1:
+	.zero	2
+
+	.section	.data.sdiscard2,"aw"
+	.type	sdiscard2, STT_OBJECT
+sdiscard2:
+	.word	1
+
+	.section	.text.fndiscard0,"ax"
+	.global	fndiscard0
+	.type	fndiscard0, STT_FUNC
+fndiscard0:
+	.word 0
+
+	.global	retain0
+	.section	.bss.retain0,"awR"
+	.type	retain0, STT_OBJECT
+retain0:
+	.zero	2
+
+	.global	retain1
+	.section	.bss.retain1,"awR"
+	.type	retain1, STT_OBJECT
+retain1:
+	.zero	2
+
+	.global	retain2
+	.section	.data.retain2,"awR"
+	.type	retain2, STT_OBJECT
+retain2:
+	.word	1
+
+	.section	.bss.sretain0,"awR"
+	.type	sretain0, STT_OBJECT
+sretain0:
+	.zero	2
+
+	.section	.bss.sretain1,"awR"
+	.type	sretain1, STT_OBJECT
+sretain1:
+	.zero	2
+
+	.section	.data.sretain2,"aRw"
+	.type	sretain2, STT_OBJECT
+sretain2:
+	.word	1
+
+	.section	.text.fnretain1,"Rax"
+	.global	fnretain1
+	.type	fnretain1, STT_FUNC
+fnretain1:
+	.word	0
+
+	.section	.text.fndiscard2,"ax"
+	.global	fndiscard2
+	.type	fndiscard2, STT_FUNC
+fndiscard2:
+	.word	0
+
+	.section	.bss.lsretain0,"awR"
+	.type	lsretain0.2, STT_OBJECT
+lsretain0.2:
+	.zero	2
+
+	.section	.bss.lsretain1,"aRw"
+	.type	lsretain1.1, STT_OBJECT
+lsretain1.1:
+	.zero	2
+
+	.section	.data.lsretain2,"aRw"
+	.type	lsretain2.0, STT_OBJECT
+lsretain2.0:
+	.word	1
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/ld/testsuite/ld-elf/retain4.s b/ld/testsuite/ld-elf/retain4.s
new file mode 100644
index 0000000000..080a5dfc2e
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain4.s
@@ -0,0 +1,19 @@
+/* The retention of bar should also prevent foo from being gc'ed, since bar
+   references foo.  */
+	.section	.text.foo,"ax"
+	.global	foo
+	.type	foo, STT_FUNC
+foo:
+	.word 0
+
+	.section	.text.bar,"axR"
+	.global	bar
+	.type	bar, STT_FUNC
+bar:
+	.long foo
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/ld/testsuite/ld-elf/retain5.s b/ld/testsuite/ld-elf/retain5.s
new file mode 100644
index 0000000000..5ffadad694
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain5.s
@@ -0,0 +1,13 @@
+/* A section which doesn't match any linker script input section rules but
+   has SHF_GNU_RETAIN applied should not be garbage collected.  */
+	.section	.orphaned_section,"axR"
+	.global	foo
+	.type	foo, STT_FUNC
+foo:
+	.word 0
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/ld/testsuite/ld-elf/retain6lib.s b/ld/testsuite/ld-elf/retain6lib.s
new file mode 100644
index 0000000000..4de7adea3d
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain6lib.s
@@ -0,0 +1,6 @@
+/* The link will fail if foo is included because undefined_sym is not defined.  */
+	.section	.text.foo,"axR"
+	.global	foo
+	.type	foo, STT_FUNC
+foo:
+	.long undefined_sym
diff --git a/ld/testsuite/ld-elf/retain6main.s b/ld/testsuite/ld-elf/retain6main.s
new file mode 100644
index 0000000000..7c722481e8
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain6main.s
@@ -0,0 +1,5 @@
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
diff --git a/ld/testsuite/ld-elf/retain7.msg b/ld/testsuite/ld-elf/retain7.msg
new file mode 100644
index 0000000000..c21e3b9d75
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain7.msg
@@ -0,0 +1 @@
+.*: removing unused section '.text.discard_from_lib' in file.*
diff --git a/ld/testsuite/ld-elf/retain7lib.s b/ld/testsuite/ld-elf/retain7lib.s
new file mode 100644
index 0000000000..2aebf32bd0
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain7lib.s
@@ -0,0 +1,17 @@
+	.section	.text.bar,"ax"
+	.global	bar
+	.type	bar, STT_FUNC
+bar:
+	.word 0
+
+	.section	.text.retain_from_lib,"axR"
+	.global	retain_from_lib
+	.type	retain_from_lib, STT_FUNC
+retain_from_lib:
+	.word 0
+
+	.section	.text.discard_from_lib,"ax"
+	.global	discard_from_lib
+	.type	discard_from_lib, STT_FUNC
+discard_from_lib:
+	.word 0
diff --git a/ld/testsuite/ld-elf/retain7main.s b/ld/testsuite/ld-elf/retain7main.s
new file mode 100644
index 0000000000..d8ce70e718
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain7main.s
@@ -0,0 +1,13 @@
+/* Undefined symbol reference in retained section .text.foo requires symbol
+   definition to be pulled out of library.  */
+	.section	.text.foo,"axR"
+	.global	foo
+	.type	foo, STT_FUNC
+foo:
+	.long bar
+
+	.section	.text._start,"ax"
+	.global	_start
+	.type	_start, STT_FUNC
+_start:
+	.word 0
-- 
2.28.0


^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2020-09-29 14:11 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-22 20:29 [PATCH] Support SHF_GNU_RETAIN ELF section flag Jozef Lawrynowicz
2020-09-22 23:24 ` Hans-Peter Nilsson
2020-09-22 23:58 ` H.J. Lu
2020-09-23  1:09 ` Fangrui Song
2020-09-23  9:58   ` Jozef Lawrynowicz
2020-09-23 13:39     ` H.J. Lu
2020-09-23 13:51       ` Michael Matz
2020-09-23 16:52         ` Jozef Lawrynowicz
2020-09-23 17:13           ` H.J. Lu
2020-09-23 18:47             ` Jozef Lawrynowicz
2020-09-23 19:03               ` H.J. Lu
2020-09-23 20:04                 ` Jozef Lawrynowicz
2020-09-23 20:17                   ` H.J. Lu
2020-09-23 23:29                     ` Fangrui Song
2020-09-24 11:39                       ` Jozef Lawrynowicz
2020-09-24 19:06                         ` Fangrui Song
2020-09-24 13:27                       ` Michael Matz
2020-09-24 13:18                     ` H.J. Lu
2020-09-24 13:49                       ` Jozef Lawrynowicz
2020-09-24 13:59                         ` H.J. Lu
2020-09-24 16:56                           ` Jozef Lawrynowicz
2020-09-24 17:04                             ` H.J. Lu
2020-09-24 17:18                               ` Jozef Lawrynowicz
2020-09-24 17:37                                 ` H.J. Lu
2020-09-23 12:13 ` Jozef Lawrynowicz
2020-09-23 13:59   ` Alan Modra
2020-09-23 16:54     ` Jozef Lawrynowicz
2020-09-28 11:35 ` Pedro Alves
2020-09-28 12:28   ` Jozef Lawrynowicz
2020-09-28 14:46     ` Pedro Alves
2020-09-29 13:18       ` Michael Matz
2020-09-29 13:22         ` Jozef Lawrynowicz
2020-09-29 13:48         ` Pedro Alves
2020-09-29 13:55           ` Michael Matz
2020-09-29 14:04             ` Pedro Alves
2020-09-29 14:10               ` Michael Matz
2020-09-29 14:11                 ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).