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

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).

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
This commit is contained in:
Fangrui Song 2021-04-16 11:26:39 -07:00
parent 1a8605b6cd
commit cd6ae7ea54
13 changed files with 162 additions and 4 deletions

View File

@ -187,6 +187,9 @@
/* Define if gcc supports attribute ifunc. */ /* Define if gcc supports attribute ifunc. */
#undef HAVE_GCC_IFUNC #undef HAVE_GCC_IFUNC
/* Define if CC supports attribute retain. */
#undef HAVE_GNU_RETAIN
/* Define if the linker defines __ehdr_start. */ /* Define if the linker defines __ehdr_start. */
#undef HAVE_EHDR_START #undef HAVE_EHDR_START

59
configure vendored
View File

@ -4105,6 +4105,31 @@ fi
$as_echo "$libc_cv_textrel_ifunc" >&6; } $as_echo "$libc_cv_textrel_ifunc" >&6; }
# Check if CC supports attribute retain as it is used in attribute_used_retain macro.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU attribute retain support" >&5
$as_echo_n "checking for GNU attribute retain support... " >&6; }
if ${libc_cv_gnu_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_gnu_retain=no
if ${CC-cc} -Werror -c conftest.c -o /dev/null 1>&5 \
2>&5 ; then
libc_cv_gnu_retain=yes
fi
rm -f conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_gnu_retain" >&5
$as_echo "$libc_cv_gnu_retain" >&6; }
if test $libc_cv_gnu_retain = yes; then
$as_echo "#define HAVE_GNU_RETAIN 1" >>confdefs.h
fi
config_vars="$config_vars
have-gnu-retain = $libc_cv_gnu_retain"
# Check if gcc warns about alias for function with incompatible types. # 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 "$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; } $as_echo_n "checking if compiler warns about alias for function with incompatible types... " >&6; }
@ -5871,6 +5896,40 @@ fi
$as_echo "$libc_linker_feature" >&6; } $as_echo "$libc_linker_feature" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports -z start-stop-gc" >&5
$as_echo_n "checking for linker that supports -z start-stop-gc... " >&6; }
libc_linker_feature=no
if test x"$gnu_ld" = x"yes"; then
libc_linker_check=`$LD -v --help 2>/dev/null | grep "\-z start-stop-gc"`
if test -n "$libc_linker_check"; then
cat > conftest.c <<EOF
int _start (void) { return 42; }
EOF
if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
-Wl,-z,start-stop-gc -nostdlib -nostartfiles
-fPIC -shared -o conftest.so conftest.c
1>&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
libc_linker_feature=yes
fi
rm -f conftest*
fi
fi
if test $libc_linker_feature = yes; then
libc_cv_z_start_stop_gc=yes
else
libc_cv_z_start_stop_gc=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5
$as_echo "$libc_linker_feature" >&6; }
config_vars="$config_vars
have-z-start-stop-gc = $libc_cv_z_start_stop_gc"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports --no-dynamic-linker" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports --no-dynamic-linker" >&5
$as_echo_n "checking for linker that supports --no-dynamic-linker... " >&6; } $as_echo_n "checking for linker that supports --no-dynamic-linker... " >&6; }
libc_linker_feature=no libc_linker_feature=no

View File

