[PATCH] x86-64: Add GLIBC_ABI_GNU2_TLS version dependency

H.J. Lu hjl.tools@gmail.com
Thu Aug 14 14:07:05 GMT 2025


On Linux/x86-64, programs and shared libraries compiled with
-mtls-dialect=gnu2 may fail silently at run-time against glibc without
the GNU2 TLS run-time fix for:

https://sourceware.org/bugzilla/show_bug.cgi?id=31372

A version tag, GLIBC_ABI_GNU2_TLS, has been added to glibc to indicate
that glibc has the working GNU2 TLS run-time:

commit 9df8fa397d515dc86ff5565f6c45625e672d539e
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Mon Jul 28 12:18:22 2025 -0700

    x86-64: Add GLIBC_ABI_GNU2_TLS version [BZ #33129]

Add the --enable-gnu2-tls-tag option to x86-64 ELF linker to add
GLIBC_ABI_GNU2_TLS version dependency in output programs and shared
libraries when linking against glibc if input relocatable object files
have R_X86_64_TLSDESC_CALL relocation so that the output will fail to
load and run at run-time against glibc which doesn't have the
GLIBC_ABI_GNU2_TLS version.

bfd/

	PR ld/33130
	* elf-linker-x86.h (elf_linker_x86_params): Add gnu2_tls.
	* elf64-x86-64.c (elf_x86_64_scan_relocs): Set has_tlsdesc_call
	to 1 for R_X86_64_TLSDESC_CALL.
	(elf_x86_64_add_glibc_version_dependency): Add GLIBC_ABI_GNU2_TLS
	version dependency if -z gnu2-tls is enabled has_tlsdesc_call
	isn't 0.
	* elfxx-x86.h (elf_x86_link_hash_table): Add has_tlsdesc_call.

ld/

	PR ld/33130
	* NEWS: Mention --enable-gnu2-tls-tag/--disable-gnu2-tls-tag and
	--enable-gnu2-tls-tag.
	* config.in: Regenerated.
	* configure: Likewise.
	* configure.ac: Add --enable-gnu2-tls-tag.
	* ld.texi: Document --enable-gnu2-tls-tag/--disable-gnu2-tls-tag.
	* ldlex.h (option_values): Add OPTION_ENABLE_GNU2_TLS_TAG and
	OPTION_DISABLE_GNU2_TLS_TAG.
	* emulparams/elf32_x86_64.sh (EXTRA_EM_FILE): Changed to
	"elf-x86-64".
	* emulparams/elf_x86_64.sh (EXTRA_EM_FILE): Likewise.
	* emultempl/elf-x86-64.em: New file.
	* emultempl/elf-x86.em (elf_x86_64_before_parse): Removed.
	(LDEMUL_BEFORE_PARSE): Likewise.
	(elf_x86_64_before_allocation): Likewise.
	* testsuite/ld-x86-64/gnu2-tls-1.rd: New file.
	* testsuite/ld-x86-64/gnu2-tls-1a.od: New file.
	* testsuite/ld-x86-64/gnu2-tls-1a.s: Likewise.
	* testsuite/ld-x86-64/gnu2-tls-1b.od: Likewise.
	* testsuite/ld-x86-64/gnu2-tls-1b.s: Likewise.
	* testsuite/ld-x86-64/gnu2-tls-1c.od: Likewise.
	* testsuite/ld-x86-64/gnu2-tls-2.rd: Likewise.
	* testsuite/ld-x86-64/gnu2-tls-2.s: Likewise.
	* testsuite/ld-x86-64/x86-64.exp: Run GLIBC_ABI_GNU2_TLS tests.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 bfd/elf-linker-x86.h                  |   4 +
 bfd/elf64-x86-64.c                    |  22 ++++--
 bfd/elfxx-x86.h                       |   4 +
 ld/NEWS                               |   6 ++
 ld/config.in                          |   4 +
 ld/configure                          |  27 ++++++-
 ld/configure.ac                       |  17 +++++
 ld/emulparams/elf32_x86_64.sh         |   2 +-
 ld/emulparams/elf_x86_64.sh           |   2 +-
 ld/emultempl/elf-x86-64.em            | 103 ++++++++++++++++++++++++++
 ld/emultempl/elf-x86.em               |  58 ---------------
 ld/ld.texi                            |   8 ++
 ld/ldlex.h                            |   3 +
 ld/testsuite/ld-x86-64/gnu2-tls-1.rd  |   7 ++
 ld/testsuite/ld-x86-64/gnu2-tls-1a.od |  13 ++++
 ld/testsuite/ld-x86-64/gnu2-tls-1a.s  |  14 ++++
 ld/testsuite/ld-x86-64/gnu2-tls-1b.od |  13 ++++
 ld/testsuite/ld-x86-64/gnu2-tls-1b.s  |   9 +++
 ld/testsuite/ld-x86-64/gnu2-tls-1c.od |  13 ++++
 ld/testsuite/ld-x86-64/gnu2-tls-2.rd  |   4 +
 ld/testsuite/ld-x86-64/gnu2-tls-2.s   |  13 ++++
 ld/testsuite/ld-x86-64/x86-64.exp     |  45 ++++++++++-
 22 files changed, 323 insertions(+), 68 deletions(-)
 create mode 100644 ld/emultempl/elf-x86-64.em
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-1.rd
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-1a.od
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-1a.s
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-1b.od
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-1b.s
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-1c.od
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-2.rd
 create mode 100644 ld/testsuite/ld-x86-64/gnu2-tls-2.s

diff --git a/bfd/elf-linker-x86.h b/bfd/elf-linker-x86.h
index 2c98257038f..ef0252f92cf 100644
--- a/bfd/elf-linker-x86.h
+++ b/bfd/elf-linker-x86.h
@@ -72,6 +72,10 @@ struct elf_linker_x86_params
   /* Mark PLT with dynamic tags.  */
   unsigned int mark_plt : 1;
 
+  /* Add the GLIBC_ABI_GNU2_TLS version dependency if input object files
+     have R_X86_64_TLSDESC_CALL relocation.  */
+  unsigned int gnu2_tls : 1;
+
   /* X86-64 ISA level needed.  */
   unsigned int isa_level;
 
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index b6f97b5b69b..5ff7010b021 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -2694,6 +2694,10 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
 	    eh->zero_undefweak &= 0x2;
 	  break;
 
+	case R_X86_64_TLSDESC_CALL:
+	  htab->has_tlsdesc_call = 1;
+	  goto need_got;
+
 	case R_X86_64_GOTTPOFF:
 	case R_X86_64_CODE_4_GOTTPOFF:
 	case R_X86_64_CODE_5_GOTTPOFF:
@@ -2715,7 +2719,7 @@ elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_X86_64_GOTPLT64:
 	case R_X86_64_GOTPC32_TLSDESC:
 	case R_X86_64_CODE_4_GOTPC32_TLSDESC:
-	case R_X86_64_TLSDESC_CALL:
+need_got:
 	  /* This symbol requires a global offset table entry.	*/
 	  {
 	    int tls_type, old_tls_type;
@@ -6243,7 +6247,7 @@ elf_x86_64_add_glibc_version_dependency
   (struct elf_find_verdep_info *rinfo)
 {
   unsigned int i = 0;
-  const char *version[3] = { NULL, NULL, NULL };
+  const char *version[4] = { NULL, NULL, NULL, NULL };
   struct elf_x86_link_hash_table *htab;
 
   if (rinfo->info->enable_dt_relr)
@@ -6253,10 +6257,18 @@ elf_x86_64_add_glibc_version_dependency
     }
 
   htab = elf_x86_hash_table (rinfo->info, X86_64_ELF_DATA);
-  if (htab != NULL && htab->params->mark_plt)
+  if (htab != NULL)
     {
-      version[i] = "GLIBC_2.36";
-      i++;
+      if (htab->params->gnu2_tls && htab->has_tlsdesc_call)
+	{
+	  version[i] = "GLIBC_ABI_GNU2_TLS";
+	  i++;
+	}
+      if (htab->params->mark_plt)
+	{
+	  version[i] = "GLIBC_2.36";
+	  i++;
+	}
     }
 
   if (i != 0)
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index f6ee6a65356..8fd2d81ab6b 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -670,6 +670,10 @@ struct elf_x86_link_hash_table
   /* Number of relative reloc generation pass.  */
   unsigned int generate_relative_reloc_pass;
 
+  /* TRUE if inputs have R_X86_64_TLSDESC_CALL relocation.  This is
+     only used for x86-64.  */
+  unsigned int has_tlsdesc_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 54c1df5aadf..3452c2891b2 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,11 @@
 -*- text -*-
 
+* Add --enable-gnu2-tls-tag/--disable-gnu2-tls-tag options to x86-64 ELF
+  linker to add the GLIBC_ABI_GNU2_TLS version dependency in output if
+  input object files have R_X86_64_TLSDESC_CALL relocation.  Also added
+  --enable-gnu2-tls-tag configure option to enable --enable-gnu2-tls-tag
+  by default.
+
 * NaCl target support is removed.
 
 Changes in 2.45:
diff --git a/ld/config.in b/ld/config.in
index 37812241bd9..1756481b7b8 100644
--- a/ld/config.in
+++ b/ld/config.in
@@ -19,6 +19,10 @@
 /* Define if you want compressed debug sections by default. */
 #undef DEFAULT_FLAG_COMPRESS_DEBUG
 
+/* Define to 1 if you want to enable --enable-gnu2-tls-tag in ELF x86-64
+   linker by default. */
+#undef DEFAULT_LD_ENABLE_GNU2_TLS_TAG
+
 /* Define to 1 if you want to turn executable stack warnings into errors by
    default. */
 #undef DEFAULT_LD_ERROR_EXECSTACK
diff --git a/ld/configure b/ld/configure
index 124b44182bc..63ec21150e2 100755
--- a/ld/configure
+++ b/ld/configure
@@ -851,6 +851,7 @@ enable_textrel_check
 enable_separate_code
 enable_rosegment
 enable_mark_plt
+enable_gnu2_tls_tag
 enable_memory_seal
 enable_warn_execstack
 enable_error_execstack
@@ -1548,6 +1549,8 @@ Optional Features:
   --enable-separate-code  enable -z separate-code in ELF linker by default
   --enable-rosegment      enable --rosegment in the ELF linker by default
   --enable-mark-plt       enable -z mark-plt in ELF x86-64 linker by default
+  --enable-gnu2-tls-tag   enable --enable-gnu2-tls-tag in ELF x86-64 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
@@ -11514,7 +11517,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11517 "configure"
+#line 11520 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11620,7 +11623,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11623 "configure"
+#line 11626 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15507,6 +15510,18 @@ esac
 fi
 
 
+# Decide if --enable-gnu2-tls-tag should be enabled in ELF x86-64 linker
+# by default.
+ac_default_ld_enable_gnu2_tls_tag=unset
+# Check whether --enable-gnu2-tls-tag was given.
+if test "${enable_gnu2_tls_tag+set}" = set; then :
+  enableval=$enable_gnu2_tls_tag; case "${enableval}" in
+  yes) ac_default_ld_enable_gnu2_tls_tag=1 ;;
+  no) ac_default_ld_enable_gnu2_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.
@@ -18981,6 +18996,14 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+if test "${ac_default_ld_enable_gnu2_tls_tag}" = unset; then
+  ac_default_ld_enable_gnu2_tls_tag=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_LD_ENABLE_GNU2_TLS_TAG $ac_default_ld_enable_gnu2_tls_tag
+_ACEOF
+
 
 
 cat >>confdefs.h <<_ACEOF
