]>
Commit | Line | Data |
---|---|---|
4a94e368 | 1 | # Copyright 2021-2022 Free Software Foundation, Inc. |
a640adf7 KB |
2 | |
3 | # This program is free software; you can redistribute it and/or modify | |
4 | # it under the terms of the GNU General Public License as published by | |
5 | # the Free Software Foundation; either version 3 of the License, or | |
6 | # (at your option) any later version. | |
7 | # | |
8 | # This program is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License | |
14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | ||
16 | # This test case uses the DWARF assembler to reproduce the problem | |
17 | # described by PR28030. The bug turned out to be that | |
18 | # FIELD_LOC_KIND_DWARF_BLOCK was not handled when recursively copying | |
19 | # a value's type when preserving the value history during the freeing | |
20 | # up of objfiles associated with a shared object. (Yes, figuring out | |
21 | # how to make this happen in a concise test case turned out to be | |
22 | # challenging.) | |
23 | # | |
24 | # The following elements proved to be necessary for reproducing the | |
25 | # problem: | |
26 | # | |
27 | # 1) A location expression needed to be used with | |
28 | # DW_AT_data_member_location rather than a simple offset. | |
29 | # Moreover, this location expression needed to use opcodes | |
30 | # which GDB's DWARF reader could not convert to a simple | |
31 | # offset. (Note, however, that GDB could probably be improved | |
32 | # to handle the opcodes chosen for this test; if decode_locdesc() | |
33 | # in dwarf2/read.c is ever updated to handle both DW_OP_pick and | |
34 | # DW_OP_drop, then this test could end up passing even if | |
35 | # the bug it's intended to test has not been fixed.) | |
36 | # | |
37 | # 2) The debug info containing the above DWARF info needed | |
38 | # to be associated with a shared object since the problem | |
39 | # occurred while GDB was preserving values during the | |
40 | # purging of shared objects. | |
41 | # | |
42 | # 3) After performing some simple gdb commands, the program is | |
43 | # run again. In the course of running the objfile destructor | |
44 | # associated with the shared object, values are preserved | |
45 | # along with their types. As noted earlier, it was during | |
46 | # the recursive type copy that the bug was observed. | |
47 | # | |
48 | # Therefore, due to #2 above, this test case creates debug info | |
49 | # which is then used by a shared object. | |
50 | ||
51 | # This test can't be run on targets lacking shared library support. | |
52 | if [skip_shlib_tests] { | |
53 | return 0 | |
54 | } | |
55 | ||
56 | load_lib dwarf.exp | |
57 | ||
58 | # This test can only be run on targets which support DWARF-2 and use gas. | |
59 | if ![dwarf2_support] { | |
60 | return 0 | |
61 | } | |
62 | ||
63 | # gdb_test_file_name is the name of this file without the .exp | |
64 | # extension. Use it to form basenames for the main program | |
65 | # and shared object. | |
66 | set main_basename ${::gdb_test_file_name}-main | |
67 | set lib_basename ${::gdb_test_file_name}-lib | |
68 | ||
69 | # We're generating DWARF assembly for the shared object; therefore, | |
70 | # the source file for the library / shared object must be listed first | |
71 | # (in the standard_testfile invocation) since ${srcfile} is used by | |
72 | # get_func_info (for determining the start, end, and length of a | |
73 | # function). | |
74 | # | |
75 | # The output of Dwarf::assemble will be placed in $lib_basename.S | |
76 | # which will be ${srcfile3} after the execution of standard_testfile. | |
77 | ||
78 | standard_testfile $lib_basename.c $main_basename.c $lib_basename.S | |
79 | ||
80 | set libsrc "${::srcdir}/${::subdir}/${::srcfile}" | |
81 | set lib_so [standard_output_file ${lib_basename}.so] | |
82 | set asm_file [standard_output_file ${::srcfile3}] | |
83 | ||
84 | # We need to know the size of some types in order to write some of the | |
85 | # debugging info that we're about to generate. For that, we ask GDB | |
86 | # by debugging the shared object associated with this test case. | |
87 | ||
88 | # Compile the shared library: -DIS_SHAREDLIB prevents main() from | |
89 | # being defined. Note that debugging symbols will be present for | |
90 | # this compilation. | |
91 | if {[gdb_compile_shlib $libsrc $lib_so \ | |
92 | {additional_flags=-DIS_SHAREDLIB debug}] != ""} { | |
93 | untested "failed to compile shared library" | |
94 | return | |
95 | } | |
96 | ||
97 | # Start a fresh GDB and load the shared library. | |
98 | clean_restart $lib_so | |
99 | ||
100 | # Using our running GDB session, determine sizes of several types. | |
101 | set long_size [get_sizeof "long" -1] | |
102 | set addr_size [get_sizeof "void *" -1] | |
103 | set struct_A_size [get_sizeof "g_A" -1] | |
104 | set struct_B_size [get_sizeof "g_B" -1] | |
105 | ||
106 | if { $long_size == -1 || $addr_size == -1 \ | |
107 | || $struct_A_size == -1 || $struct_B_size == -1} { | |
108 | perror "Can't determine type sizes" | |
109 | return | |
110 | } | |
111 | ||
112 | # Retrieve struct offset of MBR in struct TP | |
113 | proc get_offsetof { tp mbr } { | |
114 | return [get_integer_valueof "&((${tp} *) 0)->${mbr}" -1] | |
115 | } | |
116 | ||
117 | # Use running GDB session to get struct offsets | |
118 | set A_a [get_offsetof A a] | |
119 | set A_x [get_offsetof A x] | |
120 | set B_a [get_offsetof B a] | |
121 | set B_b [get_offsetof B b] | |
122 | set B_x2 [get_offsetof B x2] | |
123 | ||
124 | # Create the DWARF. | |
125 | Dwarf::assemble ${asm_file} { | |
126 | declare_labels L | |
127 | ||
128 | # Find start, end, and length of functions foo and bar. | |
129 | # These calls to get_func_info will create and set variables | |
130 | # foo_start, bar_start, foo_end, bar_end, foo_len, and | |
131 | # bar_len. | |
132 | # | |
133 | # In order to get the right answers, get_func_info (and, | |
134 | # underneath, function_range) should use the same compiler flags | |
135 | # as those used to make a shared object. For any targets that get | |
136 | # this far, -fpic is probably correct. | |
137 | # | |
138 | # Also, it should be noted that IS_SHAREDLIB is NOT defined as one | |
139 | # of the additional flags. Not defining IS_SHAREDLIB will cause a | |
140 | # main() to be defined for the compilation of the shared library | |
141 | # source file which happens as a result of using get_func_info; | |
142 | # this is currently required in order to this facility. | |
143 | set flags {additional_flags=-fpic debug} | |
144 | get_func_info foo $flags | |
145 | get_func_info bar $flags | |
146 | ||
3859e65e | 147 | cu { label cu_label } { |
a640adf7 KB |
148 | DW_TAG_compile_unit { |
149 | {DW_AT_language @DW_LANG_C_plus_plus} | |
150 | {name ${::srcfile}} | |
151 | {stmt_list $L DW_FORM_sec_offset} | |
152 | } { | |
153 | declare_labels int_label class_A_label class_B_label \ | |
154 | B_ptr_label | |
155 | ||
156 | int_label: DW_TAG_base_type { | |
157 | {DW_AT_byte_size ${::long_size} DW_FORM_udata} | |
158 | {DW_AT_encoding @DW_ATE_signed} | |
159 | {DW_AT_name "int"} | |
160 | } | |
161 | ||
162 | class_A_label: DW_TAG_class_type { | |
163 | {DW_AT_name "A"} | |
164 | {DW_AT_byte_size ${::struct_A_size} DW_FORM_sdata} | |
165 | } { | |
166 | DW_TAG_member { | |
167 | {DW_AT_name "a"} | |
168 | {DW_AT_type :$int_label} | |
169 | {DW_AT_data_member_location ${::A_a} DW_FORM_udata} | |
170 | } | |
171 | DW_TAG_member { | |
172 | {DW_AT_name "x"} | |
173 | {DW_AT_type :$int_label} | |
174 | {DW_AT_data_member_location ${::A_x} DW_FORM_udata} | |
175 | } | |
176 | } | |
177 | ||
178 | class_B_label: DW_TAG_class_type { | |
179 | {DW_AT_name "B"} | |
180 | {DW_AT_byte_size ${::struct_B_size} DW_FORM_sdata} | |
181 | } { | |
182 | # While there are easier / better ways to specify an | |
183 | # offset used by DW_AT_data_member_location than that | |
184 | # used below, we need a location expression here in | |
185 | # order to reproduce the bug. Moreover, this location | |
186 | # expression needs to use opcodes that aren't handled | |
187 | # by decode_locdesc() in dwarf2/read.c; if we use | |
188 | # opcodes that _are_ handled by that function, the | |
189 | # location expression will be converted into a simple | |
190 | # offset - which will then (again) not reproduce the | |
191 | # bug. At the time that this test was written, | |
192 | # neither DW_OP_pick nor DW_OP_drop were being handled | |
193 | # by decode_locdesc(); this is why those opcodes were | |
194 | # chosen. | |
195 | DW_TAG_inheritance { | |
196 | {DW_AT_type :$class_A_label} | |
197 | {DW_AT_data_member_location { | |
198 | DW_OP_constu ${::B_a} | |
199 | DW_OP_plus | |
200 | DW_OP_pick 0 | |
201 | DW_OP_drop} SPECIAL_expr} | |
202 | {DW_AT_accessibility 1 DW_FORM_data1} | |
203 | } | |
204 | DW_TAG_member { | |
205 | {DW_AT_name "b"} | |
206 | {DW_AT_type :$int_label} | |
207 | {DW_AT_data_member_location ${::B_b} DW_FORM_udata} | |
208 | } | |
209 | DW_TAG_member { | |
210 | {DW_AT_name "x2"} | |
211 | {DW_AT_type :$int_label} | |
212 | {DW_AT_data_member_location ${::B_x2} DW_FORM_udata} | |
213 | } | |
214 | } | |
215 | ||
216 | B_ptr_label: DW_TAG_pointer_type { | |
217 | {DW_AT_type :$class_B_label} | |
218 | {DW_AT_byte_size ${::addr_size} DW_FORM_sdata} | |
219 | } | |
220 | ||
221 | DW_TAG_variable { | |
222 | {DW_AT_name "g_A"} | |
223 | {DW_AT_type :$class_A_label} | |
224 | {DW_AT_external 1 flag} | |
225 | {DW_AT_location {DW_OP_addr [gdb_target_symbol "g_A"]} \ | |
226 | SPECIAL_expr} | |
227 | } | |
228 | ||
229 | DW_TAG_variable { | |
230 | {DW_AT_name "g_B"} | |
231 | {DW_AT_type :$class_B_label} | |
232 | {DW_AT_external 1 flag} | |
233 | {DW_AT_location {DW_OP_addr [gdb_target_symbol "g_B"]} \ | |
234 | SPECIAL_expr} | |
235 | } | |
236 | ||
237 | # We can't use MACRO_AT for the definitions of foo and bar | |
238 | # because it doesn't provide a way to pass the appropriate | |
239 | # flags. Therefore, we list the name, low_pc, and high_pc | |
240 | # explicitly. | |
241 | DW_TAG_subprogram { | |
242 | {DW_AT_name foo} | |
243 | {DW_AT_low_pc $foo_start DW_FORM_addr} | |
244 | {DW_AT_high_pc $foo_end DW_FORM_addr} | |
245 | {DW_AT_type :${B_ptr_label}} | |
246 | {DW_AT_external 1 flag} | |
247 | } | |
248 | ||
249 | DW_TAG_subprogram { | |
250 | {DW_AT_name bar} | |
251 | {DW_AT_low_pc $bar_start DW_FORM_addr} | |
252 | {DW_AT_high_pc $bar_end DW_FORM_addr} | |
253 | {DW_AT_type :${B_ptr_label}} | |
254 | {DW_AT_external 1 flag} | |
255 | } { | |
256 | DW_TAG_formal_parameter { | |
257 | {DW_AT_name v} | |
258 | {DW_AT_type :${B_ptr_label}} | |
259 | } | |
260 | } | |
261 | } | |
262 | } | |
263 | ||
264 | lines {version 2} L { | |
265 | include_dir "${::srcdir}/${::subdir}" | |
266 | file_name "${::srcfile}" 1 | |
267 | ||
268 | # Generate a line table program. | |
269 | program { | |
d4c4a229 SM |
270 | DW_LNE_set_address $foo_start |
271 | line [gdb_get_line_number "foo prologue"] | |
272 | DW_LNS_copy | |
273 | DW_LNE_set_address foo_label | |
274 | line [gdb_get_line_number "foo return"] | |
275 | DW_LNS_copy | |
276 | line [gdb_get_line_number "foo end"] | |
277 | DW_LNS_copy | |
278 | DW_LNE_set_address $foo_end | |
279 | DW_LNS_advance_line 1 | |
280 | DW_LNS_copy | |
281 | DW_LNE_end_sequence | |
a640adf7 | 282 | |
d4c4a229 SM |
283 | DW_LNE_set_address $bar_start |
284 | line [gdb_get_line_number "bar prologue"] | |
285 | DW_LNS_copy | |
286 | DW_LNE_set_address bar_label | |
287 | line [gdb_get_line_number "bar return"] | |
288 | DW_LNS_copy | |
289 | line [gdb_get_line_number "bar end"] | |
290 | DW_LNS_copy | |
291 | DW_LNE_set_address $bar_end | |
292 | DW_LNS_advance_line 1 | |
293 | DW_LNS_copy | |
294 | DW_LNE_end_sequence | |
a640adf7 KB |
295 | } |
296 | } | |
3859e65e TV |
297 | |
298 | aranges {} cu_label { | |
299 | arange {} $foo_start $foo_end | |
300 | arange {} $bar_start $bar_end | |
301 | } | |
a640adf7 KB |
302 | } |
303 | ||
304 | # Compile the shared object again, but this time include / use the | |
305 | # DWARF info that we've created above. Note that (again) | |
306 | # -DIS_SHAREDLIB is used to prevent inclusion of main() in the shared | |
307 | # object. Also note the use of the "nodebug" option. Any debugging | |
308 | # information that we need will be provided by the DWARF info created | |
309 | # above. | |
310 | if {[gdb_compile_shlib [list $libsrc $asm_file] $lib_so \ | |
311 | {additional_flags=-DIS_SHAREDLIB nodebug}] != ""} { | |
312 | untested "failed to compile shared library" | |
313 | return | |
314 | } | |
315 | ||
316 | # Compile the main program for use with the shared object. | |
317 | if [prepare_for_testing "failed to prepare" ${testfile} \ | |
318 | ${::srcfile2} [list debug shlib=$lib_so]] { | |
319 | return -1 | |
320 | } | |
321 | ||
322 | # Do whatever is necessary to make sure that the shared library is | |
323 | # loaded for remote targets. | |
324 | gdb_load_shlib ${lib_so} | |
325 | ||
326 | if ![runto_main] then { | |
a640adf7 KB |
327 | return |
328 | } | |
329 | ||
330 | # Step into foo so that we can finish out of it. | |
331 | gdb_test "step" "foo .. at .* foo end.*" "step into foo" | |
332 | ||
333 | # Finishing out of foo will create a value that will later need to | |
334 | # be preserved when restarting the program. | |
335 | gdb_test "finish" "= \\(class B \\*\\) ${::hex} .*" "finish out of foo" | |
336 | ||
337 | # Dereferencing and printing the return value isn't necessary | |
338 | # for reproducing the bug, but we should make sure that the | |
339 | # return value is what we expect it to be. | |
340 | gdb_test "p *$" { = {<A> = {a = 8, x = 9}, b = 10, x2 = 11}} \ | |
341 | "dereference return value" | |
342 | ||
343 | # The original PR28030 reproducer stepped back into the shared object, | |
344 | # so we'll do the same here: | |
345 | gdb_test "step" "bar \\(.*" "step into bar" | |
346 | ||
347 | # We don't want a clean restart here since that will be too clean. | |
348 | # The original reproducer for PR28030 set a breakpoint in the shared | |
349 | # library and then restarted via "run". The command below does roughly | |
350 | # the same thing. It's at this step that an internal error would | |
351 | # occur for PR28030. The "message" argument tells runto to turn on | |
352 | # the printing of PASSes while runto is doing its job. | |
353 | runto "bar" message |