On Thu, Aug 28, 2025 at 7:23 AM Sam James <[email protected]> wrote:
>
> GNU2 TLS descriptors were introduced in 2006 (r0-73091-g5bf5a10b1ccacf)
> but were only opt-in with -mtls-dialect=gnu2. They are more efficient
> and it's time to enable them by default.
>
> Builds on the --with-tls= machinery from r16-3355-g96a291c4bb0b8a.
>
> We achieve this for GNU/Linux IA-32/X86-64 targets by checking if ld emits
> GLIBC_ABI_GNU2_TLS, using its presence to decide if we can default to
> -mtls-dialect=gnu2.
>
> For PR ld/33130, newer ld will add GLIBC_ABI_GNU2_TLS if either unconfigured
> (auto mode) or if configured with --enable-gnu2-tls-tag. In auto mode,
> GLIBC_ABI_GNU2_TLS is only added if glibc provides it. In explicit mode, the
> user has asked for this behavior and binaries will depend on
> GLIBC_ABI_GNU2_TLS
> and fixed glibc. Hence the presence of GLIBC_ABI_GNU2_TLS tells us if we can
> safely default to GNU2 TLS descriptors. We added GLIBC_ABI_GNU2_TLS in glibc
> to indicate that PR dynamic-link/33129 is fixed.
>
> If distributions wish to opt-out of this for systems which meet the above
> conditions, they can either configure GCC using --with-tls=gnu, or configure
> binutils with --disable-gnu2-tls-tag: if this is necessary, it is recommended
> to use --with-tls=gnu instead, to avoid affecting the ecosystem negatively by
> having unmarked binaries.
I wonder if we can piggy-back on the existing --with-glibc-version=... somehow?
> Some implementation notes:
> * The readelf check had to be moved earlier because we want
> to set `with_tls` before `config.gcc` is processed (which has default
> machinery for TLS).
>
> * The check doesn't really handle cross, but I don't see
> this as a huge problem. The check is already opportunistic and if it
> fails, it falls back to --with-tls=DIALECT if passed, and failing that,
> the previous and safe default of 'gnu'.
>
> * The change is only made for glibc systems at this time. Enablement and
> testing
> can be done for other libcs as future work.
>
> * In future, we may do the same thing for ARM if/when appropriate equivalent
> machinery is added to glibc and bfd. This makes the separate position of
> the check (not with some of the others) a bit more palatable IMO.
>
> gcc/ChangeLog:
> PR target/120933
> * configure: Regenerate.
> * configure.ac (gcc_cv_readelf): Move check earlier.
> (gcc_cv_libc_x86_tlsdesc_call): Define to 'yes' if
> glibc has the GLIBC_ABI_GNU2_TLS version tag and ld emits it.
> (with_tls): Default to 'gnu2' if --with-tls is not passed and
> gcc_cv_libc_x86_tlsdesc_call is 'yes'.
> ---
> OK?
>
> gcc/configure | 217 +++++++++++++++++++++++++++++++----------------
> gcc/configure.ac | 112 ++++++++++++++++++------
> 2 files changed, 229 insertions(+), 100 deletions(-)
>
> diff --git a/gcc/configure b/gcc/configure
> index 4a751d969bab..ba303469613c 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -737,7 +737,6 @@ libgcc_visibility
> ORIGINAL_DSYMUTIL_FOR_TARGET
> gcc_cv_dsymutil
> gcc_cv_otool
> -gcc_cv_readelf
> gcc_cv_objdump
> ORIGINAL_NM_FOR_TARGET
> gcc_cv_nm
> @@ -801,6 +800,7 @@ HAVE_AUTO_BUILD
> extra_opt_files
> extra_modes_file
> NATIVE_SYSTEM_HEADER_DIR
> +gcc_cv_readelf
> objext
> manext
> LIBICONV_DEP
> @@ -12927,6 +12927,145 @@ if test "x$enable_win32_utf8_manifest" != xno; then
> host_extra_objs_mingw=utf8-mingw32.o
> fi
>
> +
> +# Figure out what readelf we will be using.
> +if ${gcc_cv_readelf+:} false; then :
> +
> +else
> +
> +if test -f $gcc_cv_binutils_srcdir/configure.ac \
> + && test -f ../binutils/Makefile \
> + && test x$build = x$host; then
> + # Single tree build which includes binutils.
> + gcc_cv_readelf=../binutils/readelf$build_exeext
> +elif test -x readelf$build_exeext; then
> + gcc_cv_readelf=./readelf$build_exeext
> +elif ( set dummy $READELF_FOR_TARGET; test -x $2 ); then
> + gcc_cv_readelf="$READELF_FOR_TARGET"
> +else
> + # Extract the first word of "$READELF_FOR_TARGET", so it can be a
> program name with args.
> +set dummy $READELF_FOR_TARGET; ac_word=$2
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
> +$as_echo_n "checking for $ac_word... " >&6; }
> +if ${ac_cv_path_gcc_cv_readelf+:} false; then :
> + $as_echo_n "(cached) " >&6
> +else
> + case $gcc_cv_readelf in
> + [\\/]* | ?:[\\/]*)
> + ac_cv_path_gcc_cv_readelf="$gcc_cv_readelf" # Let the user override the
> test with a path.
> + ;;
> + *)
> + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
> +for as_dir in $PATH
> +do
> + IFS=$as_save_IFS
> + test -z "$as_dir" && as_dir=.
> + for ac_exec_ext in '' $ac_executable_extensions; do
> + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
> + ac_cv_path_gcc_cv_readelf="$as_dir/$ac_word$ac_exec_ext"
> + $as_echo "$as_me:${as_lineno-$LINENO}: found
> $as_dir/$ac_word$ac_exec_ext" >&5
> + break 2
> + fi
> +done
> + done
> +IFS=$as_save_IFS
> +
> + ;;
> +esac
> +fi
> +gcc_cv_readelf=$ac_cv_path_gcc_cv_readelf
> +if test -n "$gcc_cv_readelf"; then
> + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_readelf" >&5
> +$as_echo "$gcc_cv_readelf" >&6; }
> +else
> + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +fi
> +
> +
> +fi
> +fi
> +
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking what readelf to use" >&5
> +$as_echo_n "checking what readelf to use... " >&6; }
> +if test "$gcc_cv_readelf" = ../binutils/readelf$build_exeext; then
> + # Single tree build which includes binutils.
> + { $as_echo "$as_me:${as_lineno-$LINENO}: result: newly built readelf"
> >&5
> +$as_echo "newly built readelf" >&6; }
> +elif test x$gcc_cv_readelf = x; then
> + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
> +$as_echo "not found" >&6; }
> +else
> + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_readelf" >&5
> +$as_echo "$gcc_cv_readelf" >&6; }
> +fi
> +
> +case $target in
> + i[34567]86-*-gnu* | x86_64-*-gnu* )
> + # PR target/120933
> + # For GNU/Linux targets, check if ld emits GLIBC_ABI_GNU2_TLS. For
> PR ld/33130,
> + # newer ld will add GLIBC_ABI_GNU2_TLS if either unconfigured (auto
> mode) or if
> + # configured with --enable-gnu2-tls-tag. In auto mode,
> GLIBC_ABI_GNU2_TLS
> + # is only added if glibc provides it. In explicit mode, the user has
> asked
> + # for this behavior and binaries will depend on GLIBC_ABI_GNU2_TLS
> and fixed
> + # glibc. Hence the presence of GLIBC_ABI_GNU2_TLS tells us if we can
> safely
> + # default to GNU2 TLS descriptors.
> + conftest_S='
> + .section .text.startup,"ax",@progbits
> + .p2align 4
> + .globl main
> + .type main, @function
> + main:
> + #ifdef __x86_64__
> + leaq foo@TLSDESC(%rip), %rax
> + call *foo@TLSCALL(%rax)
> + movl %fs:(%rax), %eax
> + #else
> + leal ld@TLSDESC(%ebx), %eax
> + call *ld@TLSCALL(%eax)
> + addl %gs:0, %eax
> + #endif
> + ret
> + .size main, .-main
> + .section .note.GNU-stack,"",@progbits
> + '
> +
> + { $as_echo "$as_me:${as_lineno-$LINENO}: checking libc has
> GLIBC_ABI_GNU2_TLS symbol dep and ld emits it" >&5
> +$as_echo_n "checking libc has GLIBC_ABI_GNU2_TLS symbol dep and ld emits
> it... " >&6; }
> +if ${gcc_cv_libc_x86_tlsdesc_call+:} false; then :
> + $as_echo_n "(cached) " >&6
> +else
> +
> + gcc_cv_libc_x86_tlsdesc_call=no
> + echo "$conftest_S" > conftest.S
> + if $CC $CFLAGS conftest.S -o conftest -shared > /dev/null
> 2>&1; then
> + if test x$gcc_cv_readelf != x; then
> + if $gcc_cv_readelf --version-info conftest
> 2>&1 \
> + | grep "GLIBC_ABI_GNU2_TLS" >
> /dev/null 2>&1; then
> + gcc_cv_libc_x86_tlsdesc_call=yes
> + else
> + gcc_cv_libc_x86_tlsdesc_call=no
> + fi
> + fi
> + fi
> + rm -f conftest.*
> +
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result:
> $gcc_cv_libc_x86_tlsdesc_call" >&5
> +$as_echo "$gcc_cv_libc_x86_tlsdesc_call" >&6; }
> +
> + # Set with_tls only if it's not already set via --with-tls=DIALECT
> + case "$gcc_cv_libc_x86_tlsdesc_call" in
> + yes)
> + with_tls=${with_tls:-gnu2}
> + ;;
> + *)
> + with_tls=${with_tls:-gnu}
> + ;;
> + esac
> + ;;
> +esac
> +
> # --------------------------------------------------------
> # Build, host, and target specific configuration fragments
> # --------------------------------------------------------
> @@ -21484,7 +21623,7 @@ else
> lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
> lt_status=$lt_dlunknown
> cat > conftest.$ac_ext <<_LT_EOF
> -#line 21487 "configure"
> +#line 21626 "configure"
> #include "confdefs.h"
>
> #if HAVE_DLFCN_H
> @@ -21590,7 +21729,7 @@ else
> lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
> lt_status=$lt_dlunknown
> cat > conftest.$ac_ext <<_LT_EOF
> -#line 21593 "configure"
> +#line 21732 "configure"
> #include "confdefs.h"
>
> #if HAVE_DLFCN_H
> @@ -25419,78 +25558,6 @@ else
> $as_echo "$gcc_cv_objdump" >&6; }
> fi
>
> -# Figure out what readelf we will be using.
> -if ${gcc_cv_readelf+:} false; then :
> -
> -else
> -
> -if test -f $gcc_cv_binutils_srcdir/configure.ac \
> - && test -f ../binutils/Makefile \
> - && test x$build = x$host; then
> - # Single tree build which includes binutils.
> - gcc_cv_readelf=../binutils/readelf$build_exeext
> -elif test -x readelf$build_exeext; then
> - gcc_cv_readelf=./readelf$build_exeext
> -elif ( set dummy $READELF_FOR_TARGET; test -x $2 ); then
> - gcc_cv_readelf="$READELF_FOR_TARGET"
> -else
> - # Extract the first word of "$READELF_FOR_TARGET", so it can be a
> program name with args.
> -set dummy $READELF_FOR_TARGET; ac_word=$2
> -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
> -$as_echo_n "checking for $ac_word... " >&6; }
> -if ${ac_cv_path_gcc_cv_readelf+:} false; then :
> - $as_echo_n "(cached) " >&6
> -else
> - case $gcc_cv_readelf in
> - [\\/]* | ?:[\\/]*)
> - ac_cv_path_gcc_cv_readelf="$gcc_cv_readelf" # Let the user override the
> test with a path.
> - ;;
> - *)
> - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
> -for as_dir in $PATH
> -do
> - IFS=$as_save_IFS
> - test -z "$as_dir" && as_dir=.
> - for ac_exec_ext in '' $ac_executable_extensions; do
> - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
> - ac_cv_path_gcc_cv_readelf="$as_dir/$ac_word$ac_exec_ext"
> - $as_echo "$as_me:${as_lineno-$LINENO}: found
> $as_dir/$ac_word$ac_exec_ext" >&5
> - break 2
> - fi
> -done
> - done
> -IFS=$as_save_IFS
> -
> - ;;
> -esac
> -fi
> -gcc_cv_readelf=$ac_cv_path_gcc_cv_readelf
> -if test -n "$gcc_cv_readelf"; then
> - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_readelf" >&5
> -$as_echo "$gcc_cv_readelf" >&6; }
> -else
> - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> -$as_echo "no" >&6; }
> -fi
> -
> -
> -fi
> -fi
> -
> -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking what readelf to use" >&5
> -$as_echo_n "checking what readelf to use... " >&6; }
> -if test "$gcc_cv_readelf" = ../binutils/readelf$build_exeext; then
> - # Single tree build which includes binutils.
> - { $as_echo "$as_me:${as_lineno-$LINENO}: result: newly built readelf"
> >&5
> -$as_echo "newly built readelf" >&6; }
> -elif test x$gcc_cv_readelf = x; then
> - { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
> -$as_echo "not found" >&6; }
> -else
> - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_readelf" >&5
> -$as_echo "$gcc_cv_readelf" >&6; }
> -fi
> -
> # Figure out what otool we will be using.
> if ${gcc_cv_otool+:} false; then :
>
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index 4532c5c22fe5..4c268d565d72 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -1886,6 +1886,93 @@ if test "x$enable_win32_utf8_manifest" != xno; then
> host_extra_objs_mingw=utf8-mingw32.o
> fi
>
> +
> +# Figure out what readelf we will be using.
> +AS_VAR_SET_IF(gcc_cv_readelf,, [
> +if test -f $gcc_cv_binutils_srcdir/configure.ac \
> + && test -f ../binutils/Makefile \
> + && test x$build = x$host; then
> + # Single tree build which includes binutils.
> + gcc_cv_readelf=../binutils/readelf$build_exeext
> +elif test -x readelf$build_exeext; then
> + gcc_cv_readelf=./readelf$build_exeext
> +elif ( set dummy $READELF_FOR_TARGET; test -x $[2] ); then
> + gcc_cv_readelf="$READELF_FOR_TARGET"
> +else
> + AC_PATH_PROG(gcc_cv_readelf, $READELF_FOR_TARGET)
> +fi])
> +
> +AC_MSG_CHECKING(what readelf to use)
> +if test "$gcc_cv_readelf" = ../binutils/readelf$build_exeext; then
> + # Single tree build which includes binutils.
> + AC_MSG_RESULT(newly built readelf)
> +elif test x$gcc_cv_readelf = x; then
> + AC_MSG_RESULT(not found)
> +else
> + AC_MSG_RESULT($gcc_cv_readelf)
> +fi
> +
> +case $target in
> +changequote(,)dnl
> + i[34567]86-*-gnu* | x86_64-*-gnu* )
> +changequote([,])dnl
> + # PR target/120933
> + # For GNU/Linux targets, check if ld emits GLIBC_ABI_GNU2_TLS. For
> PR ld/33130,
> + # newer ld will add GLIBC_ABI_GNU2_TLS if either unconfigured (auto
> mode) or if
> + # configured with --enable-gnu2-tls-tag. In auto mode,
> GLIBC_ABI_GNU2_TLS
> + # is only added if glibc provides it. In explicit mode, the user has
> asked
> + # for this behavior and binaries will depend on GLIBC_ABI_GNU2_TLS
> and fixed
> + # glibc. Hence the presence of GLIBC_ABI_GNU2_TLS tells us if we can
> safely
> + # default to GNU2 TLS descriptors.
> + conftest_S='
> + .section .text.startup,"ax",@progbits
> + .p2align 4
> + .globl main
> + .type main, @function
> + main:
> + #ifdef __x86_64__
> + leaq foo@TLSDESC(%rip), %rax
> + call *foo@TLSCALL(%rax)
> + movl %fs:(%rax), %eax
> + #else
> + leal ld@TLSDESC(%ebx), %eax
> + call *ld@TLSCALL(%eax)
> + addl %gs:0, %eax
> + #endif
> + ret
> + .size main, .-main
> + .section .note.GNU-stack,"",@progbits
> + '
> +
> + AC_CACHE_CHECK([libc has GLIBC_ABI_GNU2_TLS symbol dep and ld emits
> it],
> + gcc_cv_libc_x86_tlsdesc_call, [
> + gcc_cv_libc_x86_tlsdesc_call=no
> + echo "$conftest_S" > conftest.S
> + if $CC $CFLAGS conftest.S -o conftest -shared > /dev/null
> 2>&1; then
> + if test x$gcc_cv_readelf != x; then
> + if $gcc_cv_readelf --version-info conftest
> 2>&1 \
> + | grep "GLIBC_ABI_GNU2_TLS" >
> /dev/null 2>&1; then
> + gcc_cv_libc_x86_tlsdesc_call=yes
> + else
> + gcc_cv_libc_x86_tlsdesc_call=no
> + fi
> + fi
> + fi
> + rm -f conftest.*
> + ])
> +
> + # Set with_tls only if it's not already set via --with-tls=DIALECT
> + case "$gcc_cv_libc_x86_tlsdesc_call" in
> + yes)
> + with_tls=${with_tls:-gnu2}
> + ;;
> + *)
> + with_tls=${with_tls:-gnu}
> + ;;
> + esac
> + ;;
> +esac
> +
> # --------------------------------------------------------
> # Build, host, and target specific configuration fragments
> # --------------------------------------------------------
> @@ -2934,31 +3021,6 @@ else
> AC_MSG_RESULT($gcc_cv_objdump)
> fi
>
> -# Figure out what readelf we will be using.
> -AS_VAR_SET_IF(gcc_cv_readelf,, [
> -if test -f $gcc_cv_binutils_srcdir/configure.ac \
> - && test -f ../binutils/Makefile \
> - && test x$build = x$host; then
> - # Single tree build which includes binutils.
> - gcc_cv_readelf=../binutils/readelf$build_exeext
> -elif test -x readelf$build_exeext; then
> - gcc_cv_readelf=./readelf$build_exeext
> -elif ( set dummy $READELF_FOR_TARGET; test -x $[2] ); then
> - gcc_cv_readelf="$READELF_FOR_TARGET"
> -else
> - AC_PATH_PROG(gcc_cv_readelf, $READELF_FOR_TARGET)
> -fi])
> -
> -AC_MSG_CHECKING(what readelf to use)
> -if test "$gcc_cv_readelf" = ../binutils/readelf$build_exeext; then
> - # Single tree build which includes binutils.
> - AC_MSG_RESULT(newly built readelf)
> -elif test x$gcc_cv_readelf = x; then
> - AC_MSG_RESULT(not found)
> -else
> - AC_MSG_RESULT($gcc_cv_readelf)
> -fi
> -
> # Figure out what otool we will be using.
> AS_VAR_SET_IF(gcc_cv_otool,, [
> if test -x otool$build_exeext; then
> --
> 2.51.0
>