summaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp
blob: adb4e0a4c21f2f5de9ef3acb22045d4d899de09f (plain)
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
# 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; therefore,
# the source file for the library / shared object must be listed first
# (in the standard_testfile invocation) since ${srcfile} is used by
# get_func_info (for determining the start, end, and length of a
# function).
#
# The output of Dwarf::assemble will be placed in $lib_basename.S
# which will be ${srcfile3} after the execution of standard_testfile.

standard_testfile $lib_basename.c $main_basename.c $lib_basename.S

set libsrc "${::srcdir}/${::subdir}/${::srcfile}"
set lib_so [standard_output_file ${lib_basename}.so]
set asm_file [standard_output_file ${::srcfile3}]

# We need to know the size of some types in order to write some of the
# debugging info that we're about to generate.  For that, we ask GDB
# by debugging the shared object associated with this test case.

# Compile the shared library: -DIS_SHAREDLIB prevents main() from
# being defined.  Note that debugging symbols will be present for
# this compilation.
if {[gdb_compile_shlib $libsrc $lib_so \
                       {additional_flags=-DIS_SHAREDLIB debug}] != ""} {
    untested "failed to compile shared library"
    return
}

# Start a fresh GDB and load the shared library.
clean_restart $lib_so

# 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]

if { $long_size == -1 || $addr_size == -1 \
     || $struct_A_size == -1 || $struct_B_size == -1} {
    perror "Can't determine type sizes"
    return
}

# 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]

# Create the DWARF.
Dwarf::assemble ${asm_file} {
    declare_labels L

    # Find start, end, and length of functions foo and bar.
    # These calls to get_func_info will create and set variables
    # foo_start, bar_start, foo_end, bar_end, foo_len, and
    # bar_len.
    #
    # In order to get the right answers, get_func_info (and,
    # underneath, function_range) should use the same compiler flags
    # as those used to make a shared object.  For any targets that get
    # this far, -fpic is probably correct.
    #
    # Also, it should be noted that IS_SHAREDLIB is NOT defined as one
    # of the additional flags.  Not defining IS_SHAREDLIB will cause a
    # main() to be defined for the compilation of the shared library
    # source file which happens as a result of using get_func_info;
    # this is currently required in order to this facility.
    set flags {additional_flags=-fpic debug}
    get_func_info foo $flags
    get_func_info bar $flags

    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 "${::srcfile}" 1

	# Generate a line table program.
	program {
	    DW_LNE_set_address $foo_start
	    line [gdb_get_line_number "foo prologue"]
	    DW_LNS_copy
	    DW_LNE_set_address foo_label
	    line [gdb_get_line_number "foo return"]
	    DW_LNS_copy
	    line [gdb_get_line_number "foo end"]
	    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"]
	    DW_LNS_copy
	    DW_LNE_set_address bar_label
	    line [gdb_get_line_number "bar return"]
	    DW_LNS_copy
	    line [gdb_get_line_number "bar end"]
	    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 that (again)
# -DIS_SHAREDLIB is used to prevent inclusion of main() in the shared
# object.  Also 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 \
                       {additional_flags=-DIS_SHAREDLIB nodebug}] != ""} {
    untested "failed to compile shared library"
    return
}

# Compile the main program for use with the shared object.
if [prepare_for_testing "failed to prepare" ${testfile} \
                        ${::srcfile2} [list debug shlib=$lib_so]] {
    return -1
}

# Do whatever is necessary to make sure that the shared library is
# loaded for remote targets.
gdb_load_shlib ${lib_so}

if ![runto_main] then {
    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"

# 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