diff --git a/ld/configure.ac b/ld/configure.ac
index e306c1ded4a..8ad3d458922 100644
--- a/ld/configure.ac
+++ b/ld/configure.ac
@@ -245,6 +245,17 @@ AC_ARG_ENABLE(mark-plt,
   no) ac_default_ld_z_mark_plt=0 ;;
 esac])
 
+# Decide if --enable-gnu2-tls-tag should be enabled in ELF x86-64 linker
+# by default.
+ac_default_ld_enable_gnu2_tls_tag=unset
+AC_ARG_ENABLE(gnu2-tls-tag,
+	      AS_HELP_STRING([--enable-gnu2-tls-tag],
+	      [enable --enable-gnu2-tls-tag in ELF x86-64 linker by default]),
+[case "${enableval}" in
+  yes) ac_default_ld_enable_gnu2_tls_tag=1 ;;
+  no) ac_default_ld_enable_gnu2_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,
@@ -646,6 +657,12 @@ AC_DEFINE_UNQUOTED(DEFAULT_LD_Z_MEMORY_SEAL,
   $ac_default_ld_z_memory_seal,
   [Define to 1 if you want to enable -z memory_seal in ELF linker by default.])
 
+if test "${ac_default_ld_enable_gnu2_tls_tag}" = unset; then
+  ac_default_ld_enable_gnu2_tls_tag=0
+fi
+AC_DEFINE_UNQUOTED(DEFAULT_LD_ENABLE_GNU2_TLS_TAG,
+  $ac_default_ld_enable_gnu2_tls_tag,
+  [Define to 1 if you want to enable --enable-gnu2-tls-tag in ELF x86-64 linker by default.])
 
 AC_DEFINE_UNQUOTED(DEFAULT_LD_WARN_EXECSTACK,
   $ac_default_ld_warn_execstack,
diff --git a/ld/emulparams/elf32_x86_64.sh b/ld/emulparams/elf32_x86_64.sh
index 6a92eec129d..4807413d133 100644
--- a/ld/emulparams/elf32_x86_64.sh
+++ b/ld/emulparams/elf32_x86_64.sh
@@ -20,7 +20,7 @@ COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
 ARCH="i386:x64-32"
 MACHINE=
 TEMPLATE_NAME=elf
-EXTRA_EM_FILE="elf-x86"
+EXTRA_EM_FILE="elf-x86-64"
 GENERATE_SHLIB_SCRIPT=yes
 GENERATE_PIE_SCRIPT=yes
 NO_SMALL_DATA=yes
diff --git a/ld/emulparams/elf_x86_64.sh b/ld/emulparams/elf_x86_64.sh
index 92449745c7a..39cbf2ca189 100644
--- a/ld/emulparams/elf_x86_64.sh
+++ b/ld/emulparams/elf_x86_64.sh
@@ -21,7 +21,7 @@ COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
 ARCH="i386:x86-64"
 MACHINE=
 TEMPLATE_NAME=elf
-EXTRA_EM_FILE="elf-x86"
+EXTRA_EM_FILE="elf-x86-64"
 GENERATE_SHLIB_SCRIPT=yes
 GENERATE_PIE_SCRIPT=yes
 NO_SMALL_DATA=yes
diff --git a/ld/emultempl/elf-x86-64.em b/ld/emultempl/elf-x86-64.em
new file mode 100644
index 00000000000..bbe84125563
--- /dev/null
+++ b/ld/emultempl/elf-x86-64.em
@@ -0,0 +1,103 @@
+# This shell script emits a C file. -*- C -*-
+#   Copyright (C) 2025 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the license, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING3. If not,
+# see <http://www.gnu.org/licenses/>.
+#
+
+# This file is sourced from elf.em, and defines x86-64 specific routines.
+#
+
+source_em ${srcdir}/emultempl/elf-x86.em
+
+fragment <<EOF
+static void
+elf_x86_64_before_parse (void)
+{
+  params.mark_plt = DEFAULT_LD_Z_MARK_PLT;
+  params.gnu2_tls = DEFAULT_LD_ENABLE_GNU2_TLS_TAG;
+
+  elf_x86_before_parse ();
+}
+
+static void
+elf_x86_64_before_allocation (void)
+{
+  if (!bfd_link_relocatable (&link_info)
+      && is_elf_hash_table (link_info.hash)
+      && expld.phase != lang_mark_phase_enum)
+    {
+      struct elf_link_hash_table *htab = elf_hash_table (&link_info);
+      /* Run one_lang_size_sections_pass to estimate the output section
+	 layout before sizing dynamic sections.  */
+      expld.dataseg.phase = exp_seg_none;
+      expld.phase = lang_mark_phase_enum;
+      /* NB: Exclude linker created GOT setions when estimating output
+	 section layout as sizing dynamic sections may change linker
+	 created GOT sections.  */
+      if (htab->sgot != NULL)
+	htab->sgot->flags |= SEC_EXCLUDE;
+      if (htab->sgotplt != NULL)
+	htab->sgotplt->flags |= SEC_EXCLUDE;
+      one_lang_size_sections_pass (NULL, false);
+      /* Restore linker created GOT setions.  */
+      if (htab->sgot != NULL)
+	htab->sgot->flags &= ~SEC_EXCLUDE;
+      if (htab->sgotplt != NULL)
+	htab->sgotplt->flags &= ~SEC_EXCLUDE;
+      lang_reset_memory_regions ();
+    }
+
+  gld${EMULATION_NAME}_before_allocation ();
+}
+EOF
+
+LDEMUL_BEFORE_PARSE=elf_x86_64_before_parse
+LDEMUL_BEFORE_ALLOCATION=elf_x86_64_before_allocation
+
+# Define some shell vars to insert bits of code into the standard elf
+# parse_args and list_options functions.
+#
+
+PARSE_AND_LIST_LONGOPTS_X86_64='
+  { "enable-gnu2-tls-tag", no_argument, NULL, OPTION_ENABLE_GNU2_TLS_TAG },
+  { "disable-gnu2-tls-tag", no_argument, NULL, OPTION_DISABLE_GNU2_TLS_TAG },
+'
+
+PARSE_AND_LIST_OPTIONS_X86_64='
+  if (DEFAULT_LD_ENABLE_GNU2_TLS_TAG != 0)
+    fprintf (file, _("\
+  --enable-gnu2-tls-tag       Add GLIBC_ABI_GNU2_TLS dependency(default)\n\
+  --disable-gnu2-tls-tag      Add not GLIBC_ABI_GNU2_TLS dependency\n"));
+  else
+    fprintf (file, _("\
+  --enable-gnu2-tls-tag       Add GLIBC_ABI_GNU2_TLS dependency\n\
+  --disable-gnu2-tls-tag      Add not GLIBC_ABI_GNU2_TLS dependency(default)\n"));
+'
+
+PARSE_AND_LIST_ARGS_CASES_X86_64='
+    case OPTION_ENABLE_GNU2_TLS_TAG:
+      params.gnu2_tls = 1;
+      break;
+
+    case OPTION_DISABLE_GNU2_TLS_TAG:
+      params.gnu2_tls = 0;
+      break;
+'
+
+PARSE_AND_LIST_LONGOPTS="$PARSE_AND_LIST_LONGOPTS $PARSE_AND_LIST_LONGOPTS_X86_64"
+PARSE_AND_LIST_OPTIONS="$PARSE_AND_LIST_OPTIONS $PARSE_AND_LIST_OPTIONS_X86_64"
+PARSE_AND_LIST_ARGS_CASES="$PARSE_AND_LIST_ARGS_CASES $PARSE_AND_LIST_ARGS_CASES_X86_64"
diff --git a/ld/emultempl/elf-x86.em b/ld/emultempl/elf-x86.em
index f72a0cd0d4a..411a4d62294 100644
--- a/ld/emultempl/elf-x86.em
+++ b/ld/emultempl/elf-x86.em
@@ -56,61 +56,3 @@ EOF
 
 LDEMUL_BEFORE_PARSE=elf_x86_before_parse
 fi
-
-case x${OUTPUT_FORMAT}${CALL_NOP_BYTE} in
-  x*x86-64*0x67)
-fragment <<EOF
-
-static void
-elf_x86_64_before_parse (void)
-{
-  params.mark_plt = DEFAULT_LD_Z_MARK_PLT;
-
-  elf_x86_before_parse ();
-}
-EOF
-
-    LDEMUL_BEFORE_PARSE=elf_x86_64_before_parse
-    ;;
-esac
-
-case x${OUTPUT_FORMAT} in
-  x*x86-64*)
-fragment <<EOF
-
-static void
-elf_x86_64_before_allocation (void)
-{
-  if (!bfd_link_relocatable (&link_info)
-      && is_elf_hash_table (link_info.hash)
-      && expld.phase != lang_mark_phase_enum)
-    {
-      struct elf_link_hash_table *htab = elf_hash_table (&link_info);
-      /* Run one_lang_size_sections_pass to estimate the output section
-	 layout before sizing dynamic sections.  */
-      expld.dataseg.phase = exp_seg_none;
-      expld.phase = lang_mark_phase_enum;
-      /* NB: Exclude linker created GOT setions when estimating output
-	 section layout as sizing dynamic sections may change linker
-	 created GOT sections.  */
-      if (htab->sgot != NULL)
-	htab->sgot->flags |= SEC_EXCLUDE;
-      if (htab->sgotplt != NULL)
-	htab->sgotplt->flags |= SEC_EXCLUDE;
-      one_lang_size_sections_pass (NULL, false);
-      /* Restore linker created GOT setions.  */
-      if (htab->sgot != NULL)
-	htab->sgot->flags &= ~SEC_EXCLUDE;
-      if (htab->sgotplt != NULL)
-	htab->sgotplt->flags &= ~SEC_EXCLUDE;
-      lang_reset_memory_regions ();
-    }
-
-  gld${EMULATION_NAME}_before_allocation ();
-}
-
-EOF
-
-LDEMUL_BEFORE_ALLOCATION=elf_x86_64_before_allocation
-    ;;
-esac
diff --git a/ld/ld.texi b/ld/ld.texi
index 413335ad765..0135bb846fb 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1743,6 +1743,14 @@ Supported for Linux/i386 and Linux/x86_64.
 
 @end table
 
