On Mon, Oct 26, 2020 at 07:08:06PM +0000, Pedro Alves via Gcc-patches wrote: > On 10/6/20 12:10 PM, Jozef Lawrynowicz wrote: > > > Should "used" apply SHF_GNU_RETAIN? > > =================================== > > Another talking point is whether the existing "used" attribute should > > apply the SHF_GNU_RETAIN flag to the containing section. > > > > It seems unlikely that a user applies the "used" attribute to a > > declaration, and means for it to be saved from only compiler > > optimization, but *not* linker optimization. So perhaps it would be > > beneficial for "used" to apply SHF_GNU_RETAIN in some way. > > > > If "used" did apply SHF_GNU_RETAIN, we would also have to > > consider the above options for how to apply SHF_GNU_RETAIN to the > > section. Since the "used" attribute has been around for a while > > it might not be appropriate for its behavior to be changed to place the > > associated declaration in its own, unique section, as in option (2). > > > > To me, if I use attribute((used)), and the linker still garbage > collects the symbol, then the toolchain has a bug. Is there any > use case that would suggest otherwise?
I revised the implementation so the "used" attribute will save the symbol from garbage collection. By implementing the TARGET_MARK_DECL_PRESERVED macro, a ".retain <symname>" directive will be emitted for decls that had the "used" attribute applied. GAS will set the SHF_GNU_RETAIN flag on sections containing symbols referenced in ".retain" directives. GAS still supports setting the "R" flag in .section directives, but GCC won't emit these. LD will save SHF_GNU_RETAIN sections from garbage collection. For reference, I've attached the Binutils and GCC patches that implement this. The results from a bootstrap and regtest for x86_64-pc-linux-gnu and light testing for arm-eabi are looking good, but I need to add more tests and clean the patches up before final submission. Thanks! Jozef > > Thanks, > Pedro Alves >
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 140a98594d..ffb75f7919 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -1897,14 +1897,15 @@ struct output_elf_obj_tdata bfd_boolean flags_init; }; -/* Indicate if the bfd contains SHF_GNU_MBIND sections or symbols that - have the STT_GNU_IFUNC symbol type or STB_GNU_UNIQUE binding. Used - to set the osabi field in the ELF header structure. */ +/* Indicate if the bfd contains SHF_GNU_MBIND/SHF_GNU_RETAIN sections or + symbols that have the STT_GNU_IFUNC symbol type or STB_GNU_UNIQUE + binding. Used to set the osabi field in the ELF header structure. */ enum elf_gnu_osabi { elf_gnu_osabi_mbind = 1 << 0, elf_gnu_osabi_ifunc = 1 << 1, elf_gnu_osabi_unique = 1 << 2, + elf_gnu_osabi_retain = 1 << 3, }; typedef struct elf_section_list @@ -2034,7 +2035,7 @@ struct elf_obj_tdata ENUM_BITFIELD (dynamic_lib_link_class) dyn_lib_class : 4; /* Whether the bfd uses OS specific bits that require ELFOSABI_GNU. */ - ENUM_BITFIELD (elf_gnu_osabi) has_gnu_osabi : 3; + ENUM_BITFIELD (elf_gnu_osabi) has_gnu_osabi : 4; /* Whether if the bfd contains the GNU_PROPERTY_NO_COPY_ON_PROTECTED property. */ diff --git a/bfd/elf.c b/bfd/elf.c index 9d7cbd52e0..8ec21d7705 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -1066,9 +1066,12 @@ _bfd_elf_make_section_from_shdr (bfd *abfd, /* FIXME: We should not recognize SHF_GNU_MBIND for ELFOSABI_NONE, but binutils as of 2019-07-23 did not set the EI_OSABI header byte. */ - case ELFOSABI_NONE: case ELFOSABI_GNU: case ELFOSABI_FREEBSD: + if ((hdr->sh_flags & SHF_GNU_RETAIN) != 0) + elf_tdata (abfd)->has_gnu_osabi |= elf_gnu_osabi_retain; + /* Fall through */ + case ELFOSABI_NONE: if ((hdr->sh_flags & SHF_GNU_MBIND) != 0) elf_tdata (abfd)->has_gnu_osabi |= elf_gnu_osabi_mbind; break; @@ -12454,8 +12457,8 @@ _bfd_elf_final_write_processing (bfd *abfd) i_ehdrp->e_ident[EI_OSABI] = get_elf_backend_data (abfd)->elf_osabi; /* Set the osabi field to ELFOSABI_GNU if the binary contains - SHF_GNU_MBIND sections or symbols of STT_GNU_IFUNC type or - STB_GNU_UNIQUE binding. */ + SHF_GNU_MBIND or SHF_GNU_RETAIN sections or symbols of STT_GNU_IFUNC type + or STB_GNU_UNIQUE binding. */ if (elf_tdata (abfd)->has_gnu_osabi != 0) { if (i_ehdrp->e_ident[EI_OSABI] == ELFOSABI_NONE) @@ -12464,11 +12467,17 @@ _bfd_elf_final_write_processing (bfd *abfd) && i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_FREEBSD) { if (elf_tdata (abfd)->has_gnu_osabi & elf_gnu_osabi_mbind) - _bfd_error_handler (_("GNU_MBIND section is unsupported")); + _bfd_error_handler (_("GNU_MBIND section is supported only by GNU " + "and FreeBSD targets")); if (elf_tdata (abfd)->has_gnu_osabi & elf_gnu_osabi_ifunc) - _bfd_error_handler (_("symbol type STT_GNU_IFUNC is unsupported")); + _bfd_error_handler (_("symbol type STT_GNU_IFUNC is supported " + "only by GNU and FreeBSD targets")); if (elf_tdata (abfd)->has_gnu_osabi & elf_gnu_osabi_unique) - _bfd_error_handler (_("symbol binding STB_GNU_UNIQUE is unsupported")); + _bfd_error_handler (_("symbol binding STB_GNU_UNIQUE is supported " + "only by GNU and FreeBSD targets")); + if (elf_tdata (abfd)->has_gnu_osabi & elf_gnu_osabi_retain) + _bfd_error_handler (_("GNU_RETAIN section is supported " + "only by GNU and FreeBSD targets")); bfd_set_error (bfd_error_sorry); return FALSE; } diff --git a/bfd/elflink.c b/bfd/elflink.c index e23d189b98..22af7bbb1d 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -10733,6 +10733,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd) extsymoff = symtab_hdr->sh_info; } + /* Enable GNU OSABI features in the output BFD that are used in the input + BFD. */ + elf_tdata (output_bfd)->has_gnu_osabi |= elf_tdata (input_bfd)->has_gnu_osabi; + /* Read the local symbols. */ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; if (isymbuf == NULL && locsymcount != 0) @@ -14103,7 +14107,9 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info) == SHT_FINI_ARRAY))) || (elf_section_data (o)->this_hdr.sh_type == SHT_NOTE && elf_next_in_group (o) == NULL - && elf_linked_to_section (o) == NULL))) + && elf_linked_to_section (o) == NULL) + || ((elf_tdata (sub)->has_gnu_osabi & elf_gnu_osabi_retain) + && (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 35e4e303e1..32c264d74e 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -7,6 +7,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. + 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 03cfc97464..dd82ff8899 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -5996,6 +5996,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) @@ -6028,7 +6030,6 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags) case SHF_TLS: sindex = 9; break; case SHF_EXCLUDE: sindex = 18; break; case SHF_COMPRESSED: sindex = 20; break; - case SHF_GNU_MBIND: sindex = 24; break; default: sindex = -1; @@ -6080,10 +6081,26 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags) if (flag == SHF_PPC_VLE) sindex = 25; break; + } + switch (filedata->file_header.e_ident[EI_OSABI]) + { + case ELFOSABI_GNU: + case ELFOSABI_FREEBSD: + if (flag == SHF_GNU_RETAIN) + sindex = 26; + /* Fall through */ + case ELFOSABI_NONE: + if (flag == SHF_GNU_MBIND) + /* We should not recognize SHF_GNU_MBIND for + ELFOSABI_NONE, but binutils as of 2019-07-23 did + not set the EI_OSABI header byte. */ + sindex = 24; + break; default: break; } + break; } if (sindex != -1) @@ -6126,7 +6143,6 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags) case SHF_TLS: *p = 'T'; break; case SHF_EXCLUDE: *p = 'E'; break; case SHF_COMPRESSED: *p = 'C'; break; - case SHF_GNU_MBIND: *p = 'D'; break; default: if ((filedata->file_header.e_machine == EM_X86_64 @@ -6136,14 +6152,37 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags) *p = 'l'; else if (filedata->file_header.e_machine == EM_ARM && flag == SHF_ARM_PURECODE) - *p = 'y'; + *p = 'y'; else if (filedata->file_header.e_machine == EM_PPC && flag == SHF_PPC_VLE) - *p = 'v'; + *p = 'v'; else if (flag & SHF_MASKOS) { - *p = 'o'; - sh_flags &= ~ SHF_MASKOS; + switch (filedata->file_header.e_ident[EI_OSABI]) + { + case ELFOSABI_GNU: + case ELFOSABI_FREEBSD: + if (flag == SHF_GNU_RETAIN) + { + *p = 'R'; + break; + } + /* Fall through */ + case ELFOSABI_NONE: + if (flag == SHF_GNU_MBIND) + { + /* We should not recognize SHF_GNU_MBIND for + ELFOSABI_NONE, but binutils as of 2019-07-23 did + not set the EI_OSABI header byte. */ + *p = 'D'; + break; + } + /* Fall through */ + default: + *p = 'o'; + sh_flags &= ~SHF_MASKOS; + break; + } } else if (flag & SHF_MASKPROC) { diff --git a/binutils/testsuite/binutils-all/readelf-maskos-1a.d b/binutils/testsuite/binutils-all/readelf-maskos-1a.d new file mode 100644 index 0000000000..7b27358599 --- /dev/null +++ b/binutils/testsuite/binutils-all/readelf-maskos-1a.d @@ -0,0 +1,10 @@ +#name: Unknown SHF_MASKOS value in section +#source: readelf-maskos.s +#notarget: [supports_gnu_osabi] msp430-*-elf visium-*-elf +#xfail: arm-*-elf +#readelf: -S --wide +# PR26722 for the arm-*-elf XFAIL + +#... + \[[ 0-9]+\] .data.retain_var.*WAo.* +#pass diff --git a/binutils/testsuite/binutils-all/readelf-maskos-1b.d b/binutils/testsuite/binutils-all/readelf-maskos-1b.d new file mode 100644 index 0000000000..2cbb58a73b --- /dev/null +++ b/binutils/testsuite/binutils-all/readelf-maskos-1b.d @@ -0,0 +1,12 @@ +#name: -t (section details) for unknown SHF_MASKOS value in section +#source: readelf-maskos.s +#notarget: [supports_gnu_osabi] msp430-*-elf visium-*-elf +#xfail: arm-*-elf +#readelf: -S -t --wide +# PR26722 for the arm-*-elf XFAIL + +#... + \[[ 0-9]+\] .data.retain_var + PROGBITS +0+ +[0-9a-f]+ +[0-9a-f]+ +[0-9a-f]+ +0 +0 +(1|2|4|8) + \[00200003\]: WRITE, ALLOC, OS \(00200000\) +#pass diff --git a/binutils/testsuite/binutils-all/readelf-maskos.s b/binutils/testsuite/binutils-all/readelf-maskos.s new file mode 100644 index 0000000000..d671119bca --- /dev/null +++ b/binutils/testsuite/binutils-all/readelf-maskos.s @@ -0,0 +1,11 @@ + .section .data.retain_var,"0x200003" + .global retain_var + .type retain_var, %object +retain_var: + .long 2 + + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0 diff --git a/binutils/testsuite/binutils-all/readelf.exp b/binutils/testsuite/binutils-all/readelf.exp index 1fb36ae5c4..9d1d496e5c 100644 --- a/binutils/testsuite/binutils-all/readelf.exp +++ b/binutils/testsuite/binutils-all/readelf.exp @@ -364,8 +364,15 @@ 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 "retain1a" + run_dump_test "retain1b" + run_dump_test "readelf-maskos-1a" + run_dump_test "readelf-maskos-1b" +} # 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.s b/binutils/testsuite/binutils-all/retain1.s new file mode 100644 index 0000000000..f7716faabe --- /dev/null +++ b/binutils/testsuite/binutils-all/retain1.s @@ -0,0 +1,104 @@ + .global discard0 + .section .bss.discard0,"aw" + .type discard0, %object +discard0: + .zero 2 + + .global discard1 + .section .bss.discard1,"aw" + .type discard1, %object +discard1: + .zero 2 + + .global discard2 + .section .data.discard2,"aw" + .type discard2, %object +discard2: + .word 1 + + .section .bss.sdiscard0,"aw" + .type sdiscard0, %object +sdiscard0: + .zero 2 + + .section .bss.sdiscard1,"aw" + .type sdiscard1, %object +sdiscard1: + .zero 2 + + .section .data.sdiscard2,"aw" + .type sdiscard2, %object +sdiscard2: + .word 1 + + .section .text.fndiscard0,"ax" + .global fndiscard0 + .type fndiscard0, %function +fndiscard0: + .word 0 + + .global retain0 + .section .bss.retain0,"awR" + .type retain0, %object +retain0: + .zero 2 + + .global retain1 + .section .bss.retain1,"awR" + .type retain1, %object +retain1: + .zero 2 + + .global retain2 + .section .data.retain2,"awR" + .type retain2, %object +retain2: + .word 1 + + .section .bss.sretain0,"awR" + .type sretain0, %object +sretain0: + .zero 2 + + .section .bss.sretain1,"awR" + .type sretain1, %object +sretain1: + .zero 2 + + .section .data.sretain2,"aRw" + .type sretain2, %object +sretain2: + .word 1 + + .section .text.fnretain1,"Rax" + .global fnretain1 + .type fnretain1, %function +fnretain1: + .word 0 + + .section .text.fndiscard2,"ax" + .global fndiscard2 + .type fndiscard2, %function +fndiscard2: + .word 0 + + .section .bss.lsretain0,"awR" + .type lsretain0.2, %object +lsretain0.2: + .zero 2 + + .section .bss.lsretain1,"aRw" + .type lsretain1.1, %object +lsretain1.1: + .zero 2 + + .section .data.lsretain2,"aRw" + .type lsretain2.0, %object +lsretain2.0: + .word 1 + + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0 diff --git a/binutils/testsuite/binutils-all/retain1a.d b/binutils/testsuite/binutils-all/retain1a.d new file mode 100644 index 0000000000..6397ac52ae --- /dev/null +++ b/binutils/testsuite/binutils-all/retain1a.d @@ -0,0 +1,18 @@ +#name: readelf SHF_GNU_RETAIN +#source: retain1.s +#target: [supports_gnu_osabi] +#readelf: -S --wide + +#... + \[[ 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/retain1b.d b/binutils/testsuite/binutils-all/retain1b.d new file mode 100644 index 0000000000..12bc388ba1 --- /dev/null +++ b/binutils/testsuite/binutils-all/retain1b.d @@ -0,0 +1,46 @@ +#name: -t (section details) for readelf SHF_GNU_RETAIN +#source: retain1.s +#target: [supports_gnu_osabi] +#readelf: -S -t --wide + +#... + \[[ 0-9]+\] .bss.retain0 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .bss.retain1 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .data.retain2 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .bss.sretain0 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .bss.sretain1 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .data.sretain2 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .text.fnretain1 +#... + \[0+200006\]: ALLOC, EXEC, GNU_RETAIN +#... + \[[ 0-9]+\] .bss.lsretain0 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .bss.lsretain1 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#... + \[[ 0-9]+\] .data.lsretain2 +#... + \[0+200003\]: WRITE, ALLOC, GNU_RETAIN +#pass diff --git a/binutils/testsuite/lib/binutils-common.exp b/binutils/testsuite/lib/binutils-common.exp index b9a1e6e4bc..a43639bafb 100644 --- a/binutils/testsuite/lib/binutils-common.exp +++ b/binutils/testsuite/lib/binutils-common.exp @@ -195,13 +195,15 @@ proc match_target { target } { # True if the ELF target supports setting the ELF header OSABI field # to ELFOSABI_GNU or ELFOSABI_FREEBSD, a requirement for STT_GNU_IFUNC -# symbol and SHF_GNU_MBIND section support. +# symbol and SHF_GNU_MBIND or SHF_GNU_RETAIN section support. # # This generally depends on the target OS only, however there are a # number of exceptions for bare metal targets as follows. The MSP430 # and Visium targets set OSABI to ELFOSABI_STANDALONE. Likewise # non-EABI ARM targets set OSABI to ELFOSABI_ARM # +# Non-Linux HPPA defaults to ELFOSABI_HPUX. +# # Note that some TI C6X targets use ELFOSABI_C6000_* but one doesn't, # so we don't try to sort out tic6x here. (The effect is that linker # testcases will generally need to exclude tic6x or use a -m option.) @@ -227,6 +229,7 @@ proc supports_gnu_osabi {} { } if { [istarget "arm*-*-*"] || [istarget "msp430-*-*"] + || [istarget "hppa-unknown-elf"] || [istarget "visium-*-*"] } { return 0 } diff --git a/gas/NEWS b/gas/NEWS index 41cc668e61..5340eb8223 100644 --- a/gas/NEWS +++ b/gas/NEWS @@ -44,6 +44,11 @@ * Configure with --enable-x86-used-note by default for Linux/x86. +* Add support for the "R" flag in the .section directive. + This flag requires ELFOSABI_GNU or ELFOSABI_FREEBSD, and applies the + ELF SHF_GNU_RETAIN flag to the specified section. This flag specifies + the section should not be garbage collected by the linker. + 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 f061ea61f3..11f6795229 100644 --- a/gas/config/obj-elf.c +++ b/gas/config/obj-elf.c @@ -79,6 +79,7 @@ static void obj_elf_tls_common (int); static void obj_elf_lcomm (int); static void obj_elf_struct (int); static void obj_elf_attach_to_group (int); +static void obj_elf_retain (int); static const pseudo_typeS elf_pseudo_table[] = { @@ -121,6 +122,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}, @@ -529,9 +533,9 @@ get_section_by_match (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf) const char *group_name = elf_group_name (sec); const char *linked_to_symbol_name = sec->map_head.linked_to_symbol_name; - unsigned int info = elf_section_data (sec)->this_hdr.sh_info; + unsigned int sh_info = elf_section_data (sec)->this_hdr.sh_info; - return (info == match->info + return (sh_info == match->sh_info && ((bfd_section_flags (sec) & SEC_ASSEMBLER_SECTION_ID) == (match->flags & SEC_ASSEMBLER_SECTION_ID)) && sec->section_id == match->section_id @@ -740,7 +744,7 @@ obj_elf_change_section (const char *name, type = bfd_elf_get_default_section_type (flags); elf_section_type (sec) = type; elf_section_flags (sec) = attr; - elf_section_data (sec)->this_hdr.sh_info = match_p->info; + elf_section_data (sec)->this_hdr.sh_info = match_p->sh_info; /* Prevent SEC_HAS_CONTENTS from being inadvertently set. */ if (type == SHT_NOBITS) @@ -806,9 +810,16 @@ 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; + { + /* FIXME: Maybe we should consider removing a previously set + processor or application specific attribute as suspicious ? */ + /* Don't overwrite a previously set SHF_GNU_RETAIN flag for the + section. The entire section will be marked retained. */ + if ((elf_tdata (stdoutput)->has_gnu_osabi & elf_gnu_osabi_retain) + && ((elf_section_flags (old_sec) & SHF_GNU_RETAIN))) + attr |= SHF_GNU_RETAIN; + elf_section_flags (sec) = attr; + } if ((flags & SEC_MERGE) && old_sec->entsize != (unsigned) entsize) as_bad (_("changed section entity size for %s"), name); @@ -861,6 +872,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; @@ -890,8 +904,32 @@ obj_elf_parse_section_letters (char *str, size_t len, if (ISDIGIT (*str)) { char * end; + struct elf_backend_data *bed; + bfd_vma numeric_flags = strtoul (str, &end, 0); + + attr |= numeric_flags; + + bed = (struct elf_backend_data *) + get_elf_backend_data (stdoutput); + + if (bed->elf_osabi == ELFOSABI_NONE + || bed->elf_osabi == ELFOSABI_STANDALONE + || bed->elf_osabi == ELFOSABI_GNU + || bed->elf_osabi == ELFOSABI_FREEBSD) + { + /* Add flags in the SHF_MASKOS range to gnu_attr for + OSABIs that support those flags. + Also adding the flags for ELFOSABI_{NONE,STANDALONE} + allows them to be validated later in obj_elf_section. + We can't just always set these bits in gnu_attr for + all OSABIs, since Binutils does not recognize all + SHF_MASKOS bits for non-GNU OSABIs. It's therefore + possible that numeric flags are being used to set bits + in the SHF_MASKOS range for those targets, and we + don't want assembly to fail in those situations. */ + *gnu_attr |= (numeric_flags & SHF_MASKOS); + } - attr |= strtoul (str, & end, 0); /* Update str and len, allowing for the fact that we will execute str++ and len-- below. */ end --; @@ -1287,18 +1325,21 @@ obj_elf_section (int push) if (ISDIGIT (* input_line_pointer)) { char *t = input_line_pointer; - match.info = strtoul (input_line_pointer, + match.sh_info = strtoul (input_line_pointer, &input_line_pointer, 0); - if (match.info == (unsigned int) -1) + if (match.sh_info == (unsigned int) -1) { as_warn (_("unsupported mbind section info: %s"), t); - match.info = 0; + match.sh_info = 0; } } else input_line_pointer = save; } + if ((gnu_attr & SHF_GNU_RETAIN) != 0) + match.sh_flags |= SHF_GNU_RETAIN; + if (*input_line_pointer == ',') { char *save = input_line_pointer; @@ -1387,26 +1428,39 @@ obj_elf_section (int push) done: demand_empty_rest_of_line (); - obj_elf_change_section (name, type, attr, entsize, &match, linkonce, - push); - - if ((gnu_attr & SHF_GNU_MBIND) != 0) + if ((gnu_attr & (SHF_GNU_MBIND | SHF_GNU_RETAIN)) != 0) { struct elf_backend_data *bed; + bfd_boolean mbind_p = (gnu_attr & SHF_GNU_MBIND) != 0; - if ((attr & SHF_ALLOC) == 0) + if (mbind_p && (attr & SHF_ALLOC) == 0) as_bad (_("SHF_ALLOC isn't set for GNU_MBIND section: %s"), name); bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput); - if (bed->elf_osabi == ELFOSABI_NONE) - bed->elf_osabi = ELFOSABI_GNU; - else if (bed->elf_osabi != ELFOSABI_GNU - && bed->elf_osabi != ELFOSABI_FREEBSD) - as_bad (_("GNU_MBIND section is supported only by GNU " - "and FreeBSD targets")); - elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_mbind; + + if (bed->elf_osabi != ELFOSABI_GNU + && bed->elf_osabi != ELFOSABI_FREEBSD + && bed->elf_osabi != ELFOSABI_NONE) + { + as_bad (_("%s section is supported only by GNU and FreeBSD targets"), + mbind_p ? "GNU_MBIND" : "GNU_RETAIN"); + } + else + { + if (bed->elf_osabi == ELFOSABI_NONE) + bed->elf_osabi = ELFOSABI_GNU; + + if (mbind_p) + elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_mbind; + if ((gnu_attr & SHF_GNU_RETAIN) != 0) + elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_retain; + + attr |= gnu_attr; + } } - elf_section_flags (now_seg) |= gnu_attr; + + obj_elf_change_section (name, type, attr, entsize, &match, linkonce, + push); if (linked_to_section_index != -1UL) { @@ -2031,6 +2085,34 @@ obj_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED) obj_elf_vendor_attribute (OBJ_ATTR_GNU); } +/* TODO */ +void +obj_elf_retain (int arg ATTRIBUTE_UNUSED) +{ + struct elf_backend_data *bed; + symbolS *sym; + + bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput); + + if (bed->elf_osabi != ELFOSABI_GNU + && bed->elf_osabi != ELFOSABI_FREEBSD + && bed->elf_osabi != ELFOSABI_NONE) + { + as_bad (_(".retain directive is supported only by GNU and FreeBSD targets")); + } + else + { + if (bed->elf_osabi == ELFOSABI_NONE) + bed->elf_osabi = ELFOSABI_GNU; + elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_retain; + } + + sym = get_sym_from_input_line_and_check (); + symbol_get_obj (sym)->retain = 1; + + demand_empty_rest_of_line (); +} + void elf_obj_read_begin_hook (void) { @@ -2624,6 +2706,9 @@ elf_frob_symbol (symbolS *symp, int *puntp) } } + if (symbol_get_obj (symp)->retain) + elf_section_flags (S_GET_SEGMENT (symp)) |= SHF_GNU_RETAIN; + /* Double check weak symbols. */ if (S_IS_WEAK (symp)) { diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h index 4f29572eef..628f5b529d 100644 --- a/gas/config/obj-elf.h +++ b/gas/config/obj-elf.h @@ -84,6 +84,10 @@ struct elf_obj_sy /* Whether visibility of the symbol should be changed. */ ENUM_BITFIELD (elf_visibility) visibility : 2; + /* Set when the symbol was marked with the .retain directive, so it's + containing section must be marked with SHF_GNU_RETAIN. */ + unsigned int retain : 1; + /* Use this to keep track of .size expressions that involve differences that we can't compute yet. */ expressionS *size; @@ -106,8 +110,9 @@ struct elf_section_match { const char * group_name; const char * linked_to_symbol_name; - unsigned int info; unsigned int section_id; + unsigned int sh_info; /* ELF section information. */ + bfd_vma sh_flags; /* ELF section flags. */ flagword flags; }; diff --git a/gas/doc/as.texi b/gas/doc/as.texi index 4d5294552a..e92432a8bd 100644 --- a/gas/doc/as.texi +++ b/gas/doc/as.texi @@ -6657,6 +6657,9 @@ section is a member of a section group section is used for thread-local-storage @item ? section is a member of the previously-current section's group, if any +@item R +retained section (apply SHF_GNU_RETAIN to prevent linker garbage +collection, GNU ELF extension) @item @code{<number>} a numeric value indicating the bits to be set in the ELF section header's flags field. Note - if one or more of the alphabetic characters described above is diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp index 9d75154483..49d5a47959 100644 --- a/gas/testsuite/gas/elf/elf.exp +++ b/gas/testsuite/gas/elf/elf.exp @@ -261,8 +261,11 @@ if { [is_elf_format] } then { run_dump_test "section19" run_dump_test "section20" run_dump_test "section21" + run_dump_test "section22" + run_dump_test "section23a" + run_dump_test "section23b" + run_dump_test "section24" run_dump_test "sh-link-zero" - run_dump_test "dwarf2-1" $dump_opts run_dump_test "dwarf2-2" $dump_opts run_dump_test "dwarf2-3" $dump_opts diff --git a/gas/testsuite/gas/elf/section10.d b/gas/testsuite/gas/elf/section10.d index 554a791f1d..6aa7b088b1 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\) +[ ]*\[.*fedff030\]: 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\) +[ ]*\[.*fedf0000\]:.* EXCLUDE, OS \(.*ed00000\), PROC \(.*[3467]0000000\), UNKNOWN \(.*f0000\) [ ]*\[.*\][ ]+.data.foo [ ]*LOUSER\+0x7f000000[ ].* [ ]*\[0+003\]: WRITE, ALLOC diff --git a/gas/testsuite/gas/elf/section10.s b/gas/testsuite/gas/elf/section10.s index 29f1184523..d52b3458fb 100644 --- a/gas/testsuite/gas/elf/section10.s +++ b/gas/testsuite/gas/elf/section10.s @@ -7,7 +7,7 @@ .word 2 # Make sure that specifying further arguments to .sections is still supported - .section sec3, "0xfefff000MS", %progbits, 32 + .section sec3, "0xfedff000MS", %progbits, 32 .word 3 # Make sure that extra flags can be set for well known sections as well. @@ -19,7 +19,7 @@ .word 5 # Test both together, with a quoted type value. - .section sec5, "0xfeff0000", "0x80000009" + .section sec5, "0xfedf0000", "0x80000009" .word 6 # Test that declaring an extended version of a known special section works. diff --git a/gas/testsuite/gas/elf/section22.d b/gas/testsuite/gas/elf/section22.d new file mode 100644 index 0000000000..8aa7fcfc34 --- /dev/null +++ b/gas/testsuite/gas/elf/section22.d @@ -0,0 +1,19 @@ +#readelf: -h -S --wide +#name: SHF_GNU_RETAIN sections 22 +#notarget: ![supports_gnu_osabi] + +#... + +OS/ABI: +UNIX - GNU +#... + \[..\] .text.discard0[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AX.* +#... + \[..\] .data.discard1[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WA.* +#... + \[..\] .bss.discard2[ ]+NOBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WA.* +#... + \[..\] .bss.retain0[ ]+NOBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.* +#... + \[..\] .data.retain1[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.* +#... + \[..\] .text.retain2[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AXR.* +#pass diff --git a/gas/testsuite/gas/elf/section22.s b/gas/testsuite/gas/elf/section22.s new file mode 100644 index 0000000000..66ed990e57 --- /dev/null +++ b/gas/testsuite/gas/elf/section22.s @@ -0,0 +1,34 @@ + .section .text.discard0,"ax",%progbits + .global discard0 + .type discard0, %function +discard0: + .word 0 + + .section .data.discard1,"aw" + .global discard1 + .type discard1, %object +discard1: + .word 1 + + .section .bss.discard2,"aw" + .global discard2 + .type discard2, %object +discard2: + .zero 2 + + .section .bss.retain0,"awR",%nobits + .global retain0 + .type retain0, %object +retain0: + .zero 2 + + .section .data.retain1,"awR",%progbits + .type retain1, %object +retain1: + .word 1 + + .section .text.retain2,"axR",%progbits + .global retain2 + .type retain2, %function +retain2: + .word 0 diff --git a/gas/testsuite/gas/elf/section23.s b/gas/testsuite/gas/elf/section23.s new file mode 100644 index 0000000000..d671119bca --- /dev/null +++ b/gas/testsuite/gas/elf/section23.s @@ -0,0 +1,11 @@ + .section .data.retain_var,"0x200003" + .global retain_var + .type retain_var, %object +retain_var: + .long 2 + + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0 diff --git a/gas/testsuite/gas/elf/section23a.d b/gas/testsuite/gas/elf/section23a.d new file mode 100644 index 0000000000..1d850d9e8e --- /dev/null +++ b/gas/testsuite/gas/elf/section23a.d @@ -0,0 +1,11 @@ +#name: SHF_GNU_RETAIN set with numeric flag value in .section +#source: section23.s +#target: [supports_gnu_osabi] +#readelf: -h -S --wide + +#... + +OS/ABI: +UNIX - GNU +#... + \[..\] .data.retain_var[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 WAR.* +#pass + diff --git a/gas/testsuite/gas/elf/section23b.d b/gas/testsuite/gas/elf/section23b.d new file mode 100644 index 0000000000..c85200e5ff --- /dev/null +++ b/gas/testsuite/gas/elf/section23b.d @@ -0,0 +1,6 @@ +#name: SHF_GNU_RETAIN set with numeric flag value in .section for non-GNU OSABI target +#source: section23.s +#error_output: section23b.err +#target: msp430-*-elf visium-*-elf + +# This test only runs for targets which set ELFOSABI_STANDALONE. diff --git a/gas/testsuite/gas/elf/section23b.err b/gas/testsuite/gas/elf/section23b.err new file mode 100644 index 0000000000..83de60c397 --- /dev/null +++ b/gas/testsuite/gas/elf/section23b.err @@ -0,0 +1,2 @@ +.*: Assembler messages: +.*:1: Error: GNU_RETAIN section is supported only by GNU and FreeBSD targets diff --git a/gas/testsuite/gas/elf/section24.d b/gas/testsuite/gas/elf/section24.d new file mode 100644 index 0000000000..5ee4aee3af --- /dev/null +++ b/gas/testsuite/gas/elf/section24.d @@ -0,0 +1,18 @@ +#name: Warn for SHF_GNU_RETAIN set on existing section +#notarget: ![supports_gnu_osabi] rx-*-* +#readelf: -h -S --wide +#warning_output: section24.l +# rx-*-* does not automatically create a ".text" section when starting assembly. + +#... + +OS/ABI: +UNIX - GNU +#... + \[..\] .text[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AX .* +#... + \[..\] .text[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AXR .* +#... + \[..\] .text.foo[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AXR .* +#... + \[..\] .text.bar[ ]+PROGBITS[ ]+[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 AX .* +#pass + diff --git a/gas/testsuite/gas/elf/section24.l b/gas/testsuite/gas/elf/section24.l new file mode 100644 index 0000000000..e0ea36078c --- /dev/null +++ b/gas/testsuite/gas/elf/section24.l @@ -0,0 +1,4 @@ +[^:]*: Assembler messages: +[^:]*:4: Warning: ignoring changed section attributes for .text +[^:]*:20: Warning: ignoring changed section attributes for .text.foo +[^:]*:30: Warning: ignoring changed section attributes for .text.bar diff --git a/gas/testsuite/gas/elf/section24.s b/gas/testsuite/gas/elf/section24.s new file mode 100644 index 0000000000..deaff0a323 --- /dev/null +++ b/gas/testsuite/gas/elf/section24.s @@ -0,0 +1,32 @@ +/* The default .text section automatically created by the assembler does not + have the SHF_GNU_RETAIN flag set, so the "R" flag cannot be used with that, + or any other, default section. */ + .section .text,"axR",%progbits +retain_bad: + .word 0 + +/* A unique .text section with SHF_GNU_RETAIN applied can be created. */ + .section .text,"axR",%progbits,unique,0 +retain_good: + .word 0 + +/* SHF_GNU_RETAIN can be applied to a new section. */ + .section .text.foo,"axR",%progbits +foo_retain: + .word 0 + +/* If the section is used again without SHF_GNU_RETAIN, a warning should be + emitted. */ + .section .text.foo,"ax",%progbits +foo: + .word 0 + + .section .text.bar,"ax",%progbits +bar: + .word 0 + +/* SHF_GNU_RETAIN cannot be applied to a section which was already explicitly + declared without SHF_GNU_RETAIN set. */ + .section .text.bar,"axR",%progbits +bar_retain: + .word 0 diff --git a/include/elf/common.h b/include/elf/common.h index b3c30e0e2f..a17cafcc70 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 81c44191d2..bb23010dad 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -23,6 +23,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. + Changes in 2.35: * X86 NaCl target support is removed. diff --git a/ld/ld.texi b/ld/ld.texi index 48e78aecdb..40c209d914 100644 --- a/ld/ld.texi +++ b/ld/ld.texi @@ -1805,6 +1805,9 @@ 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. + @kindex --print-gc-sections @kindex --no-print-gc-sections @cindex garbage collection @@ -5265,6 +5268,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. + 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 f2ff0397c7..bd06ab0d39 100644 --- a/ld/testsuite/ld-elf/elf.exp +++ b/ld/testsuite/ld-elf/elf.exp @@ -119,6 +119,17 @@ if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"] } { set ASFLAGS "$ASFLAGS -mx86-used-note=no" } +# Build libraries required for SHF_GNU_RETAIN tests. +if { [check_gc_sections_available] && [supports_gnu_osabi] } { + run_ld_link_tests [list \ + [list "Build libretain5.a" "" "" "" \ + {retain5lib.s} {} "libretain5.a"] \ + [list "Build libretain6.a" "" "" "" \ + {retain6lib.s} {} "libretain6.a"] \ + ] +} + + set test_list [lsort [glob -nocomplain $srcdir/$subdir/*.d]] foreach t $test_list { # We need to strip the ".d", but can leave the dirname. diff --git a/ld/testsuite/ld-elf/retain1.s b/ld/testsuite/ld-elf/retain1.s new file mode 100644 index 0000000000..f7716faabe --- /dev/null +++ b/ld/testsuite/ld-elf/retain1.s @@ -0,0 +1,104 @@ + .global discard0 + .section .bss.discard0,"aw" + .type discard0, %object +discard0: + .zero 2 + + .global discard1 + .section .bss.discard1,"aw" + .type discard1, %object +discard1: + .zero 2 + + .global discard2 + .section .data.discard2,"aw" + .type discard2, %object +discard2: + .word 1 + + .section .bss.sdiscard0,"aw" + .type sdiscard0, %object +sdiscard0: + .zero 2 + + .section .bss.sdiscard1,"aw" + .type sdiscard1, %object +sdiscard1: + .zero 2 + + .section .data.sdiscard2,"aw" + .type sdiscard2, %object +sdiscard2: + .word 1 + + .section .text.fndiscard0,"ax" + .global fndiscard0 + .type fndiscard0, %function +fndiscard0: + .word 0 + + .global retain0 + .section .bss.retain0,"awR" + .type retain0, %object +retain0: + .zero 2 + + .global retain1 + .section .bss.retain1,"awR" + .type retain1, %object +retain1: + .zero 2 + + .global retain2 + .section .data.retain2,"awR" + .type retain2, %object +retain2: + .word 1 + + .section .bss.sretain0,"awR" + .type sretain0, %object +sretain0: + .zero 2 + + .section .bss.sretain1,"awR" + .type sretain1, %object +sretain1: + .zero 2 + + .section .data.sretain2,"aRw" + .type sretain2, %object +sretain2: + .word 1 + + .section .text.fnretain1,"Rax" + .global fnretain1 + .type fnretain1, %function +fnretain1: + .word 0 + + .section .text.fndiscard2,"ax" + .global fndiscard2 + .type fndiscard2, %function +fndiscard2: + .word 0 + + .section .bss.lsretain0,"awR" + .type lsretain0.2, %object +lsretain0.2: + .zero 2 + + .section .bss.lsretain1,"aRw" + .type lsretain1.1, %object +lsretain1.1: + .zero 2 + + .section .data.lsretain2,"aRw" + .type lsretain2.0, %object +lsretain2.0: + .word 1 + + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0 diff --git a/ld/testsuite/ld-elf/retain1a.d b/ld/testsuite/ld-elf/retain1a.d new file mode 100644 index 0000000000..75abb9856c --- /dev/null +++ b/ld/testsuite/ld-elf/retain1a.d @@ -0,0 +1,28 @@ +#name: SHF_GNU_RETAIN 1a +#source: retain1.s +#ld: -e _start --gc-sections +#skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] +#DUMPPROG: nm + +#... +[0-9a-f]+ . fnretain1 +#... +[0-9a-f]+ . lsretain0.2 +#... +[0-9a-f]+ . lsretain1.1 +#... +[0-9a-f]+ . lsretain2.0 +#... +[0-9a-f]+ . retain0 +#... +[0-9a-f]+ . retain1 +#... +[0-9a-f]+ . retain2 +#... +[0-9a-f]+ . sretain0 +#... +[0-9a-f]+ . sretain1 +#... +[0-9a-f]+ . sretain2 +#pass diff --git a/ld/testsuite/ld-elf/retain1b.d b/ld/testsuite/ld-elf/retain1b.d new file mode 100644 index 0000000000..815c0150f5 --- /dev/null +++ b/ld/testsuite/ld-elf/retain1b.d @@ -0,0 +1,11 @@ +#name: SHF_GNU_RETAIN 1b +#source: retain1.s +#ld: -e _start --gc-sections +#skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] +#nm: -n + +#failif +#... +[0-9a-f]+ . .*discard.* +#... diff --git a/ld/testsuite/ld-elf/retain2.d b/ld/testsuite/ld-elf/retain2.d new file mode 100644 index 0000000000..11efd6ddb8 --- /dev/null +++ b/ld/testsuite/ld-elf/retain2.d @@ -0,0 +1,6 @@ +#name: SHF_GNU_RETAIN 2 (remove SHF_GNU_RETAIN sections by placing 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-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] 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.d b/ld/testsuite/ld-elf/retain3.d new file mode 100644 index 0000000000..911f5b7594 --- /dev/null +++ b/ld/testsuite/ld-elf/retain3.d @@ -0,0 +1,12 @@ +#name: SHF_GNU_RETAIN 3 (keep sections referenced by retained sections) +#source: retain3.s +#ld: -e _start --gc-sections +#skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] +#DUMPPROG: nm + +#... +[0-9a-f]+ . bar +#... +[0-9a-f]+ . foo +#pass diff --git a/ld/testsuite/ld-elf/retain3.s b/ld/testsuite/ld-elf/retain3.s new file mode 100644 index 0000000000..ce315cbaa6 --- /dev/null +++ b/ld/testsuite/ld-elf/retain3.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, %function +foo: + .word 0 + + .section .text.bar,"axR" + .global bar + .type bar, %function +bar: + .long foo + + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0 diff --git a/ld/testsuite/ld-elf/retain4.d b/ld/testsuite/ld-elf/retain4.d new file mode 100644 index 0000000000..e94898d681 --- /dev/null +++ b/ld/testsuite/ld-elf/retain4.d @@ -0,0 +1,10 @@ +#name: SHF_GNU_RETAIN 4 (keep orphaned sections when not discarding) +#source: retain4.s +#ld: -e _start --gc-sections --orphan-handling=place +#skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] +#DUMPPROG: nm + +#... +[0-9a-f]+ . orphaned_fn +#pass diff --git a/ld/testsuite/ld-elf/retain4.s b/ld/testsuite/ld-elf/retain4.s new file mode 100644 index 0000000000..9f350cd3b2 --- /dev/null +++ b/ld/testsuite/ld-elf/retain4.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 orphaned_fn + .type orphaned_fn, %function +orphaned_fn: + .word 0 + + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0 diff --git a/ld/testsuite/ld-elf/retain5.d b/ld/testsuite/ld-elf/retain5.d new file mode 100644 index 0000000000..378799599e --- /dev/null +++ b/ld/testsuite/ld-elf/retain5.d @@ -0,0 +1,12 @@ +#name: SHF_GNU_RETAIN 5 (don't pull SHF_GNU_RETAIN section out of lib) +#source: retain5main.s +#ld: --gc-sections -e _start --print-gc-sections -Ltmpdir -lretain5 -Map=retain5.map +#skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] +#map: retain5.map +#DUMPPROG: nm + +#failif +#... +[0-9a-f]+ . foo +#... diff --git a/ld/testsuite/ld-elf/retain5.map b/ld/testsuite/ld-elf/retain5.map new file mode 100644 index 0000000000..6b97c2a220 --- /dev/null +++ b/ld/testsuite/ld-elf/retain5.map @@ -0,0 +1,5 @@ +# Check that the library was actually loaded to catch any false PASS. + +#... +LOAD tmpdir/libretain5.a +#pass diff --git a/ld/testsuite/ld-elf/retain5lib.s b/ld/testsuite/ld-elf/retain5lib.s new file mode 100644 index 0000000000..4e83731719 --- /dev/null +++ b/ld/testsuite/ld-elf/retain5lib.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, %function +foo: + .long undefined_sym diff --git a/ld/testsuite/ld-elf/retain5main.s b/ld/testsuite/ld-elf/retain5main.s new file mode 100644 index 0000000000..89a7784d13 --- /dev/null +++ b/ld/testsuite/ld-elf/retain5main.s @@ -0,0 +1,5 @@ + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0 diff --git a/ld/testsuite/ld-elf/retain6a.d b/ld/testsuite/ld-elf/retain6a.d new file mode 100644 index 0000000000..92872deffc --- /dev/null +++ b/ld/testsuite/ld-elf/retain6a.d @@ -0,0 +1,14 @@ +#name: SHF_GNU_RETAIN 6a (pull section out of lib required by SHF_GNU_RETAIN section) +#source: retain6main.s +#ld: --gc-sections -e _start -u bar -Ltmpdir -lretain6 +#skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] +#DUMPPROG: nm + +#... +[0-9a-f]+ . bar +#... +[0-9a-f]+ . retain_from_lib +#... +[0-9a-f]+ . retained_fn +#pass diff --git a/ld/testsuite/ld-elf/retain6b.d b/ld/testsuite/ld-elf/retain6b.d new file mode 100644 index 0000000000..a797bf7837 --- /dev/null +++ b/ld/testsuite/ld-elf/retain6b.d @@ -0,0 +1,11 @@ +#name: SHF_GNU_RETAIN 6b (pull section out of lib required by SHF_GNU_RETAIN section) +#source: retain6main.s +#ld: --gc-sections -e _start -u bar -Ltmpdir -lretain6 +#skip: mep-*-* dlx-*-* d30v-*-* pj-*-* pru-*-* xgate-*-* s12z-*-* +#notarget: ![supports_gnu_osabi] ![check_gc_sections_available] +#DUMPPROG: nm + +#failif +#... +[0-9a-f]+ . .*discard.* +#... diff --git a/ld/testsuite/ld-elf/retain6lib.s b/ld/testsuite/ld-elf/retain6lib.s new file mode 100644 index 0000000000..a393dbac61 --- /dev/null +++ b/ld/testsuite/ld-elf/retain6lib.s @@ -0,0 +1,17 @@ + .section .text.bar,"ax" + .global bar + .type bar, %function +bar: + .word 0 + + .section .text.retain_from_lib,"axR" + .global retain_from_lib + .type retain_from_lib, %function +retain_from_lib: + .word 0 + + .section .text.discard_from_lib,"ax" + .global discard_from_lib + .type discard_from_lib, %function +discard_from_lib: + .word 0 diff --git a/ld/testsuite/ld-elf/retain6main.s b/ld/testsuite/ld-elf/retain6main.s new file mode 100644 index 0000000000..a66c5b3247 --- /dev/null +++ b/ld/testsuite/ld-elf/retain6main.s @@ -0,0 +1,13 @@ +/* Undefined symbol reference in retained section .text.retained_fn requires + symbol definition to be pulled out of library. */ + .section .text.retained_fn,"axR" + .global retained_fn + .type retained_fn, %function +retained_fn: + .long bar + + .section .text._start,"ax" + .global _start + .type _start, %function +_start: + .word 0
commit c683d66982f7e16e03e637c64f219d5a4d61ad8e Author: Jozef Lawrynowicz <joze...@mittosystems.com> Date: Thu Oct 29 21:00:07 2020 +0000 Implement TARGET_MARK_DECL_PRESERVED for ELF targets supporting SHF_GNU_RETAIN diff --git a/gcc/config.in b/gcc/config.in index 3657c46f349..585475e8908 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -1346,6 +1346,12 @@ #endif +/* Define if your assembler supports .retain directive. */ +#ifndef USED_FOR_TARGET +#undef HAVE_GAS_RETAIN +#endif + + /* Define if your assembler supports specifying the exclude section flag. */ #ifndef USED_FOR_TARGET #undef HAVE_GAS_SECTION_EXCLUDE diff --git a/gcc/config/elfos.h b/gcc/config/elfos.h index 74a3eafda6b..da6d0de44bc 100644 --- a/gcc/config/elfos.h +++ b/gcc/config/elfos.h @@ -474,3 +474,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #undef TARGET_LIBC_HAS_FUNCTION #define TARGET_LIBC_HAS_FUNCTION no_c99_libc_has_function + +#if HAVE_GAS_RETAIN +#ifndef TARGET_ASM_MARK_DECL_PRESERVED +#define TARGET_ASM_MARK_DECL_PRESERVED default_elf_mark_decl_preserved +#endif +#endif diff --git a/gcc/configure b/gcc/configure index abff47d30eb..6ee92333caf 100755 --- a/gcc/configure +++ b/gcc/configure @@ -24223,6 +24223,43 @@ cat >>confdefs.h <<_ACEOF _ACEOF +# Test if the assembler supports the .retain directive for indicating the +# section containing the specified symbol should not be garbage collected by +# the linker. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for retain directive" >&5 +$as_echo_n "checking assembler for retain directive... " >&6; } +if ${gcc_cv_as_retain_r+:} false; then : + $as_echo_n "(cached) " >&6 +else + gcc_cv_as_retain_r=no + if test x$gcc_cv_as != x; then + $as_echo '.global foo1 + .retain foo1' > 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_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_retain_r" >&5 +$as_echo "$gcc_cv_as_retain_r" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define HAVE_GAS_RETAIN `if test $gcc_cv_as_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..c32876c8c7e 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -3216,6 +3216,16 @@ 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 .retain directive for indicating the +# section containing the specified symbol should not be garbage collected by +# the linker. +gcc_GAS_CHECK_FEATURE([retain directive], gcc_cv_as_retain_r,,, + [.global foo1 + .retain foo1]) +AC_DEFINE_UNQUOTED(HAVE_GAS_RETAIN, + [`if test $gcc_cv_as_retain_r = yes; then echo 1; else echo 0; fi`], +[Define if your assembler supports .retain directive.]) + 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/output.h b/gcc/output.h index eb253c50329..c0eba372c5d 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -609,6 +609,10 @@ extern void default_elf_init_array_asm_out_constructor (rtx, int); extern void default_elf_fini_array_asm_out_destructor (rtx, int); extern int maybe_assemble_visibility (tree); +#if HAVE_GAS_RETAIN +void default_elf_mark_decl_preserved (const char *); +#endif + extern int default_address_cost (rtx, machine_mode, addr_space_t, bool); /* Stack usage. */ diff --git a/gcc/varasm.c b/gcc/varasm.c index ea0b59cf44a..618bc605a73 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -8276,6 +8276,17 @@ default_elf_fini_array_asm_out_destructor (rtx symbol, int priority) assemble_addr_to_section (symbol, sec); } +#if HAVE_GAS_RETAIN +void +default_elf_mark_decl_preserved (const char *name) +{ + fprintf (asm_out_file, "\t.retain "); + assemble_name (asm_out_file, name); + fputc ('\n', asm_out_file); +} +#endif + + /* Default TARGET_ASM_OUTPUT_IDENT hook. This is a bit of a cheat. The real default is a no-op, but this