blob: fe3ce8098df57011b3e3c183b49a37ad10945d3a [file] [log] [blame]
# 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
}
}
}