aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2025-08-17 15:22:22 -0700
committerH.J. Lu <hjl.tools@gmail.com>2025-08-20 13:53:32 -0700
commitfbcdc06c238be4fd6da7fb9bff4dd4c7f749ae07 (patch)
tree598c9896e3396c675c8bef02487ba53ec2cfbeeb
parent66e4999f343f85116cf2dda137cc0f31ac793ce6 (diff)
downloadbinutils-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.h9
-rw-r--r--bfd/elf32-i386.c14
-rw-r--r--bfd/elfxx-x86.c2
-rw-r--r--bfd/elfxx-x86.h3
-rw-r--r--ld/NEWS5
-rw-r--r--ld/config.in4
-rwxr-xr-xld/configure28
-rw-r--r--ld/configure.ac19
-rw-r--r--ld/emultempl/elf-i386-glibc.em36
-rw-r--r--ld/ld.texi13
-rw-r--r--ld/ldlex.h3
-rw-r--r--ld/testsuite/ld-i386/gnu-tls-1.s9
-rw-r--r--ld/testsuite/ld-i386/gnu-tls-1a.rd7
-rw-r--r--ld/testsuite/ld-i386/gnu-tls-1b.rd4
-rw-r--r--ld/testsuite/ld-i386/i386.exp18
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;
diff --git a/ld/NEWS b/ld/NEWS
index bacabc8..8794e88 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -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"
diff --git a/ld/ld.texi b/ld/ld.texi
index 0e13f7d..cf750d1 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -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
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 020712d..24cac1c 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -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" \