[PATCH v4] Support SHF_GNU_RETAIN ELF section flag

Jozef Lawrynowicz jozef.l@mittosystems.com
Tue Nov 3 18:11:18 GMT 2020


On Tue, Nov 03, 2020 at 09:43:36AM -0800, H.J. Lu via Binutils wrote:
> On Tue, Nov 3, 2020 at 9:33 AM Jozef Lawrynowicz
> <jozef.l@mittosystems.com> wrote:
> >
> > The attached patch is the latest version of the Binutils implementation
> > to support the SHF_GNU_RETAIN ELF GNU OSABI section flag, used to save
> > sections from linker garbage collection.
> >
> > Links to previous discussions are available here:
> > https://sourceware.org/pipermail/gnu-gabi/2020q3/000429.html
> > https://sourceware.org/pipermail/binutils/2020-September/113406.html
> > https://sourceware.org/pipermail/binutils/2020-October/113559.html
> > https://sourceware.org/pipermail/binutils/2020-October/113769.html
> >
> > The intention with this latest patch is to support declarations in the
> > source code marked with the "used" attribute being saved from linker
> > garbage collection. The previously discussed "retain" GCC attribute has
> > been removed.
> >
> > For declarations marked with the "used" attribute, GCC will emit a
> > ".retain <symname>" directive, where <symname> is the name of the
> > symbol. This hooks into existing GCC functionality for marking a
> > symbol as "preserved" (currently used only by Darwin).
> 
> I don't believe that the .retain directive is needed.

I know we previously discussed that a ".retain <section_name>" directive
is not required, and I agree.

However, ".retain <symbol_name>" to save the section containing the
symbol is beneficial.

Firstly, there is a precedent for this already with the GCC target hook
TARGET_ASM_MARK_DECL_PRESERVED, currently used by Darwin.

Secondly, for seamless integration with the "used" attribute, we must be
able to to mark the symbol with the used attribute applied as "retained"
without changing its section name. For GCC "named" sections, this is
straightforward, but for "unnamed" sections it is a giant mess.

The section name for a GCC "unnamed" section is not readily available,
instead a string which contains the full assembly code to switch to one
of these text/data/bss/rodata/comm etc. sections is encoded in the
structure.

Backends define the assembly code to switch to these sections (some
"*ASM_OP*" macro) in a variety of ways. For example, the unnamed section
"comm_section", might correspond to a .bss section, or emit a .comm
directive. I even looked at trying to parse them to extract what the
name of a section will be, but it would be very messy and not robust.

Meanwhile, having a .retain <symbol_name> directive is a very simmple
solution, and keeps the GCC implementation really concise (patch
attached). The assembler will know for sure what the section containing
the symbol will be, and can apply the SHF_GNU_RETAIN flag directly.

Finally, having a .retain directive means that we don't need to support
multiple sections with the same name, but different states for the "R"
flag. For example, and Fangrui raised this point in previous discussion,
the following is undesirable, as it violates the rule we have about
section flags set in .section directives being the same for sections of
the same name:

  .section .text,"ax",%progbits
   ...
  .section .text,"axR",%progbits
  ....


The above would be required if GCC can only mark decls are retained by
explicitly placing them in a section with the SHF_GNU_RETAIN flag
applied. The .retain <symbol_name> directive greatly simplifies the
process for GCC.

> 
> 
> > GAS supports the .retain directive, and will apply the SHF_GNU_RETAIN
> > flag to the section containing the symbol named in the .retain
> > directive.
> > GAS supports the "R" flag to the .section directive, to mark a section
> > with the SHF_GNU_RETAIN flag.
> >
> > GCC will not emit .section directives with the "R" flag set, so
> 
> I have a GCC patch to use the "o" flag:
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557675.html
> https://gcc.gnu.org/pipermail/gcc-patches/2020-February/539963.html
> 
> and GCC should use the "R" flag for ELF targets.

As far as I'm aware, these "patchable" sections always have a specific
name, and the GCC "unnamed" section mechanism described above is never
used for patchable functions. The "used" attribute applying
SHF_GNU_RETAIN must support these unnamed sections, so it integrates
without any change in behavior to the section the symbol is placed in.
Therefore, unfortunately the functionality in your patch is not enough
to support the "used" attribute applying SHF_GNU_RETAIN.

