[v3,10/11] New test - gdb.base/tls-dlobj.exp

Message ID 20241025033431.36274-11-kevinb@redhat.com
State New
Headers
Series GDB-internal TLS support for Linux targets |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Kevin Buettner Oct. 25, 2024, 2:26 a.m. UTC
  This test exercises musl_link_map_to_tls_module_id() and
glibc_link_map_to_tls_module_id(), both of which are in solib-svr4.c.

Prior to writing this test, I had only written what is now named
'musl_link_map_to_tls_module_id' and it worked for both GLIBC and
MUSL.  Once I wrote this new test, tls-dlobj.exp, there were a number
of tests which didn't work with GLIBC.  This led me to write a
GLIBC-specific link map to module id function, i.e,
'glibc_link_map_to_tls_module_id'.

It only has one compilation scenario, in which the pthread(s) library
is used - as noted in a comment, it became too much of a hassle to try
to KFAIL things, though it certainly could have been done in much the
same was as was done in gdb.base/multiobj.exp.  It didn't seem that
important to do so, however, since I believe that the other tests
have adequate coverage for different compilation scenarios.
---
 gdb/testsuite/gdb.base/tls-dlobj-lib.c |  87 ++++++
 gdb/testsuite/gdb.base/tls-dlobj.c     | 311 ++++++++++++++++++++
 gdb/testsuite/gdb.base/tls-dlobj.exp   | 378 +++++++++++++++++++++++++
 3 files changed, 776 insertions(+)
 create mode 100644 gdb/testsuite/gdb.base/tls-dlobj-lib.c
 create mode 100644 gdb/testsuite/gdb.base/tls-dlobj.c
 create mode 100644 gdb/testsuite/gdb.base/tls-dlobj.exp
  

Patch

