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
26using namespace lldb_private;
27using namespace lldb_private::dwarf;
28using namespace lldb_private::plugin::dwarf;
29
30static 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 extractor(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
65class DWARFExpressionTester : public YAMLModuleTester {
66public:
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.
77static 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.
84class DWARFExpressionMockProcessTest : public ::testing::Test {
85public:
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
98TEST(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
107TEST(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
138TEST(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
144TEST(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
157TEST(DWARFExpression, DW_OP_convert) {
158 /// Auxiliary debug info.
159 const char *yamldata = R"(
160--- !ELF
161FileHeader:
162 Class: ELFCLASS64
163 Data: ELFDATA2LSB
164 Type: ET_EXEC
165 Machine: EM_386
166DWARF:
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
314TEST(DWARFExpression, DW_OP_stack_value) {
315 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_stack_value}), llvm::Failed());
316}
317
318TEST(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
329TEST(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
337TEST(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
344TEST_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
409TEST_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 extractor(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
441TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) {
442 const char *yamldata = R"(
443--- !ELF
444FileHeader:
445 Class: ELFCLASS64
446 Data: ELFDATA2LSB
447 Type: ET_EXEC
448 Machine: EM_386
449DWARF:
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 extractor;
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 extractor(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
533class CustomSymbolFileDWARF : public SymbolFileDWARF {
534 static char ID;
535
536public:
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 GetVendorDWARFOpcodeSize(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 ParseVendorDWARFOpcode(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
615char CustomSymbolFileDWARF::ID;
616
617static 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 extractor(expr, sizeof(expr), lldb::eByteOrderLittle,
631 /*addr_size*/ 4);
632 DWARFExpression dwarf_expr(extractor);
633 ASSERT_TRUE(dwarf_expr.ContainsThreadLocalStorage(&dwarf_unit));
634}
635
636TEST(DWARFExpression, Extensions) {
637 const char *yamldata = R"(
638--- !ELF
639FileHeader:
640 Class: ELFCLASS64
641 Data: ELFDATA2LSB
642 Type: ET_EXEC
643 Machine: EM_386
644DWARF:
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
673TEST(DWARFExpression, ExtensionsDWO) {
674 const char *skeleton_yamldata = R"(
675--- !ELF
676FileHeader:
677 Class: ELFCLASS64
678 Data: ELFDATA2LSB
679 Type: ET_EXEC
680 Machine: EM_386
681DWARF:
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
733FileHeader:
734 Class: ELFCLASS64
735 Data: ELFDATA2LSB
736 Type: ET_EXEC
737 Machine: EM_386
738Sections:
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

source code of lldb/unittests/Expression/DWARFExpressionTest.cpp