Thanks,
Jozef
> 
> 
> --
> H.J.
-------------- next part --------------
>From 0827e28480b7edd07cda4f938bdd14b1cbdf1fa2 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Thu, 29 Oct 2020 21:00:07 +0000
Subject: [PATCH] Implement TARGET_MARK_DECL_PRESERVED for ELF GNU OSABI
 targets

The GAS .retain directive will apply the SHF_GNU_RETAIN flag to the
section containing the symbol that must be preserved.

gcc/ChangeLog:

	* config.in: Regenerate.
	* config/elfos.h (TARGET_ASM_MARK_DECL_PRESERVED): Define for
	HAVE_GAS_RETAIN.
	* configure: Regenerate.
	* configure.ac: Define HAVE_GAS_RETAIN.
	* doc/extend.texi (used attribute): Document saving from linker garbage
	collection.
	* doc/sourcebuild.texi: Document "retain" effective target keyword.
	* doc/tm.texi: Regenerate.
	* output.h (default_elf_mark_decl_preserved): New.
	* target.def (mark_decl_preserved): Mention GAS .retain directive.
	* varasm.c (default_elf_mark_decl_preserved): New.

gcc/testsuite/ChangeLog:

	* c-c++-common/attr-used-2.c: Test for .retain in assembler output.
	* c-c++-common/attr-used.c: Likewise.
	* lib/target-supports.exp (check_effective_target_retain): New.
---
 gcc/config.in                            |  6 ++++
 gcc/config/elfos.h                       |  7 +++++
 gcc/configure                            | 35 ++++++++++++++++++++++++
 gcc/configure.ac                         |  8 ++++++
 gcc/doc/extend.texi                      |  6 ++++
 gcc/doc/sourcebuild.texi                 |  3 ++
 gcc/doc/tm.texi                          |  2 +-
 gcc/output.h                             |  4 +++
 gcc/target.def                           |  2 +-
 gcc/testsuite/c-c++-common/attr-used-2.c |  1 +
 gcc/testsuite/c-c++-common/attr-used.c   |  2 ++
 gcc/testsuite/lib/target-supports.exp    |  9 ++++++
 gcc/varasm.c                             | 13 +++++++++
 13 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/gcc/config.in b/gcc/config.in
index 3657c46f349..8ef075a0ff3 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1346,6 +1346,12 @@
 #endif
 
 
