1 | //===-- DWARFExpressionTest.cpp -------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Expression/DWARFExpression.h" |
10 | #include "Plugins/Platform/Linux/PlatformLinux.h" |
11 | #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" |
12 | #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" |
13 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
14 | #include "TestingSupport/Symbol/YAMLModuleTester.h" |
15 | #include "lldb/Core/Debugger.h" |
16 | #include "lldb/Core/PluginManager.h" |
17 | #include "lldb/Core/Value.h" |
18 | #include "lldb/Core/dwarf.h" |
19 | #include "lldb/Host/HostInfo.h" |
20 | #include "lldb/Symbol/ObjectFile.h" |
21 | #include "lldb/Utility/StreamString.h" |
22 | #include "llvm/ADT/StringExtras.h" |
23 | #include "llvm/Testing/Support/Error.h" |
24 | #include "gtest/gtest.h" |
25 | |
26 | using namespace lldb_private; |
27 | using namespace lldb_private::dwarf; |
28 | using namespace lldb_private::plugin::dwarf; |
29 | |
30 | static llvm::Expected<Scalar> Evaluate(llvm::ArrayRef<uint8_t> expr, |
31 | lldb::ModuleSP module_sp = {}, |
32 | DWARFUnit *unit = nullptr, |
33 | ExecutionContext *exe_ctx = nullptr) { |
34 | DataExtractor (expr.data(), expr.size(), lldb::eByteOrderLittle, |
35 | /*addr_size*/ 4); |
36 | Value result; |
37 | Status status; |
38 | if (!DWARFExpression::Evaluate(exe_ctx, /*reg_ctx*/ nullptr, module_sp, |
39 | opcodes: extractor, dwarf_cu: unit, reg_set: lldb::eRegisterKindLLDB, |
40 | /*initial_value_ptr*/ nullptr, |
41 | /*object_address_ptr*/ nullptr, result, |
42 | error_ptr: &status)) |
43 | return status.ToError(); |
44 | |
45 | switch (result.GetValueType()) { |
46 | case Value::ValueType::Scalar: |
47 | return result.GetScalar(); |
48 | case Value::ValueType::LoadAddress: |
49 | return LLDB_INVALID_ADDRESS; |
50 | case Value::ValueType::HostAddress: { |
51 | // Convert small buffers to scalars to simplify the tests. |
52 | DataBufferHeap &buf = result.GetBuffer(); |
53 | if (buf.GetByteSize() <= 8) { |
54 | uint64_t val = 0; |
55 | memcpy(dest: &val, src: buf.GetBytes(), n: buf.GetByteSize()); |
56 | return Scalar(llvm::APInt(buf.GetByteSize()*8, val, false)); |
57 | } |
58 | } |
59 | [[fallthrough]]; |
60 | default: |
61 | return status.ToError(); |
62 | } |
63 | } |
64 | |
65 | class DWARFExpressionTester : public YAMLModuleTester { |
66 | public: |
67 | DWARFExpressionTester(llvm::StringRef yaml_data, size_t cu_index) : |
68 | YAMLModuleTester(yaml_data, cu_index) {} |
69 | |
70 | using YAMLModuleTester::YAMLModuleTester; |
71 | llvm::Expected<Scalar> Eval(llvm::ArrayRef<uint8_t> expr) { |
72 | return ::Evaluate(expr, module_sp: m_module_sp, unit: m_dwarf_unit); |
73 | } |
74 | }; |
75 | |
76 | /// Unfortunately Scalar's operator==() is really picky. |
77 | static Scalar GetScalar(unsigned bits, uint64_t value, bool sign) { |
78 | Scalar scalar(value); |
79 | scalar.TruncOrExtendTo(bits, sign); |
80 | return scalar; |
81 | } |
82 | |
83 | /// This is needed for the tests that use a mock process. |
84 | class DWARFExpressionMockProcessTest : public ::testing::Test { |
85 | public: |
86 | void SetUp() override { |
87 | FileSystem::Initialize(); |
88 | HostInfo::Initialize(); |
89 | platform_linux::PlatformLinux::Initialize(); |
90 | } |
91 | void TearDown() override { |
92 | platform_linux::PlatformLinux::Terminate(); |
93 | HostInfo::Terminate(); |
94 | FileSystem::Terminate(); |
95 | } |
96 | }; |
97 | |
98 | TEST(DWARFExpression, DW_OP_pick) { |
99 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0}), |
100 | llvm::HasValue(0)); |
101 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 1}), |
102 | llvm::HasValue(1)); |
103 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 2}), |
104 | llvm::Failed()); |
105 | } |
106 | |
107 | TEST(DWARFExpression, DW_OP_const) { |
108 | // Extend to address size. |
109 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x88}), llvm::HasValue(0x88)); |
110 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1s, 0x88}), |
111 | llvm::HasValue(0xffffff88)); |
112 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x47, 0x88}), |
113 | llvm::HasValue(0x8847)); |
114 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2s, 0x47, 0x88}), |
115 | llvm::HasValue(0xffff8847)); |
116 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4u, 0x44, 0x42, 0x47, 0x88}), |
117 | llvm::HasValue(0x88474244)); |
118 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4s, 0x44, 0x42, 0x47, 0x88}), |
119 | llvm::HasValue(0x88474244)); |
120 | |
121 | // Truncate to address size. |
122 | EXPECT_THAT_EXPECTED( |
123 | Evaluate({DW_OP_const8u, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}), |
124 | llvm::HasValue(0x33221100)); |
125 | EXPECT_THAT_EXPECTED( |
126 | Evaluate({DW_OP_const8s, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}), |
127 | llvm::HasValue(0x33221100)); |
128 | |
129 | // Don't truncate to address size for compatibility with clang (pr48087). |
130 | EXPECT_THAT_EXPECTED( |
131 | Evaluate({DW_OP_constu, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}), |
132 | llvm::HasValue(0x01010101010101)); |
133 | EXPECT_THAT_EXPECTED( |
134 | Evaluate({DW_OP_consts, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}), |
135 | llvm::HasValue(0xffff010101010101)); |
136 | } |
137 | |
138 | TEST(DWARFExpression, DW_OP_skip) { |
139 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x42, DW_OP_skip, 0x02, 0x00, |
140 | DW_OP_const1u, 0xff}), |
141 | llvm::HasValue(0x42)); |
142 | } |
143 | |
144 | TEST(DWARFExpression, DW_OP_bra) { |
145 | EXPECT_THAT_EXPECTED( |
146 | // clang-format off |
147 | Evaluate({ |
148 | DW_OP_const1u, 0x42, // push 0x42 |
149 | DW_OP_const1u, 0x1, // push 0x1 |
150 | DW_OP_bra, 0x02, 0x00, // if 0x1 > 0, then skip 0x0002 opcodes |
151 | DW_OP_const1u, 0xff, // push 0xff |
152 | }), |
153 | // clang-format on |
154 | llvm::HasValue(0x42)); |
155 | } |
156 | |
157 | TEST(DWARFExpression, DW_OP_convert) { |
158 | /// Auxiliary debug info. |
159 | const char *yamldata = R"( |
160 | --- !ELF |
161 | FileHeader: |
162 | Class: ELFCLASS64 |
163 | Data: ELFDATA2LSB |
164 | Type: ET_EXEC |
165 | Machine: EM_386 |
166 | DWARF: |
167 | debug_abbrev: |
168 | - Table: |
169 | - Code: 0x00000001 |
170 | Tag: DW_TAG_compile_unit |
171 | Children: DW_CHILDREN_yes |
172 | Attributes: |
173 | - Attribute: DW_AT_language |
174 | Form: DW_FORM_data2 |
175 | - Code: 0x00000002 |
176 | Tag: DW_TAG_base_type |
177 | Children: DW_CHILDREN_no |
178 | Attributes: |
179 | - Attribute: DW_AT_encoding |
180 | Form: DW_FORM_data1 |
181 | - Attribute: DW_AT_byte_size |
182 | Form: DW_FORM_data1 |
183 | debug_info: |
184 | - Version: 4 |
185 | AddrSize: 8 |
186 | AbbrevTableID: 0 |
187 | AbbrOffset: 0x0 |
188 | Entries: |
189 | - AbbrCode: 0x00000001 |
190 | Values: |
191 | - Value: 0x000000000000000C |
192 | - AbbrCode: 0x00000000 |
193 | - Version: 4 |
194 | AddrSize: 8 |
195 | AbbrevTableID: 0 |
196 | AbbrOffset: 0x0 |
197 | Entries: |
198 | - AbbrCode: 0x00000001 |
199 | Values: |
200 | - Value: 0x000000000000000C |
201 | # 0x0000000e: |
202 | - AbbrCode: 0x00000002 |
203 | Values: |
204 | - Value: 0x0000000000000007 # DW_ATE_unsigned |
205 | - Value: 0x0000000000000004 |
206 | # 0x00000011: |
207 | - AbbrCode: 0x00000002 |
208 | Values: |
209 | - Value: 0x0000000000000007 # DW_ATE_unsigned |
210 | - Value: 0x0000000000000008 |
211 | # 0x00000014: |
212 | - AbbrCode: 0x00000002 |
213 | Values: |
214 | - Value: 0x0000000000000005 # DW_ATE_signed |
215 | - Value: 0x0000000000000008 |
216 | # 0x00000017: |
217 | - AbbrCode: 0x00000002 |
218 | Values: |
219 | - Value: 0x0000000000000008 # DW_ATE_unsigned_char |
220 | - Value: 0x0000000000000001 |
221 | # 0x0000001a: |
222 | - AbbrCode: 0x00000002 |
223 | Values: |
224 | - Value: 0x0000000000000006 # DW_ATE_signed_char |
225 | - Value: 0x0000000000000001 |
226 | # 0x0000001d: |
227 | - AbbrCode: 0x00000002 |
228 | Values: |
229 | - Value: 0x000000000000000b # DW_ATE_numeric_string |
230 | - Value: 0x0000000000000001 |
231 | - AbbrCode: 0x00000000 |
232 | |
233 | )" ; |
234 | // Compile unit relative offsets to each DW_TAG_base_type |
235 | uint8_t offs_uint32_t = 0x0000000e; |
236 | uint8_t offs_uint64_t = 0x00000011; |
237 | uint8_t offs_sint64_t = 0x00000014; |
238 | uint8_t offs_uchar = 0x00000017; |
239 | uint8_t offs_schar = 0x0000001a; |
240 | |
241 | DWARFExpressionTester t(yamldata, /*cu_index=*/1); |
242 | ASSERT_TRUE((bool)t.GetDwarfUnit()); |
243 | |
244 | // Constant is given as little-endian. |
245 | bool is_signed = true; |
246 | bool not_signed = false; |
247 | |
248 | // |
249 | // Positive tests. |
250 | // |
251 | |
252 | // Leave as is. |
253 | EXPECT_THAT_EXPECTED( |
254 | t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, // |
255 | DW_OP_convert, offs_uint32_t, DW_OP_stack_value}), |
256 | llvm::HasValue(GetScalar(64, 0x44332211, not_signed))); |
257 | |
258 | // Zero-extend to 64 bits. |
259 | EXPECT_THAT_EXPECTED( |
260 | t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, // |
261 | DW_OP_convert, offs_uint64_t, DW_OP_stack_value}), |
262 | llvm::HasValue(GetScalar(64, 0x44332211, not_signed))); |
263 | |
264 | // Sign-extend to 64 bits. |
265 | EXPECT_THAT_EXPECTED( |
266 | t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, // |
267 | DW_OP_convert, offs_sint64_t, DW_OP_stack_value}), |
268 | llvm::HasValue(GetScalar(64, 0xffffffffffeeddcc, is_signed))); |
269 | |
270 | // Sign-extend, then truncate. |
271 | EXPECT_THAT_EXPECTED( |
272 | t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, // |
273 | DW_OP_convert, offs_sint64_t, // |
274 | DW_OP_convert, offs_uint32_t, DW_OP_stack_value}), |
275 | llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed))); |
276 | |
277 | // Truncate to default unspecified (pointer-sized) type. |
278 | EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, // |
279 | DW_OP_convert, offs_sint64_t, // |
280 | DW_OP_convert, 0x00, DW_OP_stack_value}), |
281 | llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed))); |
282 | |
283 | // Truncate to 8 bits. |
284 | EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert, |
285 | offs_uchar, DW_OP_stack_value}), |
286 | llvm::HasValue(GetScalar(8, 'A', not_signed))); |
287 | |
288 | // Also truncate to 8 bits. |
289 | EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert, |
290 | offs_schar, DW_OP_stack_value}), |
291 | llvm::HasValue(GetScalar(8, 'A', is_signed))); |
292 | |
293 | // |
294 | // Errors. |
295 | // |
296 | |
297 | // No Module. |
298 | EXPECT_THAT_ERROR(Evaluate({DW_OP_const1s, 'X', DW_OP_convert, 0x00}, nullptr, |
299 | t.GetDwarfUnit()) |
300 | .takeError(), |
301 | llvm::Failed()); |
302 | |
303 | // No DIE. |
304 | EXPECT_THAT_ERROR( |
305 | t.Eval({DW_OP_const1s, 'X', DW_OP_convert, 0x01}).takeError(), |
306 | llvm::Failed()); |
307 | |
308 | // Unsupported. |
309 | EXPECT_THAT_ERROR( |
310 | t.Eval({DW_OP_const1s, 'X', DW_OP_convert, 0x1d}).takeError(), |
311 | llvm::Failed()); |
312 | } |
313 | |
314 | TEST(DWARFExpression, DW_OP_stack_value) { |
315 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_stack_value}), llvm::Failed()); |
316 | } |
317 | |
318 | TEST(DWARFExpression, DW_OP_piece) { |
319 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x11, 0x22, DW_OP_piece, 2, |
320 | DW_OP_const2u, 0x33, 0x44, DW_OP_piece, 2}), |
321 | llvm::HasValue(GetScalar(32, 0x44332211, true))); |
322 | EXPECT_THAT_EXPECTED( |
323 | Evaluate({DW_OP_piece, 1, DW_OP_const1u, 0xff, DW_OP_piece, 1}), |
324 | // Note that the "00" should really be "undef", but we can't |
325 | // represent that yet. |
326 | llvm::HasValue(GetScalar(16, 0xff00, true))); |
327 | } |
328 | |
329 | TEST(DWARFExpression, DW_OP_implicit_value) { |
330 | unsigned char bytes = 4; |
331 | |
332 | EXPECT_THAT_EXPECTED( |
333 | Evaluate({DW_OP_implicit_value, bytes, 0x11, 0x22, 0x33, 0x44}), |
334 | llvm::HasValue(GetScalar(8 * bytes, 0x44332211, true))); |
335 | } |
336 | |
337 | TEST(DWARFExpression, DW_OP_unknown) { |
338 | EXPECT_THAT_EXPECTED( |
339 | Evaluate({0xff}), |
340 | llvm::FailedWithMessage( |
341 | "Unhandled opcode DW_OP_unknown_ff in DWARFExpression" )); |
342 | } |
343 | |
344 | TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) { |
345 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit0, DW_OP_deref}), llvm::Failed()); |
346 | |
347 | struct MockProcess : Process { |
348 | MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) |
349 | : Process(target_sp, listener_sp) {} |
350 | |
351 | llvm::StringRef GetPluginName() override { return "mock process" ; } |
352 | bool CanDebug(lldb::TargetSP target, |
353 | bool plugin_specified_by_name) override { |
354 | return false; |
355 | }; |
356 | Status DoDestroy() override { return {}; } |
357 | void RefreshStateAfterStop() override {} |
358 | bool DoUpdateThreadList(ThreadList &old_thread_list, |
359 | ThreadList &new_thread_list) override { |
360 | return false; |
361 | }; |
362 | size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, |
363 | Status &error) override { |
364 | for (size_t i = 0; i < size; ++i) |
365 | ((char *)buf)[i] = (vm_addr + i) & 0xff; |
366 | error.Clear(); |
367 | return size; |
368 | } |
369 | }; |
370 | |
371 | // Set up a mock process. |
372 | ArchSpec arch("i386-pc-linux" ); |
373 | Platform::SetHostPlatform( |
374 | platform_linux::PlatformLinux::CreateInstance(force: true, arch: &arch)); |
375 | lldb::DebuggerSP debugger_sp = Debugger::CreateInstance(); |
376 | ASSERT_TRUE(debugger_sp); |
377 | lldb::TargetSP target_sp; |
378 | lldb::PlatformSP platform_sp; |
379 | debugger_sp->GetTargetList().CreateTarget( |
380 | debugger&: *debugger_sp, user_exe_path: "" , arch, get_dependent_modules: eLoadDependentsNo, platform_sp, target_sp); |
381 | ASSERT_TRUE(target_sp); |
382 | ASSERT_TRUE(target_sp->GetArchitecture().IsValid()); |
383 | ASSERT_TRUE(platform_sp); |
384 | lldb::ListenerSP listener_sp(Listener::MakeListener(name: "dummy" )); |
385 | lldb::ProcessSP process_sp = |
386 | std::make_shared<MockProcess>(args&: target_sp, args&: listener_sp); |
387 | ASSERT_TRUE(process_sp); |
388 | |
389 | ExecutionContext exe_ctx(process_sp); |
390 | // Implicit location: *0x4. |
391 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, |
392 | {}, {}, &exe_ctx), |
393 | llvm::HasValue(GetScalar(32, 0x07060504, false))); |
394 | // Memory location: *(*0x4). |
395 | // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses. |
396 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref}, {}, {}, &exe_ctx), |
397 | llvm::HasValue(Scalar(LLDB_INVALID_ADDRESS))); |
398 | // Memory location: *0x4. |
399 | // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses. |
400 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4}, {}, {}, &exe_ctx), |
401 | llvm::HasValue(Scalar(4))); |
402 | // Implicit location: *0x4. |
403 | // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses. |
404 | EXPECT_THAT_EXPECTED( |
405 | Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx), |
406 | llvm::HasValue(GetScalar(32, 0x07060504, false))); |
407 | } |
408 | |
409 | TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr) { |
410 | // Set up a wasm target |
411 | ArchSpec arch("wasm32-unknown-unknown-wasm" ); |
412 | lldb::PlatformSP host_platform_sp = |
413 | platform_linux::PlatformLinux::CreateInstance(force: true, arch: &arch); |
414 | ASSERT_TRUE(host_platform_sp); |
415 | Platform::SetHostPlatform(host_platform_sp); |
416 | lldb::DebuggerSP debugger_sp = Debugger::CreateInstance(); |
417 | ASSERT_TRUE(debugger_sp); |
418 | lldb::TargetSP target_sp; |
419 | lldb::PlatformSP platform_sp; |
420 | debugger_sp->GetTargetList().CreateTarget(debugger&: *debugger_sp, user_exe_path: "" , arch, |
421 | get_dependent_modules: lldb_private::eLoadDependentsNo, |
422 | platform_sp, target_sp); |
423 | |
424 | ExecutionContext exe_ctx(target_sp, false); |
425 | // DW_OP_addr takes a single operand of address size width: |
426 | uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0}; |
427 | DataExtractor (expr, sizeof(expr), lldb::eByteOrderLittle, |
428 | /*addr_size*/ 4); |
429 | Value result; |
430 | Status status; |
431 | ASSERT_TRUE(DWARFExpression::Evaluate( |
432 | &exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor, |
433 | /*unit*/ nullptr, lldb::eRegisterKindLLDB, |
434 | /*initial_value_ptr*/ nullptr, |
435 | /*object_address_ptr*/ nullptr, result, &status)) |
436 | << status.ToError(); |
437 | |
438 | ASSERT_EQ(result.GetValueType(), Value::ValueType::LoadAddress); |
439 | } |
440 | |
441 | TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) { |
442 | const char *yamldata = R"( |
443 | --- !ELF |
444 | FileHeader: |
445 | Class: ELFCLASS64 |
446 | Data: ELFDATA2LSB |
447 | Type: ET_EXEC |
448 | Machine: EM_386 |
449 | DWARF: |
450 | debug_abbrev: |
451 | - Table: |
452 | - Code: 0x00000001 |
453 | Tag: DW_TAG_compile_unit |
454 | Children: DW_CHILDREN_no |
455 | Attributes: |
456 | - Attribute: DW_AT_addr_base |
457 | Form: DW_FORM_sec_offset |
458 | |
459 | debug_info: |
460 | - Version: 5 |
461 | AddrSize: 4 |
462 | UnitType: DW_UT_compile |
463 | Entries: |
464 | - AbbrCode: 0x00000001 |
465 | Values: |
466 | - Value: 0x8 # Offset of the first Address past the header |
467 | - AbbrCode: 0x0 |
468 | |
469 | debug_addr: |
470 | - Version: 5 |
471 | AddressSize: 4 |
472 | Entries: |
473 | - Address: 0x1234 |
474 | - Address: 0x5678 |
475 | )" ; |
476 | |
477 | // Can't use DWARFExpressionTester from above because subsystems overlap with |
478 | // the fixture. |
479 | SubsystemRAII<ObjectFileELF, SymbolFileDWARF> subsystems; |
480 | llvm::Expected<TestFile> file = TestFile::fromYaml(Yaml: yamldata); |
481 | EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); |
482 | auto module_sp = std::make_shared<Module>(args: file->moduleSpec()); |
483 | auto *dwarf_cu = llvm::cast<SymbolFileDWARF>(Val: module_sp->GetSymbolFile()) |
484 | ->DebugInfo() |
485 | .GetUnitAtIndex(idx: 0); |
486 | ASSERT_TRUE(dwarf_cu); |
487 | dwarf_cu->ExtractDIEsIfNeeded(); |
488 | |
489 | // Set up a wasm target |
490 | ArchSpec arch("wasm32-unknown-unknown-wasm" ); |
491 | lldb::PlatformSP host_platform_sp = |
492 | platform_linux::PlatformLinux::CreateInstance(force: true, arch: &arch); |
493 | ASSERT_TRUE(host_platform_sp); |
494 | Platform::SetHostPlatform(host_platform_sp); |
495 | lldb::DebuggerSP debugger_sp = Debugger::CreateInstance(); |
496 | ASSERT_TRUE(debugger_sp); |
497 | lldb::TargetSP target_sp; |
498 | lldb::PlatformSP platform_sp; |
499 | debugger_sp->GetTargetList().CreateTarget(debugger&: *debugger_sp, user_exe_path: "" , arch, |
500 | get_dependent_modules: lldb_private::eLoadDependentsNo, |
501 | platform_sp, target_sp); |
502 | |
503 | ExecutionContext exe_ctx(target_sp, false); |
504 | |
505 | auto evaluate = [&](DWARFExpression &expr, Status &status, Value &result) { |
506 | DataExtractor ; |
507 | expr.GetExpressionData(data&: extractor); |
508 | return DWARFExpression::Evaluate( |
509 | exe_ctx: &exe_ctx, /*reg_ctx*/ nullptr, /*module_sp*/ {}, opcodes: extractor, dwarf_cu, |
510 | reg_set: lldb::eRegisterKindLLDB, |
511 | /*initial_value_ptr*/ nullptr, |
512 | /*object_address_ptr*/ nullptr, result, error_ptr: &status); |
513 | }; |
514 | |
515 | // DW_OP_addrx takes a single leb128 operand, the index in the addr table: |
516 | uint8_t expr_data[] = {DW_OP_addrx, 0x01}; |
517 | DataExtractor (expr_data, sizeof(expr_data), lldb::eByteOrderLittle, |
518 | /*addr_size*/ 4); |
519 | DWARFExpression expr(extractor); |
520 | |
521 | Status status; |
522 | Value result; |
523 | ASSERT_TRUE(evaluate(expr, status, result)) << status.ToError(); |
524 | ASSERT_EQ(result.GetValueType(), Value::ValueType::LoadAddress); |
525 | ASSERT_EQ(result.GetScalar().UInt(), 0x5678u); |
526 | |
527 | ASSERT_TRUE(expr.Update_DW_OP_addr(dwarf_cu, 0xdeadbeef)); |
528 | ASSERT_TRUE(evaluate(expr, status, result)) << status.ToError(); |
529 | ASSERT_EQ(result.GetValueType(), Value::ValueType::LoadAddress); |
530 | ASSERT_EQ(result.GetScalar().UInt(), 0xdeadbeefu); |
531 | } |
532 | |
533 | class CustomSymbolFileDWARF : public SymbolFileDWARF { |
534 | static char ID; |
535 | |
536 | public: |
537 | using SymbolFileDWARF::SymbolFileDWARF; |
538 | |
539 | bool isA(const void *ClassID) const override { |
540 | return ClassID == &ID || SymbolFile::isA(ClassID); |
541 | } |
542 | static bool classof(const SymbolFile *obj) { return obj->isA(ClassID: &ID); } |
543 | |
544 | static llvm::StringRef GetPluginNameStatic() { return "custom_dwarf" ; } |
545 | |
546 | static llvm::StringRef GetPluginDescriptionStatic() { |
547 | return "Symbol file reader with expression extensions." ; |
548 | } |
549 | |
550 | static void Initialize() { |
551 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
552 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance, |
553 | debugger_init_callback: SymbolFileDWARF::DebuggerInitialize); |
554 | } |
555 | |
556 | static void Terminate() { PluginManager::UnregisterPlugin(create_callback: CreateInstance); } |
557 | |
558 | static lldb_private::SymbolFile * |
559 | CreateInstance(lldb::ObjectFileSP objfile_sp) { |
560 | return new CustomSymbolFileDWARF(std::move(objfile_sp), |
561 | /*dwo_section_list*/ nullptr); |
562 | } |
563 | |
564 | lldb::offset_t |
565 | (const lldb_private::DataExtractor &data, |
566 | const lldb::offset_t data_offset, |
567 | const uint8_t op) const final { |
568 | auto offset = data_offset; |
569 | if (op != DW_OP_WASM_location) { |
570 | return LLDB_INVALID_OFFSET; |
571 | } |
572 | |
573 | // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32 |
574 | // Called with "arguments" 0x03 and 0x04 |
575 | // Location type: |
576 | if (data.GetU8(offset_ptr: &offset) != /* global */ 0x03) { |
577 | return LLDB_INVALID_OFFSET; |
578 | } |
579 | |
580 | // Index |
581 | if (data.GetU32(offset_ptr: &offset) != 0x04) { |
582 | return LLDB_INVALID_OFFSET; |
583 | } |
584 | |
585 | // Report the skipped distance: |
586 | return offset - data_offset; |
587 | } |
588 | |
589 | bool |
590 | (uint8_t op, const lldb_private::DataExtractor &opcodes, |
591 | lldb::offset_t &offset, |
592 | std::vector<lldb_private::Value> &stack) const final { |
593 | if (op != DW_OP_WASM_location) { |
594 | return false; |
595 | } |
596 | |
597 | // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32 |
598 | // Called with "arguments" 0x03 and 0x04 |
599 | // Location type: |
600 | if (opcodes.GetU8(offset_ptr: &offset) != /* global */ 0x03) { |
601 | return false; |
602 | } |
603 | |
604 | // Index: |
605 | if (opcodes.GetU32(offset_ptr: &offset) != 0x04) { |
606 | return false; |
607 | } |
608 | |
609 | // Return some value: |
610 | stack.push_back(x: {GetScalar(bits: 32, value: 42, sign: false)}); |
611 | return true; |
612 | } |
613 | }; |
614 | |
615 | char CustomSymbolFileDWARF::ID; |
616 | |
617 | static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp, |
618 | DWARFUnit &dwarf_unit) { |
619 | // Test that expression extensions can be evaluated, for example |
620 | // DW_OP_WASM_location which is not currently handled by DWARFExpression: |
621 | EXPECT_THAT_EXPECTED(Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03 |
622 | 0x04, 0x00, 0x00, // index:u32 |
623 | 0x00, DW_OP_stack_value}, |
624 | module_sp, &dwarf_unit), |
625 | llvm::HasValue(GetScalar(32, 42, false))); |
626 | |
627 | // Test that searches for opcodes work in the presence of extensions: |
628 | uint8_t expr[] = {DW_OP_WASM_location, 0x03, 0x04, 0x00, 0x00, 0x00, |
629 | DW_OP_form_tls_address}; |
630 | DataExtractor (expr, sizeof(expr), lldb::eByteOrderLittle, |
631 | /*addr_size*/ 4); |
632 | DWARFExpression dwarf_expr(extractor); |
633 | ASSERT_TRUE(dwarf_expr.ContainsThreadLocalStorage(&dwarf_unit)); |
634 | } |
635 | |
636 | TEST(DWARFExpression, Extensions) { |
637 | const char *yamldata = R"( |
638 | --- !ELF |
639 | FileHeader: |
640 | Class: ELFCLASS64 |
641 | Data: ELFDATA2LSB |
642 | Type: ET_EXEC |
643 | Machine: EM_386 |
644 | DWARF: |
645 | debug_abbrev: |
646 | - Table: |
647 | - Code: 0x00000001 |
648 | Tag: DW_TAG_compile_unit |
649 | Children: DW_CHILDREN_no |
650 | debug_info: |
651 | - Version: 4 |
652 | AddrSize: 4 |
653 | Entries: |
654 | - AbbrCode: 0x1 |
655 | - AbbrCode: 0x0 |
656 | )" ; |
657 | |
658 | SubsystemRAII<FileSystem, HostInfo, TypeSystemClang, ObjectFileELF, |
659 | CustomSymbolFileDWARF> |
660 | subsystems; |
661 | |
662 | llvm::Expected<TestFile> file = TestFile::fromYaml(Yaml: yamldata); |
663 | EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); |
664 | |
665 | auto module_sp = std::make_shared<Module>(args: file->moduleSpec()); |
666 | auto &symfile = |
667 | *llvm::cast<CustomSymbolFileDWARF>(Val: module_sp->GetSymbolFile()); |
668 | auto *dwarf_unit = symfile.DebugInfo().GetUnitAtIndex(idx: 0); |
669 | |
670 | testExpressionVendorExtensions(module_sp, dwarf_unit&: *dwarf_unit); |
671 | } |
672 | |
673 | TEST(DWARFExpression, ExtensionsDWO) { |
674 | const char *skeleton_yamldata = R"( |
675 | --- !ELF |
676 | FileHeader: |
677 | Class: ELFCLASS64 |
678 | Data: ELFDATA2LSB |
679 | Type: ET_EXEC |
680 | Machine: EM_386 |
681 | DWARF: |
682 | debug_abbrev: |
683 | - Table: |
684 | - Code: 0x00000001 |
685 | Tag: DW_TAG_skeleton_unit |
686 | Children: DW_CHILDREN_no |
687 | Attributes: |
688 | - Attribute: DW_AT_dwo_name |
689 | Form: DW_FORM_string |
690 | - Attribute: DW_AT_dwo_id |
691 | Form: DW_FORM_data4 |
692 | debug_info: |
693 | - Version: 4 |
694 | AddrSize: 4 |
695 | Entries: |
696 | - AbbrCode: 0x1 |
697 | Values: |
698 | - CStr: "dwo_unit" |
699 | - Value: 0x01020304 |
700 | - AbbrCode: 0x0 |
701 | )" ; |
702 | |
703 | // .dwo sections aren't currently supported by dwarfyaml. The dwo_yamldata |
704 | // contents where generated by roundtripping the following yaml through |
705 | // yaml2obj | obj2yaml and renaming the sections. This works because the |
706 | // structure of the .dwo and non-.dwo sections is identical. |
707 | // |
708 | // --- !ELF |
709 | // FileHeader: |
710 | // Class: ELFCLASS64 |
711 | // Data: ELFDATA2LSB |
712 | // Type: ET_EXEC |
713 | // Machine: EM_386 |
714 | // DWARF: |
715 | // debug_abbrev: #.dwo |
716 | // - Table: |
717 | // - Code: 0x00000001 |
718 | // Tag: DW_TAG_compile_unit |
719 | // Children: DW_CHILDREN_no |
720 | // Attributes: |
721 | // - Attribute: DW_AT_dwo_id |
722 | // Form: DW_FORM_data4 |
723 | // debug_info: #.dwo |
724 | // - Version: 4 |
725 | // AddrSize: 4 |
726 | // Entries: |
727 | // - AbbrCode: 0x1 |
728 | // Values: |
729 | // - Value: 0x0120304 |
730 | // - AbbrCode: 0x0 |
731 | const char *dwo_yamldata = R"( |
732 | --- !ELF |
733 | FileHeader: |
734 | Class: ELFCLASS64 |
735 | Data: ELFDATA2LSB |
736 | Type: ET_EXEC |
737 | Machine: EM_386 |
738 | Sections: |
739 | - Name: .debug_abbrev.dwo |
740 | Type: SHT_PROGBITS |
741 | AddressAlign: 0x1 |
742 | Content: '0111007506000000' |
743 | - Name: .debug_info.dwo |
744 | Type: SHT_PROGBITS |
745 | AddressAlign: 0x1 |
746 | Content: 0D00000004000000000004010403020100 |
747 | )" ; |
748 | |
749 | SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, CustomSymbolFileDWARF> |
750 | subsystems; |
751 | |
752 | llvm::Expected<TestFile> skeleton_file = |
753 | TestFile::fromYaml(Yaml: skeleton_yamldata); |
754 | EXPECT_THAT_EXPECTED(skeleton_file, llvm::Succeeded()); |
755 | llvm::Expected<TestFile> dwo_file = TestFile::fromYaml(Yaml: dwo_yamldata); |
756 | EXPECT_THAT_EXPECTED(dwo_file, llvm::Succeeded()); |
757 | |
758 | auto skeleton_module_sp = |
759 | std::make_shared<Module>(args: skeleton_file->moduleSpec()); |
760 | auto &skeleton_symfile = |
761 | *llvm::cast<CustomSymbolFileDWARF>(Val: skeleton_module_sp->GetSymbolFile()); |
762 | |
763 | auto dwo_module_sp = std::make_shared<Module>(args: dwo_file->moduleSpec()); |
764 | SymbolFileDWARFDwo dwo_symfile( |
765 | skeleton_symfile, dwo_module_sp->GetObjectFile()->shared_from_this(), |
766 | 0x0120304); |
767 | auto *dwo_dwarf_unit = dwo_symfile.DebugInfo().GetUnitAtIndex(idx: 0); |
768 | |
769 | testExpressionVendorExtensions(module_sp: dwo_module_sp, dwarf_unit&: *dwo_dwarf_unit); |
770 | } |
771 | |