[v2,08/11] New test - gdb.base/tls-nothreads.exp

Message ID 20241012024220.101084-9-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. 12, 2024, 2:32 a.m. UTC
  This commit introduces a new test, gdb.base/tls-nothreads.exp.

It has a test case, a C file, which has several TLS variables in the
main program, which, once compiled and linked, should end up (in ELF
files) in .tdata and .tbss.  The test compiles the program in a number
of different ways, making sure that each variable is accessible
regardless of how it was compiled.

Note that some of the compilation scenarios end up with a statically
linked executable.  Prior to this series of commits, accessing TLS
variables from a statically linked program on Linux did not work.
For certain targets (x86_64, aarch64, s390x, riscv, and ppc64),
all on Linux, support has been added to GDB for accessing thread
local storage in statically linked executables.  This test is
important for testing those build scenarios.

But it's also important to make sure that GDB's internal TLS support
works for other scenarios too.  In order to accomplish that, the
tests are also run in a mode which forces the internal support to
be used.

It also adds a new file, gdb.base/tls-common.exp.tcl, which includes
some common definitions used by the three new TLS tests, including
the one added by this commit.  In particular, it sets a TCL variable,
'internal_tls_linux_targets' which list the targets mentioned earlier.
This means that as internal TLS support is added for other targets,
the target should be listed in just one file as opposed to three
(or more if other tests using tls-common.exp.tcl are added).
---
 gdb/testsuite/gdb.base/tls-common.exp.tcl |  50 +++++
 gdb/testsuite/gdb.base/tls-nothreads.c    |  57 +++++
 gdb/testsuite/gdb.base/tls-nothreads.exp  | 248 ++++++++++++++++++++++
 3 files changed, 355 insertions(+)
 create mode 100644 gdb/testsuite/gdb.base/tls-common.exp.tcl
 create mode 100644 gdb/testsuite/gdb.base/tls-nothreads.c
 create mode 100644 gdb/testsuite/gdb.base/tls-nothreads.exp
  

Patch