@ -707,6 +707,23 @@ fi
rm -f conftest*]) rm -f conftest*])
AC_SUBST(libc_cv_textrel_ifunc) AC_SUBST(libc_cv_textrel_ifunc)
# Check if CC supports attribute retain as it is used in attribute_used_retain macro.
AC_CACHE_CHECK([for GNU attribute retain support],
libc_cv_gnu_retain, [dnl
cat > conftest.c <<EOF
static int var __attribute__ ((used, retain, section ("__libc_atexit")));
EOF
libc_cv_gnu_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_gnu_retain=yes
fi
rm -f conftest*])
if test $libc_cv_gnu_retain = yes; then
AC_DEFINE(HAVE_GNU_RETAIN)
fi
LIBC_CONFIG_VAR([have-gnu-retain], [$libc_cv_gnu_retain])
# Check if gcc warns about alias for function with incompatible types. # Check if gcc warns about alias for function with incompatible types.
AC_CACHE_CHECK([if compiler 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 libc_cv_gcc_incompatible_alias, [dnl
@ -1317,6 +1334,10 @@ LIBC_LINKER_FEATURE([-z execstack], [-Wl,-z,execstack],
[libc_cv_z_execstack=yes], [libc_cv_z_execstack=no]) [libc_cv_z_execstack=yes], [libc_cv_z_execstack=no])
AC_SUBST(libc_cv_z_execstack) AC_SUBST(libc_cv_z_execstack)
LIBC_LINKER_FEATURE([-z start-stop-gc], [-Wl,-z,start-stop-gc],
[libc_cv_z_start_stop_gc=yes], [libc_cv_z_start_stop_gc=no])
LIBC_CONFIG_VAR([have-z-start-stop-gc], [$libc_cv_z_start_stop_gc])
LIBC_LINKER_FEATURE([--no-dynamic-linker], LIBC_LINKER_FEATURE([--no-dynamic-linker],
[-Wl,--no-dynamic-linker], [-Wl,--no-dynamic-linker],
[libc_cv_no_dynamic_linker=yes], [libc_cv_no_dynamic_linker=yes],

View File

@ -352,6 +352,12 @@ for linking")
*/ */
#ifdef HAVE_GNU_RETAIN
# define attribute_used_retain __attribute__ ((__used__, __retain__))
#else
# define attribute_used_retain __attribute__ ((__used__))
#endif
/* Symbol set support macros. */ /* Symbol set support macros. */
/* Make SYMBOL, which is in the text segment, an element of SET. */ /* Make SYMBOL, which is in the text segment, an element of SET. */
@ -367,12 +373,12 @@ for linking")
/* When building a shared library, make the set section writable, /* When building a shared library, make the set section writable,
because it will need to be relocated at run time anyway. */ because it will need to be relocated at run time anyway. */
# define _elf_set_element(set, symbol) \ # define _elf_set_element(set, symbol) \
static const void *__elf_set_##set##_element_##symbol##__ \ static const void *__elf_set_##set##_element_##symbol##__ \
__attribute__ ((used, section (#set))) = &(symbol) attribute_used_retain __attribute__ ((section (#set))) = &(symbol)
#else #else
# define _elf_set_element(set, symbol) \ # define _elf_set_element(set, symbol) \
static const void *const __elf_set_##set##_element_##symbol##__ \ static const void *const __elf_set_##set##_element_##symbol##__ \
__attribute__ ((used, section (#set))) = &(symbol) attribute_used_retain __attribute__ ((section (#set))) = &(symbol)
#endif #endif
/* Define SET as a symbol set. This may be required (it is in a.out) to /* Define SET as a symbol set. This may be required (it is in a.out) to

View File

@ -195,6 +195,26 @@ ifeq (yes,$(build-shared))
tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out \ tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out \
$(objpfx)tst-bz24228-mem.out $(objpfx)tst-bz24228-mem.out
endif endif
tests += tst-cleanup-default tst-cleanup-default-static
tests-static += tst-cleanup-default-static
tests-special += $(objpfx)tst-cleanup-default-cmp.out $(objpfx)tst-cleanup-default-static-cmp.out
LDFLAGS-tst-cleanup-default = -Wl,--gc-sections
LDFLAGS-tst-cleanup-default-static = -Wl,--gc-sections
ifeq ($(have-gnu-retain)$(have-z-start-stop-gc),yesyes)
tests += tst-cleanup-start-stop-gc tst-cleanup-start-stop-gc-static \
tst-cleanup-nostart-stop-gc tst-cleanup-nostart-stop-gc-static
tests-static += tst-cleanup-start-stop-gc-static tst-cleanup-nostart-stop-gc-static
tests-special += $(objpfx)tst-cleanup-start-stop-gc-cmp.out \
$(objpfx)tst-cleanup-start-stop-gc-static-cmp.out \
$(objpfx)tst-cleanup-nostart-stop-gc-cmp.out \
$(objpfx)tst-cleanup-nostart-stop-gc-static-cmp.out
LDFLAGS-tst-cleanup-start-stop-gc := -Wl,--gc-sections,-z,start-stop-gc
LDFLAGS-tst-cleanup-start-stop-gc-static := -Wl,--gc-sections,-z,start-stop-gc
LDFLAGS-tst-cleanup-nostart-stop-gc := -Wl,--gc-sections,-z,nostart-stop-gc
LDFLAGS-tst-cleanup-nostart-stop-gc-static := -Wl,--gc-sections,-z,nostart-stop-gc
endif
endif endif
include ../Rules include ../Rules
@ -224,6 +244,14 @@ $(objpfx)tst_wprintf2.out: $(gen-locales)
$(objpfx)tst-wfile-sync.out: $(gen-locales) $(objpfx)tst-wfile-sync.out: $(gen-locales)
endif endif
define gen-tst-cleanup
$(objpfx)tst-cleanup-$1-cmp.out: tst-cleanup.exp $(objpfx)tst-cleanup-$1.out
cmp $$^ > $$@; $$(evaluate-test)
endef
$(foreach t,default default-static start-stop-gc start-stop-gc-static nostart-stop-gc nostart-stop-gc-static, \
$(eval $(call gen-tst-cleanup,$(t))))
$(objpfx)test-freopen.out: test-freopen.sh $(objpfx)test-freopen $(objpfx)test-freopen.out: test-freopen.sh $(objpfx)test-freopen
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' \ $(SHELL) $< $(common-objpfx) '$(test-program-prefix)' \
$(common-objpfx)libio/; \ $(common-objpfx)libio/; \

View File

@ -0,0 +1 @@
#include "tst-cleanup.c"

View File

@ -0,0 +1 @@
#include "tst-cleanup.c"

View File

@ -0,0 +1 @@
#include "tst-cleanup.c"

View File

@ -0,0 +1 @@
#include "tst-cleanup.c"

View File

@ -0,0 +1 @@
#include "tst-cleanup.c"

View File

@ -0,0 +1 @@
#include "tst-cleanup.c"

34
libio/tst-cleanup.c Normal file
View File

@ -0,0 +1,34 @@
/* Copyright (C) 2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Test that stdout is flushed after atexit callbacks were run, even if the
* executable is linked with --gc-sections. */
#include <stdio.h>
#include <stdlib.h>
void
hook (void)
{
puts ("hello");
}
int
main (void)
{
atexit (hook);
}

1
libio/tst-cleanup.exp Normal file
View File

@ -0,0 +1 @@
hello