[PATCH v2] Set the retain attribute on _elf_set_element if CC supports [BZ #27492]

Fangrui Song maskray@google.com
Mon Mar 22 04:00:44 GMT 2021


So that text_set_element/data_set_element/bss_set_element defined
variables will be retained by the linker.

Note: 'used' and 'retain' are orthogonal: 'used' makes sure the variable
will not be optimized out; 'retain' prevents section garbage collection
if the linker support SHF_GNU_RETAIN.

GNU ld 2.37 and LLD 13 will support -z start-stop-gc which allow C
identifier name sections to be GCed even if there are live
__start_/__stop_ references.

Without the change, there are some static linking problems, e.g.
_IO_cleanup (libio/genops.c) may be discarded by ld --gc-sections, so
stdout is not flushed on exit.

Note: GCC may warning ‘retain’ attribute ignored while __has_attribute(retain) is 1
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99587).
---
 config.h.in            |  3 +++
 configure              | 23 +++++++++++++++++++++++
 configure.ac           | 16 ++++++++++++++++
 include/libc-symbols.h | 30 +++++++++++++++++++++---------
 4 files changed, 63 insertions(+), 9 deletions(-)

diff --git a/config.h.in b/config.h.in
index f21bf04e47..cc875e90f9 100644
--- a/config.h.in
+++ b/config.h.in
@@ -187,6 +187,9 @@
 /* Define if gcc supports attribute ifunc.  */
 #undef HAVE_GCC_IFUNC
 
+/* Define if gcc supports attribute retain.  */
+#undef HAVE_GCC_RETAIN
+
 /* Define if the linker defines __ehdr_start.  */
 #undef HAVE_EHDR_START
 
diff --git a/configure b/configure
index 37cef37413..28494de748 100755
--- a/configure
+++ b/configure
@@ -4105,6 +4105,29 @@ fi
 $as_echo "$libc_cv_textrel_ifunc" >&6; }
 
 
+# Check if gcc supports attribute ifunc as it is used in libc_ifunc macro.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcc attribute retain support" >&5
+$as_echo_n "checking for gcc attribute retain support... " >&6; }
+if ${libc_cv_gcc_retain+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat > conftest.c <<EOF
+static int var  __attribute__ ((used, retain, section ("__libc_atexit")));
+EOF
+libc_cv_gcc_retain=no
+if ${CC-cc} -Werror -c conftest.c -o /dev/null 1>&5 \
+   2>&5 ; then
+  libc_cv_gcc_retain=yes
+fi
+rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_gcc_retain" >&5
+$as_echo "$libc_cv_gcc_retain" >&6; }
+if test $libc_cv_gcc_retain = yes; then
+  $as_echo "#define HAVE_GCC_RETAIN 1" >>confdefs.h
+
+fi
+
 # Check if gcc warns about alias for function with incompatible types.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler warns about alias for function with incompatible types" >&5
 $as_echo_n "checking if compiler warns about alias for function with incompatible types... " >&6; }
diff --git a/configure.ac b/configure.ac
index 16b15b6f90..94ad713820 100644
--- a/configure.ac
+++ b/configure.ac
@@ -707,6 +707,22 @@ fi
 rm -f conftest*])
 AC_SUBST(libc_cv_textrel_ifunc)
 
+# Check if gcc supports attribute ifunc as it is used in libc_ifunc macro.
+AC_CACHE_CHECK([for gcc attribute retain support],
+	       libc_cv_gcc_retain, [dnl
+cat > conftest.c <<EOF
+static int var  __attribute__ ((used, retain, section ("__libc_atexit")));
+EOF
+libc_cv_gcc_retain=no
+if ${CC-cc} -Werror -c conftest.c -o /dev/null 1>&AS_MESSAGE_LOG_FD \
+   2>&AS_MESSAGE_LOG_FD ; then
+  libc_cv_gcc_retain=yes
+fi
+rm -f conftest*])
+if test $libc_cv_gcc_retain = yes; then
+  AC_DEFINE(HAVE_GCC_RETAIN)
+fi
+
 # Check if gcc warns about alias for function with incompatible types.
 AC_CACHE_CHECK([if compiler warns about alias for function with incompatible types],
 	       libc_cv_gcc_incompatible_alias, [dnl
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index c83e550b03..f8eafcf1f3 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -373,17 +373,29 @@ for linking")
 
 /* These are all done the same way in ELF.
    There is a new section created for each set.  */
-#ifdef SHARED
+#ifdef HAVE_GCC_RETAIN
+# ifdef SHARED
 /* When building a shared library, make the set section writable,
-   because it will need to be relocated at run time anyway.  */
-# define _elf_set_element(set, symbol) \
-  static const void *__elf_set_##set##_element_##symbol##__ \
-    __attribute__ ((used, section (#set))) = &(symbol)
+  because it will need to be relocated at run time anyway.  */
+#  define _elf_set_element(set, symbol) \
+     static const void *__elf_set_##set##_element_##symbol##__ \
+       __attribute__ ((used, retain, section (#set))) = &(symbol)
+# else
+#  define _elf_set_element(set, symbol) \
+     static const void *const __elf_set_##set##_element_##symbol##__ \
+       __attribute__ ((used, retain, section (#set))) = &(symbol)
+# endif
 #else
-# define _elf_set_element(set, symbol) \
-  static const void *const __elf_set_##set##_element_##symbol##__ \
-    __attribute__ ((used, section (#set))) = &(symbol)
-#endif
+# ifdef SHARED
+#  define _elf_set_element(set, symbol) \
+     static const void *__elf_set_##set##_element_##symbol##__ \
+       __attribute__ ((used, section (#set))) = &(symbol)
+# else
+#  define _elf_set_element(set, symbol) \
+     static const void *const __elf_set_##set##_element_##symbol##__ \
+       __attribute__ ((used, section (#set))) = &(symbol)
+# endif
+#endif /* HAVE_GCC_RETAIN  */
 
 /* Define SET as a symbol set.  This may be required (it is in a.out) to
    be able to use the set's contents.  */
-- 
2.31.0.rc2.261.g7f71774620-goog



More information about the Libc-alpha mailing list