+@item --enable-gnu2-tls-tag
+@itemx --disable-gnu2-tls-tag
+Add @code{GLIBC_ABI_GNU2_TLS} version tag dependency in output programs
+and shared libraries when linking against glibc if input relocatable
+object files have @code{R_X86_64_TLSDESC_CALL} relocation so that the
+output will fail to load and run at run-time against glibc which doesn't
+have the @code{GLIBC_ABI_GNU2_TLS} version.  Supported for Linux/x86_64.
+
 Other keywords are ignored for Solaris compatibility.
 
 @kindex -(
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 815da76a4c0..011fb4ba0ed 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -471,6 +471,9 @@ enum option_values
   OPTION_NO_LITERAL_MOVEMENT,
   OPTION_ABI_WINDOWED,
   OPTION_ABI_CALL0,
+  /* Used by emultempl/elf-x86-64.em.  */
+  OPTION_ENABLE_GNU2_TLS_TAG,
+  OPTION_DISABLE_GNU2_TLS_TAG,
 };
 
 /* The initial parser states.  */
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-1.rd b/ld/testsuite/ld-x86-64/gnu2-tls-1.rd
new file mode 100644
index 00000000000..3eb926a227c
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-1.rd
@@ -0,0 +1,7 @@
+#...
+Version needs section '.gnu.version_r' contains 1 entry:
+ 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_GNU2_TLS  Flags: none  Version: [0-9]+
+#pass
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-1a.od b/ld/testsuite/ld-x86-64/gnu2-tls-1a.od
new file mode 100644
index 00000000000..e237b26898d
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-1a.od
@@ -0,0 +1,13 @@
+
+.*: +file format .*
+#...
+[a-f0-9]+ <main>:
+ +[a-f0-9]+:	48 83 ec 08          	sub    \$0x8,%rsp
+ +[a-f0-9]+:	48 8d 05 ([0-9a-f]{2} ){4}	lea    0x[a-f0-9]+\(%rip\),%rax        # [a-f0-9]+ <foo@@Base\+0x[a-f0-9]+>
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	cs nopw 0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	ff 10                	call   \*\(%rax\)
+ +[a-f0-9]+:	64 8b 00             	mov    %fs:\(%rax\),%eax
+ +[a-f0-9]+:	48 83 c4 08          	add    \$0x8,%rsp
+ +[a-f0-9]+:	c3                   	ret
+ +[a-f0-9]+:	90                   	nop
+#pass
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-1a.s b/ld/testsuite/ld-x86-64/gnu2-tls-1a.s
new file mode 100644
index 00000000000..b8c004538ff
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-1a.s
@@ -0,0 +1,14 @@
+	.section	.text.startup,"ax",@progbits
+	.p2align 4
+	.globl	main
+	.type	main, @function
+main:
+	subq	$8, %rsp
+	leaq	foo@TLSDESC(%rip), %rax
+	.nops 10
+	call	*foo@TLSCALL(%rax)
+	movl	%fs:(%rax), %eax
+	addq	$8, %rsp
+	ret
+	.size	main, .-main
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-1b.od b/ld/testsuite/ld-x86-64/gnu2-tls-1b.od
new file mode 100644
index 00000000000..8bd685006bd
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-1b.od
@@ -0,0 +1,13 @@
+
+.*: +file format .*
+#...
+[a-f0-9]+ <main>:
+ +[a-f0-9]+:	48 83 ec 08          	sub    \$0x8,%rsp
+ +[a-f0-9]+:	48 c7 c0 fc ff ff ff 	mov    \$0xfffffffffffffffc,%rax
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	cs nopw 0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	66 90                	xchg   %ax,%ax
+ +[a-f0-9]+:	64 8b 00             	mov    %fs:\(%rax\),%eax
+ +[a-f0-9]+:	48 83 c4 08          	add    \$0x8,%rsp
+ +[a-f0-9]+:	c3                   	ret
+ +[a-f0-9]+:	90                   	nop
+#pass
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-1b.s b/ld/testsuite/ld-x86-64/gnu2-tls-1b.s
new file mode 100644
index 00000000000..fcc355f7e5c
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-1b.s
@@ -0,0 +1,9 @@
+	.text
+	.globl	foo
+	.section	.tbss,"awT",@nobits
+	.align 4
+	.type	foo, @object
+	.size	foo, 4
+foo:
+	.zero	4
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-1c.od b/ld/testsuite/ld-x86-64/gnu2-tls-1c.od
new file mode 100644
index 00000000000..8bd685006bd
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-1c.od
@@ -0,0 +1,13 @@
+
+.*: +file format .*
+#...
+[a-f0-9]+ <main>:
+ +[a-f0-9]+:	48 83 ec 08          	sub    \$0x8,%rsp
+ +[a-f0-9]+:	48 c7 c0 fc ff ff ff 	mov    \$0xfffffffffffffffc,%rax
+ +[a-f0-9]+:	66 2e 0f 1f 84 00 00 00 00 00 	cs nopw 0x0\(%rax,%rax,1\)
+ +[a-f0-9]+:	66 90                	xchg   %ax,%ax
+ +[a-f0-9]+:	64 8b 00             	mov    %fs:\(%rax\),%eax
+ +[a-f0-9]+:	48 83 c4 08          	add    \$0x8,%rsp
+ +[a-f0-9]+:	c3                   	ret
+ +[a-f0-9]+:	90                   	nop
+#pass
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-2.rd b/ld/testsuite/ld-x86-64/gnu2-tls-2.rd
new file mode 100644
index 00000000000..33ef8acb232
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-2.rd
@@ -0,0 +1,4 @@
+#failif
+#...
+  0x[a-f0-9]+:   Name: GLIBC_ABI_GNU2_TLS  Flags: none  Version: [0-9]+
+#...
diff --git a/ld/testsuite/ld-x86-64/gnu2-tls-2.s b/ld/testsuite/ld-x86-64/gnu2-tls-2.s
new file mode 100644
index 00000000000..d957eb71f6d
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/gnu2-tls-2.s
@@ -0,0 +1,13 @@
+	.text
+	.p2align 4
+	.globl	func
+	.type	func, @function
+func:
+	leaq	foo@tlsld(%rip), %rdi
+	call	__tls_get_addr@PLT
+	data16	leaq	foo@tlsgd(%rip), %rdi
+	.value	0x6666
+	rex64
+	call	__tls_get_addr@PLT
+	.size	func, .-func
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 6b86bc3d018..0d88d20c2f6 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -2358,7 +2358,7 @@ run_dump_test "ibt-plt-3b-x32"
 run_dump_test "ibt-plt-3c-x32"
 run_dump_test "ibt-plt-3d-x32"
 
