[PATCH] Support SHF_GNU_RETAIN ELF section flag
Jozef Lawrynowicz
jozef.l@mittosystems.com
Wed Sep 23 12:13:48 GMT 2020
Attached a revised patch:
- Removed mmix-*-* XFAILs
- Fixed SHF_GNU_RETAIN being clobbered when using the .section directive
without the "R" flag, after the section has previously been marked
with SHF_GNU_RETAIN. Added tests for this behavior.
Thanks,
Jozef
On Tue, Sep 22, 2020 at 09:29:33PM +0100, Jozef Lawrynowicz wrote:
> 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
-------------- next part --------------
>From 862a04415e1b490b4561fe5a44c9fa4120c50825 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. SEC_KEEP 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 | 71 +++++++++++-
gas/doc/as.texi | 20 ++++
gas/testsuite/gas/elf/elf.exp | 7 ++
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/retain4.d | 6 ++
gas/testsuite/gas/elf/retain4.s | 20 ++++
gas/testsuite/gas/elf/retain5.d | 6 ++
gas/testsuite/gas/elf/retain5.s | 18 ++++
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 | 33 ++++++
ld/testsuite/ld-elf/retain1.msg | 9 ++
ld/testsuite/ld-elf/retain1.s | 114 ++++++++++++++++++++
ld/testsuite/ld-elf/retain2.d | 6 ++
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 +++
40 files changed, 977 insertions(+), 7 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 gas/testsuite/gas/elf/retain4.d
create mode 100644 gas/testsuite/gas/elf/retain4.s
create mode 100644 gas/testsuite/gas/elf/retain5.d
create mode 100644 gas/testsuite/gas/elf/retain5.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..5e384f2c58 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},
@@ -802,9 +806,15 @@ obj_elf_change_section (const char *name,
as_bad (_("changed section attributes for %s"), name);
}
else
- /* FIXME: Maybe we should consider removing a previously set
- processor or application specific attribute as suspicious ? */
- elf_section_flags (sec) = attr;
+ {
+ /* Don't clobber SHF_GNU_RETAIN if it has been previously set on
+ the section. */
+ attr |= (elf_section_flags (sec) & SHF_GNU_RETAIN);
+
+ /* FIXME: Maybe we should consider removing a previously set
+ processor or application specific attribute as suspicious ? */
+ elf_section_flags (sec) = attr;
+ }
if ((flags & SEC_MERGE) && old_sec->entsize != (unsigned) entsize)
as_bad (_("changed section entity size for %s"), name);
@@ -857,6 +867,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 +1999,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..c0dfdf504f 100644
--- a/gas/testsuite/gas/elf/elf.exp
+++ b/gas/testsuite/gas/elf/elf.exp
@@ -307,6 +307,13 @@ 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 "retain4"
+ run_dump_test "retain5"
+
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/retain4.d b/gas/testsuite/gas/elf/retain4.d
new file mode 100644
index 0000000000..eacad20635
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain4.d
@@ -0,0 +1,6 @@
+#readelf: -S --wide
+#name: SHF_GNU_RETAIN 4 (switch to SHF_GNU_RETAIN section without "R" flag)
+
+#...
+ \[..\] .text.foo[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AXR.*
+#pass
diff --git a/gas/testsuite/gas/elf/retain4.s b/gas/testsuite/gas/elf/retain4.s
new file mode 100644
index 0000000000..48aeae2a11
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain4.s
@@ -0,0 +1,20 @@
+/* SHF_GNU_RETAIN should not be clobbered when switching to a section without
+ explicitly specifying the flag. */
+ .section .text.foo,"axR"
+ .global foo
+ .type foo, STT_FUNC
+foo:
+ .word 0
+
+ .section .text.foo,"ax"
+ .global bar
+ .type bar, STT_FUNC
+bar:
+ .word 0
+
+ .section .text.main,"ax"
+ .global main
+ .type main, STT_FUNC
+main:
+ .word 0
+
diff --git a/gas/testsuite/gas/elf/retain5.d b/gas/testsuite/gas/elf/retain5.d
new file mode 100644
index 0000000000..9edbfad710
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain5.d
@@ -0,0 +1,6 @@
+#readelf: -S --wide
+#name: SHF_GNU_RETAIN 5 (apply "R" flag to existing section)
+
+#...
+ \[..\] .text.foo[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AXR.*
+#pass
diff --git a/gas/testsuite/gas/elf/retain5.s b/gas/testsuite/gas/elf/retain5.s
new file mode 100644
index 0000000000..c2957a7db4
--- /dev/null
+++ b/gas/testsuite/gas/elf/retain5.s
@@ -0,0 +1,18 @@
+/* Applying the "R" flag to an existing section should apply SHF_GNU_RETAIN. */
+ .section .text.foo,"ax"
+ .global foo
+ .type foo, STT_FUNC
+foo:
+ .word 0
+
+ .section .text.foo,"axR"
+ .global bar
+ .type bar, STT_FUNC
+bar:
+ .word 0
+
+ .section .text.main,"ax"
+ .global main
+ .type main, STT_FUNC
+main:
+ .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..9046bc870a 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -225,6 +225,39 @@ 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"] \
]
}
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..9834d58297
--- /dev/null
+++ b/ld/testsuite/ld-elf/retain2.d
@@ -0,0 +1,6 @@
+# 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-*-*
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
More information about the Binutils
mailing list