| # Copyright 2024-2025 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 |
| gdb_load $::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|find address of TLS symbol) `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 |
| gdb_load $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 find address of TLS symbol `tls_tbss_1' without registers" |
| } |
| } |
| } |
| |
| # Finish test early if no core file was made. |
| if {!$core_supported} { |
| return |
| } |
| |
| clean_restart |
| gdb_load $::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 |
| } |
| } |
| } |