diff options
author | H.J. Lu <hjl.tools@gmail.com> | 2025-08-17 15:22:22 -0700 |
---|---|---|
committer | H.J. Lu <hjl.tools@gmail.com> | 2025-08-20 13:53:32 -0700 |
commit | fbcdc06c238be4fd6da7fb9bff4dd4c7f749ae07 (patch) | |
tree | 598c9896e3396c675c8bef02487ba53ec2cfbeeb | |
parent | 66e4999f343f85116cf2dda137cc0f31ac793ce6 (diff) | |
download | binutils-fbcdc06c238be4fd6da7fb9bff4dd4c7f749ae07.zip binutils-fbcdc06c238be4fd6da7fb9bff4dd4c7f749ae07.tar.gz binutils-fbcdc06c238be4fd6da7fb9bff4dd4c7f749ae07.tar.bz2 |
i386: Add GLIBC_ABI_GNU_TLS version dependency
On Linux/i386, programs and shared libraries compiled with
-mtls-dialect=gnu may fail silently at run-time against glibc without
the GNU TLS run-time fix for:
https://sourceware.org/bugzilla/show_bug.cgi?id=32996
The glibc version tag, GLIBC_ABI_GNU_TLS, has been added to indicate
that glibc has the working GNU TLS run-time:
commit ed1b7a5a489ab555a27fad9c101ebe2e1c1ba881
Author: H.J. Lu <hjl.tools@gmail.com>
Date: Mon Jul 28 12:16:11 2025 -0700
i386: Add GLIBC_ABI_GNU_TLS version [BZ #33221]
Add the --gnu-tls-tag option to x86-64 ELF linker to add the
GLIBC_ABI_GNU_TLS version dependency in output programs and shared
libraries when linking against glibc if input relocatable object files
call ___tls_get_addr. The output will fail to load and run at run-time
against glibc which doesn't define the GLIBC_ABI_GNU_TLS version.
Add the --enable-gnu-tls-tag configure option to enable --gnu-tls-tag
by default. If unspecified, linker will add the GLIBC_ABI_GNU_TLS
version dependency if input call ___tls_get_addr and libc.so defines
the GLIBC_ABI_GNU2_TLS version.
bfd/
PR ld/33287
* elf-linker-x86.h (elf_linker_x86_params): Add
gnu_tls_version_tag.
* elf32-i386.c (elf_backend_add_glibc_version_dependency): Add
GLIBC_ABI_GNU_TLS support.
* elfxx-x86.c (_bfd_x86_elf_link_check_relocs): Set
has_tls_get_addr_call to 1 if ___tls_get_addr is used.
* elfxx-x86.h (elf_x86_link_hash_table): Add has_tls_get_addr_call.
ld/
PR ld/33287
* Mention --gnu-tls-tag, --no-gnu-tls-tag and --enable-gnu-tls-tag.
* config.in: Regenerated.
* configure: Likewise.
* configure.ac: Add --enable-gnu-tls-tag.
* ld.texi: Document --gnu-tls-tag and --enable-gnu-tls-tag.
* ldlex.h (option_values): Add OPTION_GNU_TLS_VERSION_TAG and
OPTION_NO_GNU_TLS_VERSION_TAG.
* emultempl/elf-i386-glibc.em (elf_i386_glibc_before_parse):
Initialize params.gnu_tls_version_tag.
(PARSE_AND_LIST_LONGOPTS_386): New.
(PARSE_AND_LIST_OPTIONS_386): Likewise.
(PARSE_AND_LIST_ARGS_CASES_386): Likewise.
(PARSE_AND_LIST_LONGOPTS): Append $PARSE_AND_LIST_LONGOPTS_386.
(PARSE_AND_LIST_OPTIONS): Append $PARSE_AND_LIST_OPTIONS_386.
(PARSE_AND_LIST_ARGS_CASES): Append
$PARSE_AND_LIST_ARGS_CASES_386.
* testsuite/ld-i386/gnu-tls-1.s: Likewise.
* testsuite/ld-i386/gnu-tls-1a.rd: Likewise.
* testsuite/ld-i386/gnu-tls-1b.rd: Likewise.
* testsuite/ld-i386/i386.exp: Run PR ld/33287 tests.
Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
-rw-r--r-- | bfd/elf-linker-x86.h | 9 | ||||
-rw-r--r-- | bfd/elf32-i386.c | 14 | ||||
-rw-r--r-- | bfd/elfxx-x86.c | 2 | ||||
-rw-r--r-- | bfd/elfxx-x86.h | 3 | ||||
-rw-r--r-- | ld/NEWS | 5 | ||||
-rw-r--r-- | ld/config.in | 4 | ||||
-rwxr-xr-x | ld/configure | 28 | ||||
-rw-r--r-- | ld/configure.ac | 19 | ||||
-rw-r--r-- | ld/emultempl/elf-i386-glibc.em | 36 | ||||
-rw-r--r-- | ld/ld.texi | 13 | ||||
-rw-r--r-- | ld/ldlex.h | 3 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/gnu-tls-1.s | 9 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/gnu-tls-1a.rd | 7 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/gnu-tls-1b.rd | 4 | ||||
-rw-r--r-- | ld/testsuite/ld-i386/i386.exp | 18 |
15 files changed, 169 insertions, 5 deletions
diff --git a/bfd/elf-linker-x86.h b/bfd/elf-linker-x86.h index fe32215..cdd739e 100644 --- a/bfd/elf-linker-x86.h +++ b/bfd/elf-linker-x86.h @@ -80,6 +80,15 @@ struct elf_linker_x86_params */ unsigned int gnu2_tls_version_tag : 2; + /* Add the GLIBC_ABI_GNU_TLS version dependency if input object files + call ___tls_get_addr: + 0: Disable. + 1: Enable. + 2: Auto. Enable if libc.so has the GLIBC_ABI_GNU_TLS version. + This is only used by i386. + */ + unsigned int gnu_tls_version_tag : 2; + /* X86-64 ISA level needed. */ unsigned int isa_level; diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index 9d06f14..657563f 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -4501,8 +4501,8 @@ elf_i386_add_glibc_version_dependency (struct elf_find_verdep_info *rinfo) { int i = 0; - const char *version[3] = { NULL, NULL, NULL }; - bool auto_version[3] = { false, false, false }; + const char *version[4] = { NULL, NULL, NULL, NULL }; + bool auto_version[4] = { false, false, false, false }; struct elf_x86_link_hash_table *htab; if (rinfo->info->enable_dt_relr) @@ -4523,6 +4523,16 @@ elf_i386_add_glibc_version_dependency auto_version[i] = true; i++; } + if (htab->params->gnu_tls_version_tag + && htab->has_tls_get_addr_call) + { + version[i] = "GLIBC_ABI_GNU_TLS"; + /* 2 == auto, enable if libc.so defines the GLIBC_ABI_GNU_TLS + version. */ + if (htab->params->gnu_tls_version_tag == 2) + auto_version[i] = true; + i++; + } } if (i != 0) diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c index e2c61b8..3de4839 100644 --- a/bfd/elfxx-x86.c +++ b/bfd/elfxx-x86.c @@ -882,6 +882,8 @@ _bfd_x86_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info) h = (struct elf_link_hash_entry *) h->root.u.i.link; elf_x86_hash_entry (h)->tls_get_addr = 1; } + + htab->has_tls_get_addr_call = 1; } /* Pass NULL for __ehdr_start which will be defined by diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h index 791a2a2..2a28987 100644 --- a/bfd/elfxx-x86.h +++ b/bfd/elfxx-x86.h @@ -674,6 +674,9 @@ struct elf_x86_link_hash_table relocation. */ unsigned int has_tls_desc_call : 1; + /* TRUE if inputs call ___tls_get_addr. This is only used for i386. */ + unsigned int has_tls_get_addr_call : 1; + /* Value used to fill the unused bytes of the first PLT entry. This is only used for i386. */ bfd_byte plt0_pad_byte; @@ -1,5 +1,10 @@ -*- text -*- +* Add --gnu-tls-tag/--no-gnu-tls-tag options to i386 ELF linker to add + the GLIBC_ABI_GNU_TLS version dependency in output if input object + files call ___tls_get_addr. Also added --enable-gnu-tls-tag configure + option to enable --gnu-tls-tag by default. + * Add --gnu2-tls-tag/--no-gnu2-tls-tag options to i386 and x86-64 ELF linkers to add the GLIBC_ABI_GNU2_TLS version dependency in output if input object files have R_386_TLS_DESC_CALL or R_X86_64_TLSDESC_CALL diff --git a/ld/config.in b/ld/config.in index 64dbc3e..790efd3 100644 --- a/ld/config.in +++ b/ld/config.in @@ -35,6 +35,10 @@ by default. */ #undef DEFAULT_LD_GNU2_TLS_TAG +/* Define to 1 if you want to enable --gnu-tls-tag in ELF i386 linker by + default. */ +#undef DEFAULT_LD_GNU_TLS_TAG + /* Define to 1 if you want to enable --rosegment in the ELF linker by default. */ #undef DEFAULT_LD_ROSEGMENT diff --git a/ld/configure b/ld/configure index 6f1a3559..fe23178 100755 --- a/ld/configure +++ b/ld/configure @@ -852,6 +852,7 @@ enable_separate_code enable_rosegment enable_mark_plt enable_gnu2_tls_tag +enable_gnu_tls_tag enable_memory_seal enable_warn_execstack enable_error_execstack @@ -1551,6 +1552,7 @@ Optional Features: --enable-mark-plt enable -z mark-plt in ELF x86-64 linker by default --enable-gnu2-tls-tag enable --gnu2-tls-tag in ELF i386/x86-64 linker by default + --enable-gnu-tls-tag enable --gnu-tls-tag in ELF i386 linker by default --enable-memory-seal enable -z memory-seal in ELF linker by default --enable-warn-execstack enable warnings when creating an executable stack --enable-error-execstack @@ -11517,7 +11519,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11520 "configure" +#line 11522 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11623,7 +11625,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11626 "configure" +#line 11628 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -15522,6 +15524,17 @@ esac fi +# Decide if --gnu-tls-tag should be enabled in ELF i386 linker by default. +ac_default_ld_enable_gnu_tls_tag=unset +# Check whether --enable-gnu-tls-tag was given. +if test "${enable_gnu_tls_tag+set}" = set; then : + enableval=$enable_gnu_tls_tag; case "${enableval}" in + yes) ac_default_ld_enable_gnu_tls_tag=1 ;; + no) ac_default_ld_enable_gnu_tls_tag=0 ;; +esac +fi + + # Decide if -z memory-seal should be enabled in ELF linker by default. ac_default_ld_z_memory_seal=unset # Check whether --enable-memory-seal was given. @@ -19007,6 +19020,17 @@ cat >>confdefs.h <<_ACEOF _ACEOF +if test "${ac_default_ld_enable_gnu_tls_tag}" = unset; then + # Default to enable --gnu-tls-tag if libc.so has the GLIBC_ABI_GNU_TLS + # version. + ac_default_ld_enable_gnu_tls_tag=2 +fi + +cat >>confdefs.h <<_ACEOF +#define DEFAULT_LD_GNU_TLS_TAG $ac_default_ld_enable_gnu_tls_tag +_ACEOF + + cat >>confdefs.h <<_ACEOF #define DEFAULT_LD_WARN_EXECSTACK $ac_default_ld_warn_execstack diff --git a/ld/configure.ac b/ld/configure.ac index 4b9068a..3e44e33 100644 --- a/ld/configure.ac +++ b/ld/configure.ac @@ -256,6 +256,16 @@ AC_ARG_ENABLE(gnu2-tls-tag, no) ac_default_ld_enable_gnu2_tls_tag=0 ;; esac]) +# Decide if --gnu-tls-tag should be enabled in ELF i386 linker by default. +ac_default_ld_enable_gnu_tls_tag=unset +AC_ARG_ENABLE(gnu-tls-tag, + AS_HELP_STRING([--enable-gnu-tls-tag], + [enable --gnu-tls-tag in ELF i386 linker by default]), +[case "${enableval}" in + yes) ac_default_ld_enable_gnu_tls_tag=1 ;; + no) ac_default_ld_enable_gnu_tls_tag=0 ;; +esac]) + # Decide if -z memory-seal should be enabled in ELF linker by default. ac_default_ld_z_memory_seal=unset AC_ARG_ENABLE(memory-seal, @@ -666,6 +676,15 @@ AC_DEFINE_UNQUOTED(DEFAULT_LD_GNU2_TLS_TAG, $ac_default_ld_enable_gnu2_tls_tag, [Define to 1 if you want to enable --gnu2-tls-tag in ELF i386/x86-64 linker by default.]) +if test "${ac_default_ld_enable_gnu_tls_tag}" = unset; then + # Default to enable --gnu-tls-tag if libc.so has the GLIBC_ABI_GNU_TLS + # version. + ac_default_ld_enable_gnu_tls_tag=2 +fi +AC_DEFINE_UNQUOTED(DEFAULT_LD_GNU_TLS_TAG, + $ac_default_ld_enable_gnu_tls_tag, + [Define to 1 if you want to enable --gnu-tls-tag in ELF i386 linker by default.]) + AC_DEFINE_UNQUOTED(DEFAULT_LD_WARN_EXECSTACK, $ac_default_ld_warn_execstack, [Define to 1 if you want to enable --warn-execstack in ELF linker by default.]) diff --git a/ld/emultempl/elf-i386-glibc.em b/ld/emultempl/elf-i386-glibc.em index 5478237..26a7296 100644 --- a/ld/emultempl/elf-i386-glibc.em +++ b/ld/emultempl/elf-i386-glibc.em @@ -35,7 +35,43 @@ elf_i386_glibc_before_parse (void) { elf_x86_before_parse (); elf_x86_glibc_before_parse (); + params.gnu_tls_version_tag = DEFAULT_LD_GNU_TLS_TAG; } EOF LDEMUL_BEFORE_PARSE=elf_i386_glibc_before_parse + +PARSE_AND_LIST_LONGOPTS_386=' + { "gnu-tls-tag", no_argument, NULL, OPTION_GNU_TLS_VERSION_TAG }, + { "no-gnu-tls-tag", no_argument, NULL, OPTION_NO_GNU_TLS_VERSION_TAG }, +' + +PARSE_AND_LIST_OPTIONS_386=' + if (DEFAULT_LD_GNU_TLS_TAG == 0) + fprintf (file, _("\ + --gnu-tls-tag Add GLIBC_ABI_GNU_TLS dependency\n\ + --no-gnu-tls-tag Do not add GLIBC_ABI_GNU_TLS dependency (default)\n")); + else if (DEFAULT_LD_GNU_TLS_TAG == 1) + fprintf (file, _("\ + --gnu-tls-tag Add GLIBC_ABI_GNU_TLS dependency (default)\n\ + --no-gnu-tls-tag Do not add GLIBC_ABI_GNU_TLS dependency\n")); + else + fprintf (file, _("\ + --gnu-tls-tag Add GLIBC_ABI_GNU_TLS dependency (auto)\n\ + when no options are specified (default)\n\ + --no-gnu-tls-tag Do not add GLIBC_ABI_GNU_TLS dependency\n")); +' + +PARSE_AND_LIST_ARGS_CASES_386=' + case OPTION_GNU_TLS_VERSION_TAG: + params.gnu_tls_version_tag = 1; + break; + + case OPTION_NO_GNU_TLS_VERSION_TAG: + params.gnu_tls_version_tag = 0; + break; +' + +PARSE_AND_LIST_LONGOPTS="$PARSE_AND_LIST_LONGOPTS $PARSE_AND_LIST_LONGOPTS_386" +PARSE_AND_LIST_OPTIONS="$PARSE_AND_LIST_OPTIONS $PARSE_AND_LIST_OPTIONS_386" +PARSE_AND_LIST_ARGS_CASES="$PARSE_AND_LIST_ARGS_CASES $PARSE_AND_LIST_ARGS_CASES_386" @@ -1745,6 +1745,19 @@ Supported for Linux/i386 and Linux/x86_64. Other keywords are ignored for Solaris compatibility. +@item --gnu-tls-tag +@itemx --no-gnu-tls-tag +Add @code{GLIBC_ABI_GNU_TLS} version tag dependency in output programs +and shared libraries when linking against glibc if input relocatable +object files call @code{___tls_get_addr}. The output will fail to load +and run at run-time against glibc which doesn't define the +@code{GLIBC_ABI_GNU_TLS} version tag. Unless disabled by the +@option{--disable-gnu-tls-tag} configure option at the linker build +time, when no options are specified, linker will add the +@code{GLIBC_ABI_GNU_TLS} version tag dependency if inputs have +@code{___tls_get_addr} call and libc.so defines the +@code{GLIBC_ABI_GNU_TLS} version tag. Supported for Linux/i386. + @item --gnu2-tls-tag @itemx --no-gnu2-tls-tag Add @code{GLIBC_ABI_GNU2_TLS} version tag dependency in output programs @@ -472,6 +472,9 @@ enum option_values /* Used by emultempl/elf-x86-glibc.em. */ OPTION_GNU2_TLS_VERSION_TAG, OPTION_NO_GNU2_TLS_VERSION_TAG, + /* Used by emultempl/elf-i386-glibc.em. */ + OPTION_GNU_TLS_VERSION_TAG, + OPTION_NO_GNU_TLS_VERSION_TAG, }; /* The initial parser states. */ diff --git a/ld/testsuite/ld-i386/gnu-tls-1.s b/ld/testsuite/ld-i386/gnu-tls-1.s new file mode 100644 index 0000000..02ae207 --- /dev/null +++ b/ld/testsuite/ld-i386/gnu-tls-1.s @@ -0,0 +1,9 @@ + .text + .p2align 4 + .globl func + .type func, @function +func: + leal foo@tlsgd(,%ebx,1), %eax + call ___tls_get_addr@PLT + ret + .section .note.GNU-stack,"",@progbits diff --git a/ld/testsuite/ld-i386/gnu-tls-1a.rd b/ld/testsuite/ld-i386/gnu-tls-1a.rd new file mode 100644 index 0000000..65d889d --- /dev/null +++ b/ld/testsuite/ld-i386/gnu-tls-1a.rd @@ -0,0 +1,7 @@ +#... +Version needs section '.gnu.version_r' contains [0-9]+ entries: + Addr: 0x[0-9a-f]+ +Offset: 0x[0-9a-f]+ +Link: +[0-9]+ +\(.dynstr\) + +0+: Version: 1 +File: libc\.so\.6(|\.1) +Cnt: +[0-9]+ +#... + 0x[a-f0-9]+: Name: GLIBC_ABI_GNU_TLS Flags: none Version: [0-9]+ +#pass diff --git a/ld/testsuite/ld-i386/gnu-tls-1b.rd b/ld/testsuite/ld-i386/gnu-tls-1b.rd new file mode 100644 index 0000000..02006e4 --- /dev/null +++ b/ld/testsuite/ld-i386/gnu-tls-1b.rd @@ -0,0 +1,4 @@ +#failif +#... + 0x[a-f0-9]+: Name: GLIBC_ABI_GNU_TLS Flags: none Version: [0-9]+ +#... diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp index 622c06e..5b189ec 100644 --- a/ld/testsuite/ld-i386/i386.exp +++ b/ld/testsuite/ld-i386/i386.exp @@ -1519,11 +1519,27 @@ run_ld_link_tests [list \ ] \ ] -# The musl C library does not support --gnu2-tls-tag. +# The musl C library does not support --gnu-tls-tag nor --gnu2-tls-tag. if { ![istarget *-*-musl] && [check_compiler_available] } { run_cc_link_tests [list \ [list \ + "Build gnu-tls-1a.so" \ + "-shared -Wl,--no-as-needed,--gnu-tls-tag" \ + "-fPIC" \ + { gnu-tls-1.s } \ + {{readelf {-W --version-info} gnu-tls-1a.rd}} \ + "gnu-tls-1a.so" \ + ] \ + [list \ + "Build gnu-tls-1b.so" \ + "-shared -Wl,--no-as-needed,--no-gnu-tls-tag" \ + "-fPIC" \ + { gnu-tls-1.s } \ + {{readelf {-W --version-info} gnu-tls-1b.rd}} \ + "gnu-tls-1b.so" \ + ] \ + [list \ "Build gnu2-tls-1a.so" \ "-shared -Wl,--no-as-needed,--gnu2-tls-tag" \ "-fPIC" \ |