]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
i386: Add GLIBC_ABI_GNU_TLS version dependency
authorH.J. Lu <hjl.tools@gmail.com>
Sun, 17 Aug 2025 22:22:22 +0000 (15:22 -0700)
committerH.J. Lu <hjl.tools@gmail.com>
Wed, 20 Aug 2025 20:53:32 +0000 (13:53 -0700)
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>
15 files changed:
bfd/elf-linker-x86.h
bfd/elf32-i386.c
bfd/elfxx-x86.c
bfd/elfxx-x86.h
ld/NEWS
ld/config.in
ld/configure
ld/configure.ac
ld/emultempl/elf-i386-glibc.em
ld/ld.texi
ld/ldlex.h
ld/testsuite/ld-i386/gnu-tls-1.s [new file with mode: 0644]
ld/testsuite/ld-i386/gnu-tls-1a.rd [new file with mode: 0644]
ld/testsuite/ld-i386/gnu-tls-1b.rd [new file with mode: 0644]
ld/testsuite/ld-i386/i386.exp

index fe322152e1400ac1c173cab1596b4a74e9b2493b..cdd739e572ad5290fb9afdf13f2a5de98d6e4c7b 100644 (file)
@@ -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;
 
index 9d06f1494cf43e413f349310b30ee1c603fe2a52..657563f2f4f4f8ae614c5b2865fbfbd7a83b1da9 100644 (file)
@@ -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)
index e2c61b85fc628654d4da08cfa90b8fbc47cdcdc9..3de48397e7862bd20e72e4e7555aa8b5131affa7 100644 (file)
@@ -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
index 791a2a2592f4a07eaa71f946baf566309eff8e7b..2a28987f20811acf7c70fb4feba2cdbdbe95c0d0 100644 (file)
@@ -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 bacabc8440e376b21fbc97c9dd9b8a5b00bd02d4..8794e8835226d6cb215a7263e78abefbad5ca6e6 100644 (file)
--- 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
index 64dbc3e0c8838573ff9c2515351a7996f389b18e..790efd336be489eba590a07101568a91d058a84d 100644 (file)
    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
index 6f1a3559964bdfd84b0929265e1e5a44f9c19799..fe23178cd294b4b5a9dcdf5981b970b4e932a03e 100755 (executable)
@@ -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
index 4b9068a415e1b19cea393a8bd19e9ff86cf6cac8..3e44e3361efb2aeb1f028c3659eafe6217ade559 100644 (file)
@@ -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.])
index 547823750a49892bc01522e09cb2bd0921dbf7be..26a7296cf76afde56cf6e086fb389519f895e007 100644 (file)
@@ -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"
index 0e13f7d8e35a3428e9a4eb4c2ed1dcf7458f04da..cf750d15259e5b1748362da2b43097f81cab5170 100644 (file)
@@ -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
index 020712df0e7355f5334315cc59ae21843de05709..24cac1cdfc0f4f5d19f2dc0f3bcab907537c631e 100644 (file)
@@ -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 (file)
index 0000000..02ae207
--- /dev/null
@@ -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 (file)
index 0000000..65d889d
--- /dev/null
@@ -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 (file)
index 0000000..02006e4
--- /dev/null
@@ -0,0 +1,4 @@
+#failif
+#...
+  0x[a-f0-9]+:   Name: GLIBC_ABI_GNU_TLS  Flags: none  Version: [0-9]+
+#...
index 622c06eefe7ebb61efa2257d86d695081931982e..5b189ecb7f7f78b4e8fed073a9be7fd1cf7e0e62 100644 (file)
@@ -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" \