-# Skip -z mark-plt tests on MUSL.
+# Skip -z mark-plt and --enable-gnu2-tls-tag tests on MUSL.
 if { [istarget "x86_64-*-musl*"]} {
     set ASFLAGS "$saved_ASFLAGS"
     return
@@ -2384,6 +2384,49 @@ if { [check_compiler_available] } {
 	     {readelf {-W --version-info} mark-plt-1b.rd}} \
 	    "mark-plt-1.so" \
 	] \
+	[list \
+	    "Build gnu2-tls-1.so" \
+	    "-shared -Wl,--no-as-needed,--enable-gnu2-tls-tag" \
+	    "-fPIC" \
+	    { gnu2-tls-1a.s gnu2-tls-1b.s } \
+	    {{objdump {-dw} gnu2-tls-1a.od}
+	     {readelf {-W --version-info} gnu2-tls-1.rd}} \
+	    "gnu2-tls-1.so" \
+	] \
+	[list \
+	    "Build gnu2-tls-1-no-gnu2-tls.so" \
+	    "-shared -Wl,--no-as-needed,--enable-gnu2-tls-tag" \
+	    "-fPIC" \
+	    { gnu2-tls-1.s } \
+	    {{readelf {-W --version-info} gnu2-tls-2.rd}} \
+	    "gnu2-tls-1-no-gnu2-tls.so" \
+	] \
+	[list \
+	    "Build gnu2-tls-1 (PDE)" \
+	    "-Wl,--no-as-needed,--enable-gnu2-tls-tag" \
+	    "-fPIC" \
+	    { gnu2-tls-1a.s gnu2-tls-1b.s } \
+	    {{objdump {-dw} gnu2-tls-1b.od}
+	     {readelf {-W --version-info} gnu2-tls-2.rd}} \
+	    "gnu2-tls-1.pde" \
+	] \
+	[list \
+	    "Build gnu2-tls-1 (PIE)" \
+	    "-pie -Wl,--no-as-needed,--enable-gnu2-tls-tag" \
+	    "-fPIC" \
+	    { gnu2-tls-1a.s gnu2-tls-1b.s } \
+	    {{objdump {-dw} gnu2-tls-1c.od}
+	     {readelf {-W --version-info} gnu2-tls-2.rd}} \
+	    "gnu2-tls-1.pie" \
+	] \
+	[list \
+	    "Build gnu2-tls-2.so" \
+	    "-shared -Wl,--no-as-needed,--enable-gnu2-tls-tag" \
+	    "-fPIC" \
+	    { gnu2-tls-2.s } \
+	    {{readelf {-W --version-info} gnu2-tls-2.rd}} \
+	    "gnu2-tls-2.s.so" \
+	] \
     ]
 }
 
-- 
2.50.1



More information about the Binutils mailing list