diff --git a/gdb/testsuite/gdb.base/tls-common.exp.tcl b/gdb/testsuite/gdb.base/tls-common.exp.tcl
new file mode 100644
index 00000000000..7aa7f466aa1
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-common.exp.tcl
@@ -0,0 +1,50 @@ 
+# 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.
+
+# Require statement, variables and procs used by tls-nothreads.exp,
+# tls-multiobj.exp, and tls-dlobj.exp.
+
+# The tests listed above are known to work for the targets listed on
+# the 'require' line, below.
+#
+# At the moment, only the Linux target is listed, but, ideally, these
+# tests should be run on other targets too.  E.g, testing on FreeBSD
+# shows many failures which should be addressed in some fashion before
+# enabling it for that target.
+
+require {is_any_target "*-*-linux*"}
+
+# These are the targets which have support for internal TLS lookup:
+
+set internal_tls_linux_targets {"x86_64-*-linux*" "aarch64-*-linux*"
+				"riscv*-*-linux*" "powerpc64*-*-linux*"
+				"s390x*-*-linux*"}
+
+# The "maint set force-internal-tls-address-lookup" command is only
+# available for certain Linux architectures.  Don't attempt to force
+# use of internal TLS support for architectures which don't support
+# it.
+
+if [is_any_target {*}$internal_tls_linux_targets] {
+    set internal_tls_iters { false true }
+} else {
+    set internal_tls_iters { false }
+}
+
+# Set up a kfail with message KFAIL_MSG when KFAIL_COND holds, then
+# issue gdb_test with command CMD and regular expression RE.
+
+proc gdb_test_with_kfail {cmd re kfail_cond kfail_msg} {
+    if [uplevel 1 [list expr $kfail_cond]] {
+	setup_kfail $kfail_msg *-*-*
+    }
+    gdb_test $cmd $re
+}
diff --git a/gdb/testsuite/gdb.base/tls-nothreads.c b/gdb/testsuite/gdb.base/tls-nothreads.c
new file mode 100644
index 00000000000..b3aaa3383e7
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-nothreads.c
@@ -0,0 +1,57 @@ 
+/* 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/>.  */
+
+__thread int tls_tbss_1;
+__thread int tls_tbss_2;
+__thread int tls_tbss_3;
+
+__thread int tls_tdata_1 = 21;
+__thread int tls_tdata_2 = 22;
+__thread int tls_tdata_3 = 23;
+
+volatile int data;
+
+void
+use_it (int a)
+{
+  data = a;
+}
+
+int
+main (int argc, char **argv)
+{
+  use_it (-1);
+
+  tls_tbss_1 = 24;	/* main-breakpoint-1 */
+  tls_tbss_2 = 25;
+  tls_tbss_3 = 26;
+
+  tls_tdata_1 = 42;
+  tls_tdata_2 = 43;
+  tls_tdata_3 = 44;
+
+  use_it (tls_tbss_1);
+  use_it (tls_tbss_2);
+  use_it (tls_tbss_3);
+  use_it (tls_tdata_1);
+  use_it (tls_tdata_2);
+  use_it (tls_tdata_3);
+
+  use_it (100);		/* main-breakpoint-2 */
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/tls-nothreads.exp b/gdb/testsuite/gdb.base/tls-nothreads.exp
new file mode 100644
index 00000000000..d235d1a2ab5
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-nothreads.exp
@@ -0,0 +1,248 @@ 
+# 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.
+
+# Using different compilation/linking scenarios, attempt to access
+# thread-local variables in a non-threaded program.  Also test that
+# GDB internal TLS lookup works correctly.
+
+source $srcdir/$subdir/tls-common.exp.tcl
+
+standard_testfile
+
+proc do_tests {force_internal_tls {do_kfail_tls_access 0}} {
+    clean_restart $::binfile
+    if ![runto_main] {
+	return
+    }
+
+    if $force_internal_tls {
+	gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+    }
+
+    if { $do_kfail_tls_access && [istarget "*-*-linux*"] } {
+	# Turn off do_kfail_tls_access when libthread_db is loaded. 
+	# This can happen for the default case when testing x86_64
+	# w/ -m32 using glibc versions 2.34 or newer.
+	gdb_test_multiple "maint check libthread-db" "Check for loaded libthread_db" {
+	    -re -wrap "libthread_db integrity checks passed." {
+		set do_kfail_tls_access 0
+		pass $gdb_test_name
+	    }
+	    -re -wrap "No libthread_db loaded" {
+		pass $gdb_test_name
+	    }
+	}
+	# Also turn off do_kfail_tls_access when connected to a
+	# gdbserver and we observe that accessing a TLS variable
+	# works.
+	if [target_is_gdbserver] {
+	    gdb_test_multiple "print tls_tbss_1" "Check TLS accessibility when connected to a gdbserver" {
+		-re -wrap "= 0" {
+		    set do_kfail_tls_access 0
+		    pass $gdb_test_name
+		}
+		-re -wrap "Remote target failed to process qGetTLSAddr request" {
+		    pass $gdb_test_name
+		}
+	    }
+	}
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"]
+    gdb_continue_to_breakpoint "main-breakpoint-1"
+
+    set t $do_kfail_tls_access
+    set m "tls not available"
+    with_test_prefix "before assignments" {
+	gdb_test_with_kfail "print tls_tbss_1" ".* = 0" $t $m
+	gdb_test_with_kfail "print tls_tbss_2" ".* = 0" $t $m
+	gdb_test_with_kfail "print tls_tbss_3" ".* = 0" $t $m
+
+	gdb_test_with_kfail "print tls_tdata_1" ".* = 21" $t $m
+	gdb_test_with_kfail "print tls_tdata_2" ".* = 22" $t $m
+	gdb_test_with_kfail "print tls_tdata_3" ".* = 23" $t $m
+    }
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"]
+    gdb_continue_to_breakpoint "main-breakpoint-2"
+
+    with_test_prefix "after assignments" {
+	gdb_test_with_kfail "print tls_tbss_1" ".* = 24" $t $m
+	gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m
+	gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m
+
+	gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m
+	gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m
+	gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m
+    }
+
+    # Make a core file now, but save testing using it until the end
+    # in case core files are not supported.
+    set corefile ${::binfile}.core
+    set core_supported 0
+    if { ![is_remote host] } {
+	set core_supported [gdb_gcore_cmd $corefile "save corefile"]
+    }
+
+    # Now continue to end and see what happens when attempting to
+    # access a TLS variable when the program is no longer running.
+    gdb_continue_to_end
+    with_test_prefix "after exit" {
+	gdb_test "print tls_tbss_1" \
+	    "Cannot read `tls_tbss_1' without registers"
+    }
+
+    with_test_prefix "stripped" {
+	set binfile_stripped "${::binfile}.stripped"
+	set objcopy [gdb_find_objcopy]
+	set cmd "$objcopy --strip-debug ${::binfile} $binfile_stripped"
+	if ![catch "exec $cmd" cmd_output] {
+	    clean_restart $binfile_stripped
+	    if ![runto_main] {
+		return
+	    }
+
+	    if $force_internal_tls {
+		gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+	    }
+
+	    # While there are no debug (e.g.  DWARF) symbols, there
+	    # are minimal symbols, so we should be able to place a
+	    # breakpoint in use_it and continue to it.  Continuing
+	    # twice should put us past the assignments, at which point
+	    # we can see if the TLS variables are still accessible.
+	    gdb_test "break use_it" "Breakpoint 2 at $::hex"
+	    gdb_test "continue" "Breakpoint 2, $::hex in use_it.*"
+	    gdb_test "continue" "Breakpoint 2, $::hex in use_it.*" "continue 2"
+
+	    # Note that a cast has been added in order to avoid the
+	    # "...has unknown type; cast it to its declared type"
+	    # problem.
+	    gdb_test_with_kfail "print (int) tls_tbss_1" ".* = 24" $t $m
+	    gdb_test_with_kfail "print (int) tls_tbss_2" ".* = 25" $t $m
+	    gdb_test_with_kfail "print (int) tls_tbss_3" ".* = 26" $t $m
+
+	    gdb_test_with_kfail "print (int) tls_tdata_1" ".* = 42" $t $m
+	    gdb_test_with_kfail "print (int) tls_tdata_2" ".* = 43" $t $m
+	    gdb_test_with_kfail "print (int) tls_tdata_3" ".* = 44" $t $m
+
+	    # Get rid of the "use_it" breakpoint
+	    gdb_test_no_output "del 2"
+
+	    # Continue to program exit
+	    gdb_continue_to_end
+
+	    # TLS variables should not be accessible after program exit
+	    # (This case initially caused GDB to crash during development
+	    # of GDB-internal TLS lookup support.)
+	    with_test_prefix "after exit" {
+		gdb_test "print (int) tls_tbss_1" \
+		    "Cannot determine address of TLS symbol `tls_tbss_1' without registers"
+	    }
+	}
+    }
+
+    # 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_with_kfail "print tls_tbss_1" ".* = 24" $t $m
+	gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m
+	gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m
+
+	gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m
+	gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m
+	gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m
+    }
+}
+
+# Certain linux target architectures implement support for internal
+# TLS lookup which is used when thread stratum support (via
+# libthread_db) is missing or when the linux-only GDB maintenance
+# setting 'force-internal-tls-address-lookup' is 'on'.  Thus for some
+# of the testing scenarios, such as statically linked executables,
+# this internal support will be used.  Set 'do_kfail_tls_access' to 1
+# for those architectures which don't implement internal TLS support.
+if {[istarget *-*-linux*]
+    && ![is_any_target {*}$internal_tls_linux_targets]} {
+    set do_kfail_tls_access 1
+} elseif {[istarget *-*-linux*] && [is_x86_like_target]} {
+    # This covers the case of x86_64 with -m32:
+    set do_kfail_tls_access 1
+} else {
+    set do_kfail_tls_access 0
+}
+
+set binprefix $binfile
+
+with_test_prefix "default" {
+    set binfile $binprefix-default
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+	untested "failed to compile"
+    } else {
+	foreach_with_prefix force_internal_tls $internal_tls_iters {
+	    # Depending on glibc version, it might not be appropriate
+	    # for do_kfail_tls_access to be set here.  That will be
+	    # handled in 'do_tests', disabling it if necessary.
+	    # 
+	    # Specifically, glibc versions 2.34 and later have the
+	    # thread library (and libthread_db availability) in
+	    # programs not linked against libpthread.so
+	    do_tests $force_internal_tls $do_kfail_tls_access
+	}
+    }
+}
+
+with_test_prefix "static" {
+    set binfile $binprefix-static
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
+	untested "failed to compile"
+    } else {
+	foreach_with_prefix force_internal_tls $internal_tls_iters {
+	    do_tests $force_internal_tls $do_kfail_tls_access
+	}
+    }
+}
+
+with_test_prefix "pthreads" {
+    set binfile $binprefix-pthreads
+    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+	untested "failed to compile"
+    } else {
+	foreach_with_prefix force_internal_tls $internal_tls_iters {
+	    do_tests $force_internal_tls
+	}
+    }
+}
+
+with_test_prefix "pthreads-static" {
+    set binfile $binprefix-pthreads-static
+    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
+	untested "failed to compile"
+    } else {
+	foreach_with_prefix force_internal_tls $internal_tls_iters {
+	    do_tests $force_internal_tls $do_kfail_tls_access
+	}
+    }
+}