1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
|
# Copyright 2021-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/>.
# 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.
require allow_shlib_tests
load_lib dwarf.exp
# This test can only be run on targets which support DWARF-2 and use gas.
require dwarf2_support
# 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
include_file locexpr-data-member-location.h
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
DW_LNS_advance_pc 0
line [gdb_get_line_number "foo end" $libsrc]
DW_LNS_copy
DW_LNE_set_address $foo_end
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
DW_LNS_advance_pc 0
line [gdb_get_line_number "bar end" $libsrc]
DW_LNS_copy
DW_LNE_set_address $bar_end
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
}
|