[binutils-gdb] i386: Add GLIBC_ABI_GNU_TLS version dependency

H.J. Lu hjl@sourceware.org
Wed Aug 20 21:31:26 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=fbcdc06c238be4fd6da7fb9bff4dd4c7f749ae07

commit fbcdc06c238be4fd6da7fb9bff4dd4c7f749ae07
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Sun Aug 17 15:22:22 2025 -0700

    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>

Diff:
---
 bfd/elf-linker-x86.h               |  9 +++++++++
 bfd/elf32-i386.c                   | 14 ++++++++++++--
 bfd/elfxx-x86.c                    |  2 ++
 bfd/elfxx-x86.h                    |  3 +++
 ld/NEWS                            |  5 +++++
 ld/config.in                       |  4 ++++
 ld/configure                       | 28 ++++++++++++++++++++++++++--
 ld/configure.ac                    | 19 +++++++++++++++++++
 ld/emultempl/elf-i386-glibc.em     | 36 ++++++++++++++++++++++++++++++++++++
 ld/ld.texi                         | 13 +++++++++++++
 ld/ldlex.h                         |  3 +++
 ld/testsuite/ld-i386/gnu-tls-1.s   |  9 +++++++++
 ld/testsuite/ld-i386/gnu-tls-1a.rd |  7 +++++++
 ld/testsuite/ld-i386/gnu-tls-1b.rd |  4 ++++
 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 fe322152e14..cdd739e572a 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 9d06f1494cf..657563f2f4f 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 e2c61b85fc6..3de48397e78 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 791a2a2592f..2a28987f208 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 bacabc8440e..8794e883522 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 64dbc3e0c88..790efd336be 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 6f1a3559964..fe23178cd29 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 4b9068a415e..3e44e3361ef 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 547823750a4..26a7296cf76 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 0e13f7d8e35..cf750d15259 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 020712df0e7..24cac1cdfc0 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 00000000000..02ae207a9b5
--- /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 00000000000..65d889de7d9
--- /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 00000000000..02006e47da1
--- /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 622c06eefe7..5b189ecb7f7 100644
--- a/ld/testsuite/ld-i386/i386.exp
+++ b/ld/testsuite/ld-i386/i386.exp
@@ -1519,10 +1519,26 @@ 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" \


More information about the Binutils-cvs mailing list