+/* Define if your assembler supports the .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..fab7b0e8ea4 100644
--- a/gcc/config/elfos.h
+++ b/gcc/config/elfos.h
@@ -474,3 +474,10 @@ 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 the assembler supports the .retain directive for saving a symbol
+   from linker garbage collection, define this macro.  */
+#if HAVE_GAS_RETAIN
+#undef TARGET_ASM_MARK_DECL_PRESERVED
+#define TARGET_ASM_MARK_DECL_PRESERVED default_elf_mark_decl_preserved
+#endif
diff --git a/gcc/configure b/gcc/configure
index abff47d30eb..37488eac25d 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -24223,6 +24223,41 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+# Test if the assembler supports the .retain directive for saving a symbol from
+# linker garbage collection.
+{ $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 '.retain retain_sym' > 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..08b38d894a3 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -3216,6 +3216,14 @@ 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 saving a symbol from
+# linker garbage collection.
+gcc_GAS_CHECK_FEATURE([retain directive], gcc_cv_as_retain_r,,,
+ [.retain retain_sym])
+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 the .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/doc/extend.texi b/gcc/doc/extend.texi
index 62549b02452..4f77a5c0229 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3810,6 +3810,9 @@ When applied to a member function of a C++ class template, the
 attribute also means that the function is instantiated if the
 class itself is instantiated.
 
+As a GNU ELF extension, functions with this attribute will not be
+garbage collected by the linker.
+
 @item visibility ("@var{visibility_type}")
 @cindex @code{visibility} function attribute
 This attribute affects the linkage of the declaration to which it is attached.
@@ -7269,6 +7272,9 @@ When applied to a static data member of a C++ class template, the
 attribute also means that the member is instantiated if the
 class itself is instantiated.
 
+As a GNU ELF extension, variables with this attribute will not be
+garbage collected by the linker.
+
 @item vector_size (@var{bytes})
 @cindex @code{vector_size} variable attribute
 This attribute specifies the vector size for the type of the declared
diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index 49316a5d0ff..7fe77e7f09e 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -2551,6 +2551,9 @@ Target supports @option{-pie}, @option{-fpie} and @option{-fPIE}.
 @item rdynamic
 Target supports @option{-rdynamic}.
 
+@item retain
+Target supports the @code{.retain} assembler directive.
+
 @item scalar_all_fma
 Target supports all four fused multiply-add optabs for both @code{float}
 and @code{double}.  These optabs are: @code{fma_optab}, @code{fms_optab},
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 97437e8274f..b074b2ff75b 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -8773,7 +8773,7 @@ library function is given by @var{symref}, which is a @code{symbol_ref}.
 @deftypefn {Target Hook} void TARGET_ASM_MARK_DECL_PRESERVED (const char *@var{symbol})
 This target hook is a function to output to @var{asm_out_file} an assembler
 directive to annotate @var{symbol} as used.  The Darwin target uses the
-.no_dead_code_strip directive.
+.no_dead_code_strip directive, and ELF targets use the .retain directive.
 @end deftypefn
 
 @defmac ASM_OUTPUT_LABELREF (@var{stream}, @var{name})
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/target.def b/gcc/target.def
index ed2da154e30..12164792b00 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -737,7 +737,7 @@ DEFHOOK
 (mark_decl_preserved,
  "This target hook is a function to output to @var{asm_out_file} an assembler\n\
 directive to annotate @var{symbol} as used.  The Darwin target uses the\n\
-.no_dead_code_strip directive.",
+.no_dead_code_strip directive, and ELF targets use the .retain directive.",
  void, (const char *symbol),
  hook_void_constcharptr)
 
diff --git a/gcc/testsuite/c-c++-common/attr-used-2.c b/gcc/testsuite/c-c++-common/attr-used-2.c
index f78b94b53a9..cf1d25a5b27 100644
--- a/gcc/testsuite/c-c++-common/attr-used-2.c
+++ b/gcc/testsuite/c-c++-common/attr-used-2.c
@@ -9,3 +9,4 @@ void foo()
 }
 
 /* { dg-final { scan-assembler "xyzzy" } } */
+/* { dg-final { scan-assembler "\\.retain\t\.*xyzzy" { target retain } } } */
diff --git a/gcc/testsuite/c-c++-common/attr-used.c b/gcc/testsuite/c-c++-common/attr-used.c
index ba7705aaa77..65a2f029698 100644
--- a/gcc/testsuite/c-c++-common/attr-used.c
+++ b/gcc/testsuite/c-c++-common/attr-used.c
@@ -11,3 +11,5 @@ static void function_declaration_after(void) __attribute__((__used__));
 
 /* { dg-final { scan-assembler "function_declaration_before" } } */
 /* { dg-final { scan-assembler "function_declaration_after" } } */
+/* { dg-final { scan-assembler "\\.retain\t\.*function_declaration_before" { target retain } } } */
+/* { dg-final { scan-assembler "\\.retain\t\.*function_declaration_after" { target retain } } } */
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 8439720baea..7c0e925f7b4 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -380,6 +380,15 @@ proc check_effective_target_noinit { } {
     return 0
 }
 
+# The .retain assembler directive is only supported by some targets.
+# This proc returns 1 if it's supported, 0 if it's not.
+
+proc check_effective_target_retain { } {
+    return [check_no_compiler_messages retain_available object {
+	__asm__(".retain used_var");
+    }]
+}
+
 ###############################
 # proc check_visibility_available { what_kind }
 ###############################
diff --git a/gcc/varasm.c b/gcc/varasm.c
index ea0b59cf44a..c38640456c4 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -8276,6 +8276,19 @@ default_elf_fini_array_asm_out_destructor (rtx symbol, int priority)
   assemble_addr_to_section (symbol, sec);
 }
 
+
+#if HAVE_GAS_RETAIN
+/* Implement TARGET_ASM_MARK_DECL_PRESERVED for ELF targets that support the
+   .retain assembler directive.  */
+void
+default_elf_mark_decl_preserved (const char *name)
+{
+  fprintf (asm_out_file, "\t.retain\t");
+  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
-- 
2.28.0



More information about the Binutils mailing list