mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
317d2668d0
Consider a test-case consisting of source file test.c: ... extern int aaa; int main (void) { return 0; } ... and test-2.c: ... int aaa = 33; ... compiled with debug info only for test.c: ... $ gcc -c test.c -g; gcc -c test2.c; gcc test.o test2.o -g ... When trying to print aaa, we get: ... $ gdb -batch a.out -ex "print aaa" 'aaa' has unknown type; cast it to its declared type ... but with -readnow we have: ... $ gdb -readnow -batch a.out -ex "print aaa" $1 = 33 ... In the -readnow case, the symbol for aaa in the full symtab has LOC_UNRESOLVED, and the symbol type is combined with the minimal symbol address, to read the value and print it without cast. Without the -readnow, we create partial symbols, but the aaa decl is missing from the partial symtabs, so we find it only in the minimal symbols, resulting in the cast request. If the aaa decl would have been in the partial symtabs, it would have been found, and the full symtab would have been expanded, after which things would be as with -readnow. The function add_partial_symbol has a comment on the LOC_UNRESOLVED + minimal symbol addres construct at DW_TAG_variable handling: ... else if (pdi->is_external) { /* Global Variable. Don't enter into the minimal symbol tables as there is a minimal symbol table entry from the ELF symbols already. Enter into partial symbol table if it has a location descriptor or a type. If the location descriptor is missing, new_symbol will create a LOC_UNRESOLVED symbol, the address of the variable will then be determined from the minimal symbol table whenever the variable is referenced. ... but it's not triggered due to this test in scan_partial_symbols: ... case DW_TAG_variable: ... if (!pdi->is_declaration) { add_partial_symbol (pdi, cu); } ... Fix this in scan_partial_symbols by allowing external variable decls to be added to the partial symtabs. Build and reg-tested on x86_64-linux. The patch caused this regression: ... (gdb) print a_thread_local^M Cannot find thread-local storage for process 0, executable file tls/tls:^M Cannot find thread-local variables on this target^M (gdb) FAIL: gdb.threads/tls.exp: print a_thread_local ... while without the patch we have: ... (gdb) print a_thread_local^M Cannot read `a_thread_local' without registers^M (gdb) PASS: gdb.threads/tls.exp: print a_thread_local ... However, without the patch but with -readnow we have the same FAIL as with the patch (filed as PR25807). In other words, the patch has the effect that we get the same result with and without -readnow. This can be explained as follows. Without the patch, and without -readnow, we have two a_thread_locals, the def and the decl: ... $ gdb -batch outputs/gdb.threads/tls/tls \ -ex "maint expand-symtabs" \ -ex "print a_thread_local" \ -ex "maint print symbols" \ | grep "a_thread_local;" Cannot read `a_thread_local' without registers int a_thread_local; computed at runtime int a_thread_local; unresolved ... while without the patch and with -readnow, we have the opposite order: ... $ gdb -readnow -batch outputs/gdb.threads/tls/tls \ -ex "maint expand-symtabs" \ -ex "print a_thread_local" \ -ex "maint print symbols" \ | grep "a_thread_local;" Cannot find thread-local storage for process 0, executable file tls/tls: Cannot find thread-local variables on this target int a_thread_local; unresolved int a_thread_local; computed at runtime ... With the patch we have the same order with and without -readnow, but just a different one than before without -readnow. Mark the "Cannot find thread-local variables on this target" variant a PR25807 kfail. gdb/ChangeLog: 2020-04-22 Tom de Vries <tdevries@suse.de> PR symtab/25764 * dwarf2/read.c (scan_partial_symbols): Allow external variable decls in psymtabs. gdb/testsuite/ChangeLog: 2020-04-22 Tom de Vries <tdevries@suse.de> PR symtab/25764 * gdb.base/psym-external-decl-2.c: New test. * gdb.base/psym-external-decl.c: New test. * gdb.base/psym-external-decl.exp: New file. * gdb.threads/tls.exp: Add PR25807 kfail.
322 lines
9.7 KiB
Plaintext
322 lines
9.7 KiB
Plaintext
# tls.exp -- Expect script to test thread-local storage
|
|
# Copyright (C) 1992-2020 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/>. */
|
|
|
|
load_lib gdb-python.exp
|
|
|
|
standard_testfile tls.c tls2.c
|
|
|
|
if [istarget "*-*-linux"] then {
|
|
set target_cflags "-D_MIT_POSIX_THREADS"
|
|
} else {
|
|
set target_cflags ""
|
|
}
|
|
|
|
if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile} ${srcdir}/${subdir}/${srcfile2}" "${binfile}" executable [list c++ debug]] != "" } {
|
|
return -1
|
|
}
|
|
|
|
### Compute the value of the a_thread_local variable.
|
|
proc compute_expected_value {value} {
|
|
set expected_value 0
|
|
set i 0
|
|
while { $i <= $value} {
|
|
incr expected_value $i
|
|
incr i
|
|
}
|
|
return $expected_value
|
|
}
|
|
|
|
### Get the value of the variable 'me' for the current thread.
|
|
proc get_me_variable {tnum} {
|
|
global expect_out
|
|
global gdb_prompt
|
|
global decimal
|
|
|
|
set value_of_me -1
|
|
send_gdb "print me\n"
|
|
gdb_expect {
|
|
-re ".*= ($decimal).*\r\n$gdb_prompt $" {
|
|
set value_of_me $expect_out(1,string)
|
|
pass "$tnum thread print me"
|
|
}
|
|
-re "$gdb_prompt $" {
|
|
fail "$tnum thread print me"
|
|
}
|
|
timeout {
|
|
fail "$tnum thread print me (timeout)"
|
|
}
|
|
}
|
|
return ${value_of_me}
|
|
}
|
|
|
|
### Check the values of the thread local variables in the thread.
|
|
### Also check that info address print the right things.
|
|
proc check_thread_local {number} {
|
|
set me_variable [get_me_variable $number]
|
|
set expected_value [compute_expected_value ${me_variable}]
|
|
|
|
gdb_test "p a_thread_local" \
|
|
"= $expected_value" \
|
|
"${number} thread local storage"
|
|
|
|
if {![skip_python_tests]} {
|
|
gdb_test_no_output \
|
|
"python sym = gdb.lookup_symbol('a_thread_local')\[0\]" \
|
|
"${number} look up a_thread_local symbol"
|
|
# We intentionally do not pass a frame to "value" here. If a
|
|
# TLS variable requires a frame, this will fail. However, if
|
|
# it does not require a frame, then it will succeed.
|
|
gdb_test "python print(sym.value())" "$expected_value" \
|
|
"${number} get symbol value without frame"
|
|
}
|
|
|
|
gdb_test "p K::another_thread_local" \
|
|
"= $me_variable" \
|
|
"${number} another thread local storage"
|
|
|
|
gdb_test "info address a_thread_local" \
|
|
".*a_thread_local.*a thread-local variable at offset.*" \
|
|
"${number} info address a_thread_local"
|
|
|
|
gdb_test "info address K::another_thread_local" \
|
|
".*another_thread_local.*a thread-local variable at offset.*" \
|
|
"${number} info address another_thread_local"
|
|
}
|
|
|
|
### Select a particular thread.
|
|
proc select_thread {thread} {
|
|
global gdb_prompt
|
|
|
|
send_gdb "thread $thread\n"
|
|
gdb_expect {
|
|
-re "\\\[Switching to thread .*\\\].*\r\n$gdb_prompt $" {
|
|
pass "selected thread: $thread"
|
|
}
|
|
-re "$gdb_prompt $" {
|
|
fail "selected thread: $thread"
|
|
}
|
|
timeout {
|
|
fail "selected thread: $thread (timeout)"
|
|
}
|
|
}
|
|
}
|
|
|
|
### Do a backtrace for the current thread, and check that the 'spin' routine
|
|
### is in it. This means we have one of the threads we created, rather
|
|
### than the main thread. Record the thread in the spin_threads
|
|
### array. Also remember the level of the 'spin' routine in the backtrace, for
|
|
### later use.
|
|
proc check_thread_stack {number spin_threads spin_threads_level} {
|
|
global gdb_prompt
|
|
global expect_out
|
|
global decimal
|
|
global hex
|
|
upvar $spin_threads tarr
|
|
upvar $spin_threads_level tarrl
|
|
|
|
select_thread $number
|
|
send_gdb "where\n"
|
|
gdb_expect {
|
|
-re ".*(\[0-9\]+)\[ \t\]+$hex in spin \\(vp=(0x\[0-9a-f\]+).*\r\n$gdb_prompt $" {
|
|
if {[info exists tarr($number)]} {
|
|
fail "backtrace of thread number $number in spin"
|
|
} else {
|
|
pass "backtrace of thread number $number in spin"
|
|
set level $expect_out(1,string)
|
|
set tarrl($number) $level
|
|
set tarr($number) 1
|
|
}
|
|
}
|
|
-re ".*$gdb_prompt $" {
|
|
set tarr($number) 0
|
|
set tarrl($number) 0
|
|
pass "backtrace of thread number $number not relevant"
|
|
}
|
|
timeout {
|
|
fail "backtrace of thread number $number (timeout)"
|
|
}
|
|
}
|
|
}
|
|
|
|
clean_restart ${binfile}
|
|
|
|
gdb_test_multiple "print a_thread_local" "" {
|
|
-re -wrap "Cannot find thread-local variables on this target" {
|
|
kfail "gdb/25807" $gdb_test_name
|
|
}
|
|
-re -wrap "Cannot read .a_thread_local. without registers" {
|
|
pass $gdb_test_name
|
|
}
|
|
}
|
|
|
|
if ![runto_main] then {
|
|
fail "can't run to main"
|
|
return 0
|
|
}
|
|
|
|
# Set a breakpoint at the "spin" routine to
|
|
# test the thread local's value.
|
|
#
|
|
gdb_test "b [gdb_get_line_number "here we know tls value"]" \
|
|
".*Breakpoint 2.*tls.*" "set breakpoint at all threads"
|
|
|
|
# Set a bp at a point where we know all threads are alive.
|
|
#
|
|
gdb_test "b [gdb_get_line_number "still alive"]" \
|
|
".*Breakpoint 3.*tls.*" "set breakpoint at synch point"
|
|
|
|
# Set a bp at the end to see if all threads are finished.
|
|
#
|
|
gdb_test "b [gdb_get_line_number "before exit"]" \
|
|
".*Breakpoint 4.*tls.*" "set breakpoint at exit"
|
|
|
|
send_gdb "continue\n"
|
|
gdb_expect {
|
|
-re ".* received signal SIGSEGV.*a_thread_local = 0;.*$gdb_prompt $" {
|
|
# This is the first symptom if the gcc and binutils versions
|
|
# in use support TLS, but the system glibc does not.
|
|
unsupported "continue to first thread: system does not support TLS"
|
|
return -1
|
|
}
|
|
-re ".*$inferior_exited_re normally.*$gdb_prompt $" {
|
|
fail "continue to first thread: program runaway"
|
|
}
|
|
-re ".*Pass 0 done.*Pass 1 done.*$gdb_prompt $" {
|
|
fail "continue to first thread: program runaway 2"
|
|
}
|
|
-re ".*Breakpoint 2.*tls value.*$gdb_prompt $" {
|
|
pass "continue to first thread: get to thread"
|
|
}
|
|
-re ".*$gdb_prompt $" {
|
|
fail "continue to first thread: no progress?"
|
|
}
|
|
timeout { fail "continue to first thread (timeout)" }
|
|
}
|
|
|
|
gdb_test "info thread" ".*Thread.*spin.*" \
|
|
"at least one th in spin while stopped at first th"
|
|
|
|
check_thread_local "first"
|
|
|
|
gdb_test "continue" ".*Breakpoint 2.*tls value.*" "continue to second thread"
|
|
gdb_test "info thread" "Thread.*spin.*" \
|
|
"at least one th in spin while stopped at second th"
|
|
|
|
check_thread_local "second"
|
|
|
|
gdb_test "continue" ".*Breakpoint 2.*tls value.*" "continue to third thread"
|
|
gdb_test "info thread" ".*Thread.*spin.*" \
|
|
"at least one th in spin while stopped at third th"
|
|
|
|
check_thread_local "third"
|
|
|
|
gdb_test "continue" ".*Breakpoint 3.*still alive.*" "continue to synch point"
|
|
|
|
set no_of_threads 0
|
|
send_gdb "info thread\n"
|
|
gdb_expect {
|
|
-re "^info thread\[ \t\r\n\]+ *Id .*Frame\[ \t\r\n\]+.*(\[0-9\]+) *Thread\[^\r\n\]+\r\n$gdb_prompt $" {
|
|
set no_of_threads $expect_out(1,string)
|
|
pass "get number of threads"
|
|
}
|
|
-re "$gdb_prompt $" {
|
|
fail "get number of threads"
|
|
}
|
|
timeout {
|
|
fail "get number of threads (timeout)"
|
|
}
|
|
}
|
|
|
|
array set spin_threads {}
|
|
unset spin_threads
|
|
array set spin_threads_level {}
|
|
unset spin_threads_level
|
|
|
|
# For each thread check its backtrace to see if it is stopped at the
|
|
# spin routine.
|
|
for {set i 1} {$i <= $no_of_threads} {incr i} {
|
|
check_thread_stack $i spin_threads spin_threads_level
|
|
}
|
|
|
|
### Loop through the threads and check the values of the tls variables.
|
|
### keep track of how many threads we find in the spin routine.
|
|
set thrs_in_spin 0
|
|
foreach i [array names spin_threads] {
|
|
if {$spin_threads($i) == 1} {
|
|
incr thrs_in_spin
|
|
select_thread $i
|
|
set level $spin_threads_level($i)
|
|
# We expect to be in sem_wait, but if the thread has not yet
|
|
# been scheduled, we might be in sem_post still. We could be at
|
|
# any intermediate point in spin, too, but that is much less
|
|
# likely.
|
|
gdb_test "up $level" ".*spin.*sem_(wait|post).*" "thread $i up"
|
|
check_thread_local $i
|
|
}
|
|
}
|
|
|
|
if {$thrs_in_spin == 0} {
|
|
fail "no thread backtrace reported spin (vsyscall kernel problem?)"
|
|
}
|
|
|
|
gdb_test "continue" ".*Breakpoint 4.*before exit.*" "threads exited"
|
|
|
|
send_gdb "info thread\n"
|
|
gdb_expect {
|
|
-re ".* 1 *Thread.*2 *Thread.*$gdb_prompt $" {
|
|
fail "too many threads left at end"
|
|
}
|
|
-re ".*\\\* 1 *Thread.*main.*$gdb_prompt $" {
|
|
pass "expect only base thread at end"
|
|
}
|
|
-re ".*No stack.*$gdb_prompt $" {
|
|
fail "runaway at end"
|
|
}
|
|
-re ".*$gdb_prompt $" {
|
|
fail "mess at end"
|
|
}
|
|
timeout { fail "at end (timeout)" }
|
|
}
|
|
|
|
# Start over and do some "info address" stuff
|
|
#
|
|
runto spin
|
|
|
|
gdb_test "info address a_global" \
|
|
".*a_global.*static storage at address.*"
|
|
|
|
gdb_test "info address me" ".*me.*is a (complex DWARF expression:|variable).*"
|
|
|
|
|
|
# Test LOC_UNRESOLVED references resolving for `extern' TLS variables.
|
|
|
|
gdb_test "p a_thread_local" " = \[0-9\]+"
|
|
# Here it could crash with: Cannot access memory at address 0x0
|
|
gdb_test "p file2_thread_local" " = \[0-9\]+"
|
|
# Depending on the current lookup scope we get LOC_UNRESOLVED or LOC_COMPUTED
|
|
# both printing:
|
|
# Symbol "file2_thread_local" is a thread-local variable at offset 8 in the thread-local storage for `.../gdb.threads/tls'.
|
|
gdb_test "info address file2_thread_local" "Symbol \"file2_thread_local\" is a thread-local variable.*"
|
|
# Here it could also crash with: Cannot access memory at address 0x0
|
|
gdb_test "p a_thread_local" " = \[0-9\]+" "p a_thread_local second time"
|
|
gdb_test "info address a_thread_local" "Symbol \"a_thread_local\" is a thread-local variable.*"
|
|
|
|
# Done!
|
|
#
|
|
gdb_exit
|
|
|
|
return 0
|