diff --git a/gdb/testsuite/gdb.base/tls-dlobj-lib.c b/gdb/testsuite/gdb.base/tls-dlobj-lib.c
new file mode 100644
index 00000000000..3f8ebc4f6a7
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-dlobj-lib.c
@@ -0,0 +1,87 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 Free Software Foundation, Inc.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program needs to be compiled with preprocessor symbol set to
+   a small integer, e.g. "gcc -DN=1 ..."  With N defined, the CONCAT2
+   and CONCAT3 macros will construct suitable names for the global
+   variables and functions.  */
+
+#define CONCAT2(a,b) CONCAT2_(a,b)
+#define CONCAT2_(a,b) a ## b
+
+#define CONCAT3(a,b,c) CONCAT3_(a,b,c)
+#define CONCAT3_(a,b,c) a ## b ## c
+
+/* For N=1, this ends up being...
+   __thread int tls_lib1_tbss_1;
+   __thread int tls_lib1_tbss_2;
+   __thread int tls_lib1_tdata_1 = 196;
+   __thread int tls_lib1_tdata_2 = 197; */
+
+__thread int CONCAT3(tls_lib, N, _tbss_1);
+__thread int CONCAT3(tls_lib, N, _tbss_2);
+__thread int CONCAT3(tls_lib, N, _tdata_1) = CONCAT2(N, 96);
+__thread int CONCAT3(tls_lib, N,  _tdata_2) = CONCAT2(N, 97);
+
+/* Substituting for N, define function:
+  
+    int get_tls_libN_var (int which) .  */
+
+int
+CONCAT3(get_tls_lib, N, _var) (int which)
+{
+  switch (which)
+    {
+    case 0:
+      return -1;
+    case 1:
+      return CONCAT3(tls_lib, N, _tbss_1);
+    case 2:
+      return CONCAT3(tls_lib, N, _tbss_2);
+    case 3:
+      return CONCAT3(tls_lib, N, _tdata_1);
+    case 4:
+      return CONCAT3(tls_lib, N, _tdata_2);
+    }
+  return -1;
+}
+
+/* Substituting for N, define function:
+  
+   void set_tls_libN_var (int which, int val) .  */
+
+void
+CONCAT3(set_tls_lib, N, _var) (int which, int val)
+{
+  switch (which)
+    {
+    case 0:
+      break;
+    case 1:
+      CONCAT3(tls_lib, N, _tbss_1) = val;
+      break;
+    case 2:
+      CONCAT3(tls_lib, N, _tbss_2) = val;
+      break;
+    case 3:
+      CONCAT3(tls_lib, N, _tdata_1) = val;
+      break;
+    case 4:
+      CONCAT3(tls_lib, N, _tdata_2) = val;
+      break;
+    }
+}
diff --git a/gdb/testsuite/gdb.base/tls-dlobj.c b/gdb/testsuite/gdb.base/tls-dlobj.c
new file mode 100644
index 00000000000..322bdda0393
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-dlobj.c
@@ -0,0 +1,311 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 Free Software Foundation, Inc.
+
+   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.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef void (*setter_ftype) (int which, int val);
+
+__thread int tls_main_tbss_1;
+__thread int tls_main_tbss_2;
+__thread int tls_main_tdata_1 = 96;
+__thread int tls_main_tdata_2 = 97;
+
+extern void set_tls_lib10_var (int which, int val);
+extern void set_tls_lib11_var (int which, int val);
+
+volatile int data;
+
+static void
+set_tls_main_var (int which, int val)
+{
+  switch (which)
+    {
+    case 1:
+      tls_main_tbss_1 = val;
+      break;
+    case 2:
+      tls_main_tbss_2 = val;
+      break;
+    case 3:
+      tls_main_tdata_1 = val;
+      break;
+    case 4:
+      tls_main_tdata_2 = val;
+      break;
+    }
+}
+
+void
+use_it (int a)
+{
+  data = a;
+}
+
+static void *
+load_dso (char *dso_name, int n, setter_ftype *setterp)
+{
+  char buf[80];
+  void *sym;
+  void *handle = dlopen (dso_name, RTLD_NOW | RTLD_GLOBAL);
+  if (handle == NULL)
+    {
+      fprintf (stderr, "dlopen of DSO '%s' failed: %s\n", dso_name, dlerror ());
+      exit (1);
+    }
+  sprintf (buf, "set_tls_lib%d_var", n);
+  sym = dlsym (handle, buf);
+  assert (sym != NULL);
+  *setterp = sym;
+
+  /* Some libc implementations (for some architectures) refuse to
+     initialize TLS data structures (specifically, the DTV) without
+     first calling dlsym on one of the TLS symbols.  */
+  sprintf (buf, "tls_lib%d_tdata_1", n);
+  assert (dlsym (handle, buf) != NULL);
+
+  return handle;
+}
+
+int
+main (int argc, char **argv)
+{
+  int i, status;
+  setter_ftype s0, s1, s2, s3, s4, s10, s11;
+  void *h1 = load_dso (OBJ1, 1, &s1);
+  void *h2 = load_dso (OBJ2, 2, &s2);
+  void *h3 = load_dso (OBJ3, 3, &s3);
+  void *h4 = load_dso (OBJ4, 4, &s4);
+  s0 = set_tls_main_var;
+  s10 = set_tls_lib10_var;
+  s11 = set_tls_lib11_var;
+
+  use_it (0);		/* main-breakpoint-1 */
+
+  /* Set TLS variables in main program and all libraries.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 10 + i);
+  for (i = 1; i <= 4; i++)
+    s1 (i, 110 + i);
+  for (i = 1; i <= 4; i++)
+    s2 (i, 210 + i);
+  for (i = 1; i <= 4; i++)
+    s3 (i, 310 + i);
+  for (i = 1; i <= 4; i++)
+    s4 (i, 410 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1010 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1110 + i);
+
+  use_it (0);		/* main-breakpoint-2 */
+
+  /* Unload lib2 and lib3.  */
+  status = dlclose (h2);
+  assert (status == 0);
+  status = dlclose (h3);
+  assert (status == 0);
+
+  /* Set TLS variables in main program and in libraries which are still
+     loaded.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 20 + i);
+  for (i = 1; i <= 4; i++)
+    s1 (i, 120 + i);
+  for (i = 1; i <= 4; i++)
+    s4 (i, 420 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1020 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1120 + i);
+
+  use_it (0);		/* main-breakpoint-3 */
+
+  /* Load lib3.  */
+  h3 = load_dso (OBJ3, 3, &s3);
+
+  /* Set TLS vars again; currently, only lib2 is not loaded.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 30 + i);
+  for (i = 1; i <= 4; i++)
+    s1 (i, 130 + i);
+  for (i = 1; i <= 4; i++)
+    s3 (i, 330 + i);
+  for (i = 1; i <= 4; i++)
+    s4 (i, 430 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1030 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1130 + i);
+
+  use_it (0);		/* main-breakpoint-4 */
+
+  /* Unload lib1 and lib4; load lib2.  */
+  status = dlclose (h1);
+  assert (status == 0);
+  status = dlclose (h4);
+  assert (status == 0);
+  h2 = load_dso (OBJ2, 2, &s2);
+
+  /* Set TLS vars; currently, lib2 and lib3 are loaded,
+     lib1 and lib4 are not.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 40 + i);
+  for (i = 1; i <= 4; i++)
+    s2 (i, 240 + i);
+  for (i = 1; i <= 4; i++)
+    s3 (i, 340 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1040 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1140 + i);
+
+  use_it (0);		/* main-breakpoint-5 */
+
+  /* Load lib4 and lib1.  Unload lib2.  */
+  h4 = load_dso (OBJ4, 4, &s4);
+  h1 = load_dso (OBJ1, 1, &s1);
+  status = dlclose (h2);
+  assert (status == 0);
+
+  /* Set TLS vars; currently, lib1, lib3, and lib4 are loaded;
+     lib2 is not loaded.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 50 + i);
+  for (i = 1; i <= 4; i++)
+    s1 (i, 150 + i);
+  for (i = 1; i <= 4; i++)
+    s3 (i, 350 + i);
+  for (i = 1; i <= 4; i++)
+    s4 (i, 450 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1050 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1150 + i);
+
+  use_it (0);		/* main-breakpoint-6 */
+
+  /* Load lib2, unload lib1, lib3, and lib4; then load lib3 again.  */
+  h2 = load_dso (OBJ2, 2, &s2);
+  status = dlclose (h1);
+  assert (status == 0);
+  status = dlclose (h3);
+  assert (status == 0);
+  status = dlclose (h4);
+  assert (status == 0);
+  h3 = load_dso (OBJ3, 3, &s3);
+
+  /* Set TLS vars; currently, lib2 and lib3 are loaded;
+     lib1 and lib4 are not loaded.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 60 + i);
+  for (i = 1; i <= 4; i++)
+    s2 (i, 260 + i);
+  for (i = 1; i <= 4; i++)
+    s3 (i, 360 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1060 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1160 + i);
+
+  use_it (0);		/* main-breakpoint-7 */
+
+  /* Unload lib3 and lib2, then (re)load lib4, lib3, lib2, and lib1,
+     in that order.  */
+  status = dlclose (h3);
+  assert (status == 0);
+  status = dlclose (h2);
+  assert (status == 0);
+  h4 = load_dso (OBJ4, 4, &s4);
+  h3 = load_dso (OBJ3, 3, &s3);
+  h2 = load_dso (OBJ2, 2, &s2);
+  h1 = load_dso (OBJ1, 1, &s1);
+
+  /* Set TLS vars; currently, lib1, lib2, lib3, and lib4 are all
+     loaded.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 70 + i);
+  for (i = 1; i <= 4; i++)
+    s1 (i, 170 + i);
+  for (i = 1; i <= 4; i++)
+    s2 (i, 270 + i);
+  for (i = 1; i <= 4; i++)
+    s3 (i, 370 + i);
+  for (i = 1; i <= 4; i++)
+    s4 (i, 470 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1070 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1170 + i);
+
+  use_it (0);		/* main-breakpoint-8 */
+
+  /* Unload lib3, lib1, and lib4.  */
+  status = dlclose (h3);
+  assert (status == 0);
+  status = dlclose (h1);
+  assert (status == 0);
+  status = dlclose (h4);
+  assert (status == 0);
+
+  /* Set TLS vars; currently, lib2 is loaded; lib1, lib3, and lib4 are
+     not.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 80 + i);
+  for (i = 1; i <= 4; i++)
+    s2 (i, 280 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1080 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1180 + i);
+
+  use_it (0);		/* main-breakpoint-9 */
+
+  /* Load lib3, unload lib2, load lib4.  */
+  h3 = load_dso (OBJ3, 3, &s3);
+  status = dlclose (h2);
+  assert (status == 0);
+  h4 = load_dso (OBJ4, 4, &s4);
+
+  /* Set TLS vars; currently, lib3 and lib4 are loaded; lib1 and lib2
+     are not.  */
+  for (i = 1; i <= 4; i++)
+    s0 (i, 90 + i);
+  for (i = 1; i <= 4; i++)
+    s3 (i, 390 + i);
+  for (i = 1; i <= 4; i++)
+    s4 (i, 490 + i);
+  for (i = 1; i <= 4; i++)
+    s10 (i, 1090 + i);
+  for (i = 1; i <= 4; i++)
+    s11 (i, 1190 + i);
+
+  use_it (0);		/* main-breakpoint-10 */
+
+  /* Attempt to keep variables in the main program from being optimized
+     away.  */
+  use_it (tls_main_tbss_1);
+  use_it (tls_main_tbss_2);
+  use_it (tls_main_tdata_1);
+  use_it (tls_main_tdata_2);
+
+  use_it (100);		/* main-breakpoint-last */
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/tls-dlobj.exp b/gdb/testsuite/gdb.base/tls-dlobj.exp
new file mode 100644
index 00000000000..02f2ff81219
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-dlobj.exp
@@ -0,0 +1,378 @@ 
+# Copyright 2024 Free Software Foundation, Inc.
+# 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.
+
+# Test that the GDB-internal TLS link map to module id mapping code
+# works correctly when debugging a program which is linked against
+# shared objects and which also loads and unloads other shared objects
+# in different orders.  For targets which have GDB-internal TLS
+# support, it'll check both GDB-internal TLS support as well as that
+# provided by a helper library such as libthread_db.
+
+source $srcdir/$subdir/tls-common.exp.tcl
+
+require allow_shlib_tests
+
+standard_testfile
+
+set libsrc "${srcdir}/${subdir}/${testfile}-lib.c"
+
+# These will be dlopen'd:
+set lib1obj [standard_output_file "${testfile}1-lib.so"]
+set lib2obj [standard_output_file "${testfile}2-lib.so"]
+set lib3obj [standard_output_file "${testfile}3-lib.so"]
+set lib4obj [standard_output_file "${testfile}4-lib.so"]
+
+# These will be dynamically linked with the main program:
+set lib10obj [standard_output_file "${testfile}10-lib.so"]
+set lib11obj [standard_output_file "${testfile}11-lib.so"]
+
+# Due to problems with some versions of glibc, we expect some tests to
+# fail due to TLS storage not being allocated/initialized.  Test
+# command CMD using regular expression RE, and use XFAIL instead of
+# FAIL when the relevant RE is matched and COND is true when evaluated
+# in the upper level.
+
+proc gdb_test_with_xfail { cmd re cond} {
+    gdb_test_multiple $cmd $cmd {
+	-re -wrap $re {
+	    pass $gdb_test_name
+	}
+	-re -wrap "The inferior has not yet allocated storage for thread-local variables.*" {
+	    if [ uplevel 1 [list expr $cond]] {
+		xfail $gdb_test_name
+	    } else {
+		fail $gdb_test_name
+	    }
+	}
+    }
+}
+
+proc do_tests {force_internal_tls} {
+    clean_restart $::binfile
+    if ![runto_main] {
+	return
+    }
+
+    if $force_internal_tls {
+	gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"]
+    gdb_continue_to_breakpoint "main-breakpoint-1"
+
+    with_test_prefix "before assignments" {
+	gdb_test "print tls_main_tbss_1" ".* = 0"
+	gdb_test "print tls_main_tbss_2" ".* = 0"
+	gdb_test "print tls_main_tdata_1" ".* = 96"
+	gdb_test "print tls_main_tdata_2" ".* = 97"
+
+	# For these tests, where we're attempting to access TLS vars
+	# in a dlopen'd library, but before assignment to any of the
+	# vars, so it could happen that storage hasn't been allocated
+	# yet.  But it might also work.  (When testing against MUSL,
+	# things just work; GLIBC ends to produce the TLS error.) So
+	# accept either the right answer or a TLS error message.
+
+	set tlserr "The inferior has not yet allocated storage for thread-local variables.*"
+	foreach n {1 2 3 4} {
+	    gdb_test "print tls_lib${n}_tbss_1" \
+		     "0|${tlserr}"
+	    gdb_test "print tls_lib${n}_tbss_2" \
+		     "0|${tlserr}"
+	    gdb_test "print tls_lib${n}_tdata_1" \
+		     "96|${tlserr}"
+	    gdb_test "print tls_lib${n}_tdata_2" \
+		     "97|${tlserr}"
+	}
+	foreach n {10 11} {
+	    gdb_test "print tls_lib${n}_tbss_1" ".* = 0"
+	    gdb_test "print tls_lib${n}_tbss_2" ".* = 0"
+	    gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}96"
+	    gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}97"
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"]
+    gdb_continue_to_breakpoint "main-breakpoint-2"
+
+    with_test_prefix "at main-breakpoint-2" {
+	gdb_test "print tls_main_tbss_1" ".* = 11"
+	gdb_test "print tls_main_tbss_2" ".* = 12"
+	gdb_test "print tls_main_tdata_1" ".* = 13"
+	gdb_test "print tls_main_tdata_2" ".* = 14"
+
+	foreach n {1 2 3 4 10 11} {
+	    gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}11"
+	    gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}12"
+	    gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}13"
+	    gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}14"
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-3"]
+    gdb_continue_to_breakpoint "main-breakpoint-3"
+
+    # At this point lib2 and lib3 have been unloaded.  Also, TLS vars
+    # in remaining libraries have been changed.
+
+    with_test_prefix "at main-breakpoint-3" {
+	gdb_test "print tls_main_tbss_1" ".* = 21"
+	gdb_test "print tls_main_tbss_2" ".* = 22"
+	gdb_test "print tls_main_tdata_1" ".* = 23"
+	gdb_test "print tls_main_tdata_2" ".* = 24"
+
+	foreach n {1 4 10 11} {
+	    gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}21"
+	    gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}22"
+	    gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}23"
+	    gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}24"
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-4"]
+    gdb_continue_to_breakpoint "main-breakpoint-4"
+
+    # lib3 has been loaded again; lib2 is the only one not loaded.
+
+    with_test_prefix "at main-breakpoint-4" {
+	gdb_test "print tls_main_tbss_1" ".* = 31"
+	gdb_test "print tls_main_tbss_2" ".* = 32"
+	gdb_test "print tls_main_tdata_1" ".* = 33"
+	gdb_test "print tls_main_tdata_2" ".* = 34"
+
+	set cond { $n == 3 }
+	foreach n {1 3 4 10 11} {
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}31" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}32" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}33" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}34" $cond
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-5"]
+    gdb_continue_to_breakpoint "main-breakpoint-5"
+
+    # lib2 and lib3 are loaded; lib1 and lib4 are not.
+
+    with_test_prefix "at main-breakpoint-5" {
+	gdb_test "print tls_main_tbss_1" ".* = 41"
+	gdb_test "print tls_main_tbss_2" ".* = 42"
+	gdb_test "print tls_main_tdata_1" ".* = 43"
+	gdb_test "print tls_main_tdata_2" ".* = 44"
+
+	set cond { $n == 2 || $n == 3 }
+	foreach n {2 3 10 11} {
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}41" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}42" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}43" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}44" $cond
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-6"]
+    gdb_continue_to_breakpoint "main-breakpoint-6"
+
+    # lib1, lib3 and lib4 are loaded; lib2 is not loaded.
+
+    with_test_prefix "at main-breakpoint-6" {
+	gdb_test "print tls_main_tbss_1" ".* = 51"
+	gdb_test "print tls_main_tbss_2" ".* = 52"
+	gdb_test "print tls_main_tdata_1" ".* = 53"
+	gdb_test "print tls_main_tdata_2" ".* = 54"
+
+	set cond { $n == 1 || $n == 3 || $n == 4}
+	foreach n {1 3 4 10 11} {
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}51" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}52" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}53" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}54" $cond
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-7"]
+    gdb_continue_to_breakpoint "main-breakpoint-7"
+
+    # lib2 and lib3 are loaded; lib1 and lib4 are not.
+
+    with_test_prefix "at main-breakpoint-7" {
+	gdb_test "print tls_main_tbss_1" ".* = 61"
+	gdb_test "print tls_main_tbss_2" ".* = 62"
+	gdb_test "print tls_main_tdata_1" ".* = 63"
+	gdb_test "print tls_main_tdata_2" ".* = 64"
+
+	set cond { $n == 2 || $n == 3 }
+	foreach n {2 3 10 11} {
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}61" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}62" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}63" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}64" $cond
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-8"]
+    gdb_continue_to_breakpoint "main-breakpoint-8"
+
+    # lib1, lib2, lib3, and lib4 are all loaded.
+
+    with_test_prefix "at main-breakpoint-8" {
+	gdb_test "print tls_main_tbss_1" ".* = 71"
+	gdb_test "print tls_main_tbss_2" ".* = 72"
+	gdb_test "print tls_main_tdata_1" ".* = 73"
+	gdb_test "print tls_main_tdata_2" ".* = 74"
+
+	foreach n {1 2 3 4 10 11} {
+	    gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}71"
+	    gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}72"
+	    gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}73"
+	    gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}74"
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-9"]
+    gdb_continue_to_breakpoint "main-breakpoint-9"
+
+    # lib2 is loaded; lib1, lib3, and lib4 are not.
+
+    with_test_prefix "at main-breakpoint-9" {
+	gdb_test "print tls_main_tbss_1" ".* = 81"
+	gdb_test "print tls_main_tbss_2" ".* = 82"
+	gdb_test "print tls_main_tdata_1" ".* = 83"
+	gdb_test "print tls_main_tdata_2" ".* = 84"
+
+	foreach n {2 10 11} {
+	    gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}81"
+	    gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}82"
+	    gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}83"
+	    gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}84"
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-10"]
+    gdb_continue_to_breakpoint "main-breakpoint-10"
+
+    # lib3 and lib4 are loaded; lib1 and lib2 are not.
+
+    with_test_prefix "at main-breakpoint-10" {
+	gdb_test "print tls_main_tbss_1" ".* = 91"
+	gdb_test "print tls_main_tbss_2" ".* = 92"
+	gdb_test "print tls_main_tdata_1" ".* = 93"
+	gdb_test "print tls_main_tdata_2" ".* = 94"
+
+	set cond { $n == 3 || $n == 4 }
+	foreach n {3 4 10 11} {
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond
+	}
+    }
+
+    # gdb_interact
+
+    set corefile ${::binfile}.core
+    set core_supported 0
+    if { ![is_remote host] } {
+	set core_supported [gdb_gcore_cmd $corefile "save corefile"]
+    }
+
+    # Finish test early if no core file was made.
+    if !$core_supported {
+	return
+    }
+
+    clean_restart $::binfile
+
+    set core_loaded [gdb_core_cmd $corefile "load corefile"]
+    if { $core_loaded == -1 } {
+	return
+    }
+
+    with_test_prefix "core file" {
+	if $force_internal_tls {
+	    gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+	}
+
+	gdb_test "print tls_main_tbss_1" ".* = 91"
+	gdb_test "print tls_main_tbss_2" ".* = 92"
+	gdb_test "print tls_main_tdata_1" ".* = 93"
+	gdb_test "print tls_main_tdata_2" ".* = 94"
+
+	set cond { $n == 3 || $n == 4 }
+	foreach n {3 4 10 11} {
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond
+	    gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond
+	}
+    }
+}
+
+# Build shared objects for dlopen:
+if { [gdb_compile_shlib $libsrc $lib1obj [list debug additional_flags=-DN=1]] != "" } {
+    untested "failed to compile shared object"
+    return -1
+}
+if { [gdb_compile_shlib $libsrc $lib2obj [list debug additional_flags=-DN=2]] != "" } {
+    untested "failed to compile shared object"
+    return -1
+}
+if { [gdb_compile_shlib $libsrc $lib3obj [list debug additional_flags=-DN=3]] != "" } {
+    untested "failed to compile shared object"
+    return -1
+}
+if { [gdb_compile_shlib $libsrc $lib4obj [list debug additional_flags=-DN=4]] != "" } {
+    untested "failed to compile shared object"
+    return -1
+}
+
+# Build shared objects to link against main program:
+if { [gdb_compile_shlib $libsrc $lib10obj [list debug additional_flags=-DN=10]] != "" } {
+    untested "failed to compile shared object"
+    return -1
+}
+if { [gdb_compile_shlib $libsrc $lib11obj [list debug additional_flags=-DN=11]] != "" } {
+    untested "failed to compile shared object"
+    return -1
+}
+
+# Use gdb_compile_pthreads to build and link the main program for
+# testing.  It's also possible to run the tests using plain old
+# gdb_compile, but this adds complexity with setting up additional
+# KFAILs.  (When run using GLIBC versions earlier than 2.34, a program
+# that's not dynamically linked against libpthread will lack a working
+# libthread_db, and, therefore, won't be able to access thread local
+# storage without GDB-internal TLS support.  Additional complications
+# arise from when testing on x86_64 with -m32, which tends to work
+# okay on GLIBC 2.34 and newer, but not older versions.  It gets messy
+# to properly sort out all of these cases.)
+#
+# This test was originally written to do it both ways, i.e. with both
+# both gdb_compile and gdb_compile_pthreads, but the point of this
+# test is to check that the link map address to TLS module id mapping
+# code works correctly in programs which use lots of dlopen and
+# dlclose calls in various orders - and that can be done using just
+# gdb_compile_pthreads.
+
+if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \
+			    [list debug shlib_load \
+				  shlib=${lib10obj} \
+				  shlib=${lib11obj} \
+				  additional_flags=-DOBJ1=\"${lib1obj}\" \
+				  additional_flags=-DOBJ2=\"${lib2obj}\" \
+				  additional_flags=-DOBJ3=\"${lib3obj}\" \
+				  additional_flags=-DOBJ4=\"${lib4obj}\" \
+								]] != "" } {
+    untested "failed to compile"
+} else {
+    foreach_with_prefix force_internal_tls $internal_tls_iters {
+	do_tests $force_internal_tls
+    }
+}