binutils-gdb/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp
Andrew Burgess f50c72da4d gdb/testsuite: remove use of then keyword from gdb.*/*.exp scripts
The canonical form of 'if' in modern TCL is 'if {} {}'.  But there's
still a bunch of places in the testsuite where we make use of the
'then' keyword, and sometimes these get copies into new tests, which
just spreads poor practice.

This commit removes all use of the 'then' keyword from the remaining
gdb.*/*.exp scripts.  Previous commits have done the bulk of this
removal, this commit just handles the remaining directories that each
contain a low number of instances.

There should be no changes in what is tested after this commit.
2022-11-28 21:04:10 +00:00

370 lines
12 KiB
Plaintext

# Copyright 2021-2022 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 test case uses the DWARF assembler to reproduce the problem
# described by PR28030. The bug turned out to be that
# FIELD_LOC_KIND_DWARF_BLOCK was not handled when recursively copying
# a value's type when preserving the value history during the freeing
# up of objfiles associated with a shared object. (Yes, figuring out
# how to make this happen in a concise test case turned out to be
# challenging.)
#
# The following elements proved to be necessary for reproducing the
# problem:
#
# 1) A location expression needed to be used with
# DW_AT_data_member_location rather than a simple offset.
# Moreover, this location expression needed to use opcodes
# which GDB's DWARF reader could not convert to a simple
# offset. (Note, however, that GDB could probably be improved
# to handle the opcodes chosen for this test; if decode_locdesc()
# in dwarf2/read.c is ever updated to handle both DW_OP_pick and
# DW_OP_drop, then this test could end up passing even if
# the bug it's intended to test has not been fixed.)
#
# 2) The debug info containing the above DWARF info needed
# to be associated with a shared object since the problem
# occurred while GDB was preserving values during the
# purging of shared objects.
#
# 3) After performing some simple gdb commands, the program is
# run again. In the course of running the objfile destructor
# associated with the shared object, values are preserved
# along with their types. As noted earlier, it was during
# the recursive type copy that the bug was observed.
#
# Therefore, due to #2 above, this test case creates debug info
# which is then used by a shared object.
# This test can't be run on targets lacking shared library support.
if [skip_shlib_tests] {
return 0
}
load_lib dwarf.exp
# This test can only be run on targets which support DWARF-2 and use gas.
if ![dwarf2_support] {
return 0
}
# gdb_test_file_name is the name of this file without the .exp
# extension. Use it to form basenames for the main program
# and shared object.
set main_basename ${::gdb_test_file_name}-main
set lib_basename ${::gdb_test_file_name}-lib
# We're generating DWARF assembly for the shared object;
# The output of Dwarf::assemble will be placed in $lib_basename.S
# which will be ${srcfile3} after the execution of standard_testfile.
standard_testfile $main_basename.c $lib_basename.c $lib_basename.S
set libsrc "${::srcdir}/${::subdir}/${::srcfile2}"
set lib_so [standard_output_file ${lib_basename}.so]
set asm_file [standard_output_file ${::srcfile3}]
# Compile the shared library for the first GDB session. Note that debugging
# symbols will be present for this compilation, because we want to print some
# type information.
if {[gdb_compile_shlib $libsrc $lib_so \
{debug}] != ""} {
untested "failed to compile shared library"
return
}
# Compile the main program for use with the shared object. Note we're using
# debug, such that "finish out of foo" prints:
# Value returned is $1 = (class B *) $hex <g_>
# instead of:
# Value returned is $1 = (B *) $hex <g_>
# Note that this compilation is used for all GDB sessions.
set exec_options [list debug shlib=$lib_so]
if [prepare_for_testing "failed to prepare" ${testfile} \
${::srcfile} $exec_options] {
return -1
}
### First GDB session.
with_test_prefix "first session" {
# Do whatever is necessary to make sure that the shared library is
# loaded for remote targets.
gdb_load_shlib ${lib_so}
# Run to foo to make sure foo refers to the function, and not foo@PLT.
if {![runto foo qualified]} {
return
}
with_shared_gdb {
set session_options $exec_options
# Rather than start a new session, declare the current session the
# shared one. Otherwise, get_func_info would compile an executable
# in a temp dir, which means -Wl,-rpath,\\\$ORIGIN no longer finds
# the shared lib.
share_gdb ${srcdir}/${subdir}/$srcfile $session_options
get_func_info foo $session_options
get_func_info bar $session_options
# Using our running GDB session, determine sizes of several types.
set long_size [get_sizeof "long" -1]
set addr_size [get_sizeof "void *" -1]
set struct_A_size [get_sizeof "g_A" -1]
set struct_B_size [get_sizeof "g_B" -1]
# Retrieve struct offset of MBR in struct TP
proc get_offsetof { tp mbr } {
return [get_integer_valueof "&((${tp} *) 0)->${mbr}" -1]
}
# Use running GDB session to get struct offsets
set A_a [get_offsetof A a]
set A_x [get_offsetof A x]
set B_a [get_offsetof B a]
set B_b [get_offsetof B b]
set B_x2 [get_offsetof B x2]
}
}
if { $long_size == -1 || $addr_size == -1 \
|| $struct_A_size == -1 || $struct_B_size == -1} {
perror "Can't determine type sizes"
return
}
# Create the DWARF.
Dwarf::assemble ${asm_file} {
declare_labels L
global foo_start foo_end
global bar_start bar_end
global libsrc
cu { label cu_label } {
DW_TAG_compile_unit {
{DW_AT_language @DW_LANG_C_plus_plus}
{name ${::srcfile}}
{stmt_list $L DW_FORM_sec_offset}
} {
declare_labels int_label class_A_label class_B_label \
B_ptr_label
int_label: DW_TAG_base_type {
{DW_AT_byte_size ${::long_size} DW_FORM_udata}
{DW_AT_encoding @DW_ATE_signed}
{DW_AT_name "int"}
}
class_A_label: DW_TAG_class_type {
{DW_AT_name "A"}
{DW_AT_byte_size ${::struct_A_size} DW_FORM_sdata}
} {
DW_TAG_member {
{DW_AT_name "a"}
{DW_AT_type :$int_label}
{DW_AT_data_member_location ${::A_a} DW_FORM_udata}
}
DW_TAG_member {
{DW_AT_name "x"}
{DW_AT_type :$int_label}
{DW_AT_data_member_location ${::A_x} DW_FORM_udata}
}
}
class_B_label: DW_TAG_class_type {
{DW_AT_name "B"}
{DW_AT_byte_size ${::struct_B_size} DW_FORM_sdata}
} {
# While there are easier / better ways to specify an
# offset used by DW_AT_data_member_location than that
# used below, we need a location expression here in
# order to reproduce the bug. Moreover, this location
# expression needs to use opcodes that aren't handled
# by decode_locdesc() in dwarf2/read.c; if we use
# opcodes that _are_ handled by that function, the
# location expression will be converted into a simple
# offset - which will then (again) not reproduce the
# bug. At the time that this test was written,
# neither DW_OP_pick nor DW_OP_drop were being handled
# by decode_locdesc(); this is why those opcodes were
# chosen.
DW_TAG_inheritance {
{DW_AT_type :$class_A_label}
{DW_AT_data_member_location {
DW_OP_constu ${::B_a}
DW_OP_plus
DW_OP_pick 0
DW_OP_drop} SPECIAL_expr}
{DW_AT_accessibility 1 DW_FORM_data1}
}
DW_TAG_member {
{DW_AT_name "b"}
{DW_AT_type :$int_label}
{DW_AT_data_member_location ${::B_b} DW_FORM_udata}
}
DW_TAG_member {
{DW_AT_name "x2"}
{DW_AT_type :$int_label}
{DW_AT_data_member_location ${::B_x2} DW_FORM_udata}
}
}
B_ptr_label: DW_TAG_pointer_type {
{DW_AT_type :$class_B_label}
{DW_AT_byte_size ${::addr_size} DW_FORM_sdata}
}
DW_TAG_variable {
{DW_AT_name "g_A"}
{DW_AT_type :$class_A_label}
{DW_AT_external 1 flag}
{DW_AT_location {DW_OP_addr [gdb_target_symbol "g_A"]} \
SPECIAL_expr}
}
DW_TAG_variable {
{DW_AT_name "g_B"}
{DW_AT_type :$class_B_label}
{DW_AT_external 1 flag}
{DW_AT_location {DW_OP_addr [gdb_target_symbol "g_B"]} \
SPECIAL_expr}
}
# We can't use MACRO_AT for the definitions of foo and bar
# because it doesn't provide a way to pass the appropriate
# flags. Therefore, we list the name, low_pc, and high_pc
# explicitly.
DW_TAG_subprogram {
{DW_AT_name foo}
{DW_AT_low_pc $foo_start DW_FORM_addr}
{DW_AT_high_pc $foo_end DW_FORM_addr}
{DW_AT_type :${B_ptr_label}}
{DW_AT_external 1 flag}
}
DW_TAG_subprogram {
{DW_AT_name bar}
{DW_AT_low_pc $bar_start DW_FORM_addr}
{DW_AT_high_pc $bar_end DW_FORM_addr}
{DW_AT_type :${B_ptr_label}}
{DW_AT_external 1 flag}
} {
DW_TAG_formal_parameter {
{DW_AT_name v}
{DW_AT_type :${B_ptr_label}}
}
}
}
}
lines {version 2} L {
include_dir "${::srcdir}/${::subdir}"
file_name "${::srcfile2}" 1
# Generate a line table program.
program {
DW_LNE_set_address $foo_start
line [gdb_get_line_number "foo prologue" $libsrc]
DW_LNS_copy
DW_LNE_set_address foo_label
line [gdb_get_line_number "foo return" $libsrc]
DW_LNS_copy
line [gdb_get_line_number "foo end" $libsrc]
DW_LNS_copy
DW_LNE_set_address $foo_end
DW_LNS_advance_line 1
DW_LNS_copy
DW_LNE_end_sequence
DW_LNE_set_address $bar_start
line [gdb_get_line_number "bar prologue" $libsrc]
DW_LNS_copy
DW_LNE_set_address bar_label
line [gdb_get_line_number "bar return" $libsrc]
DW_LNS_copy
line [gdb_get_line_number "bar end" $libsrc]
DW_LNS_copy
DW_LNE_set_address $bar_end
DW_LNS_advance_line 1
DW_LNS_copy
DW_LNE_end_sequence
}
}
aranges {} cu_label {
# This 0,0 entry tests that the .debug_aranges reader can
# handle an apparent terminator before the end of the ranges.
arange {} 0 0
arange {} $foo_start $foo_end
arange {} $bar_start $bar_end
}
}
# Compile the shared object again, but this time include / use the
# DWARF info that we've created above. Note the use of the "nodebug" option.
# Any debugging information that we need will be provided by the DWARF info
# created above.
if {[gdb_compile_shlib [list $libsrc $asm_file] $lib_so \
{nodebug}] != ""} {
untested "failed to compile shared library"
return
}
### Second GDB session.
with_test_prefix "second session" {
clean_restart $binfile
# Again, do whatever is necessary to make sure that the shared library is
# loaded for remote targets.
gdb_load_shlib ${lib_so}
if {![runto_main]} {
return
}
# Step into foo so that we can finish out of it.
gdb_test "step" "foo .. at .* foo end.*" "step into foo"
# Finishing out of foo will create a value that will later need to
# be preserved when restarting the program.
gdb_test "finish" "= \\(class B \\*\\) ${::hex} .*" "finish out of foo"
# Dereferencing and printing the return value isn't necessary
# for reproducing the bug, but we should make sure that the
# return value is what we expect it to be.
gdb_test "p *$" { = {<A> = {a = 8, x = 9}, b = 10, x2 = 11}} \
"dereference return value"
# The original PR28030 reproducer stepped back into the shared object,
# so we'll do the same here:
gdb_test "step" "bar \\(.*" "step into bar"
}
### Third GDB session.
with_test_prefix "third session" {
# We don't want a clean restart here since that will be too clean.
# The original reproducer for PR28030 set a breakpoint in the shared
# library and then restarted via "run". The command below does roughly
# the same thing. It's at this step that an internal error would
# occur for PR28030. The "message" argument tells runto to turn on
# the printing of PASSes while runto is doing its job.
runto "bar" message
}