1 //===-- DWARFExpressionTest.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
26 using namespace lldb_private
;
27 using namespace lldb_private::dwarf
;
28 using namespace lldb_private::plugin::dwarf
;
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
extractor(expr
.data(), expr
.size(), lldb::eByteOrderLittle
,
37 llvm::Expected
<Value
> result
=
38 DWARFExpression::Evaluate(exe_ctx
, /*reg_ctx*/ nullptr, module_sp
,
39 extractor
, unit
, lldb::eRegisterKindLLDB
,
40 /*initial_value_ptr*/ nullptr,
41 /*object_address_ptr*/ nullptr);
43 return result
.takeError();
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) {
55 memcpy(&val
, buf
.GetBytes(), buf
.GetByteSize());
56 return Scalar(llvm::APInt(buf
.GetByteSize()*8, val
, false));
63 return llvm::createStringError("unsupported value type");
66 class DWARFExpressionTester
: public YAMLModuleTester
{
68 DWARFExpressionTester(llvm::StringRef yaml_data
, size_t cu_index
) :
69 YAMLModuleTester(yaml_data
, cu_index
) {}
71 using YAMLModuleTester::YAMLModuleTester
;
72 llvm::Expected
<Scalar
> Eval(llvm::ArrayRef
<uint8_t> expr
) {
73 return ::Evaluate(expr
, m_module_sp
, m_dwarf_unit
);
77 /// Unfortunately Scalar's operator==() is really picky.
78 static Scalar
GetScalar(unsigned bits
, uint64_t value
, bool sign
) {
80 scalar
.TruncOrExtendTo(bits
, sign
);
84 /// This is needed for the tests that use a mock process.
85 class DWARFExpressionMockProcessTest
: public ::testing::Test
{
87 void SetUp() override
{
88 FileSystem::Initialize();
89 HostInfo::Initialize();
90 platform_linux::PlatformLinux::Initialize();
92 void TearDown() override
{
93 platform_linux::PlatformLinux::Terminate();
94 HostInfo::Terminate();
95 FileSystem::Terminate();
99 // NB: This class doesn't use the override keyword to avoid
100 // -Winconsistent-missing-override warnings from the compiler. The
101 // inconsistency comes from the overriding definitions in the MOCK_*** macros.
102 class MockTarget
: public Target
{
104 MockTarget(Debugger
&debugger
, const ArchSpec
&target_arch
,
105 const lldb::PlatformSP
&platform_sp
)
106 : Target(debugger
, target_arch
, platform_sp
, true) {}
108 MOCK_METHOD2(ReadMemory
,
109 llvm::Expected
<std::vector
<uint8_t>>(lldb::addr_t addr
,
112 size_t ReadMemory(const Address
&addr
, void *dst
, size_t dst_len
,
113 Status
&error
, bool force_live_memory
= false,
114 lldb::addr_t
*load_addr_ptr
= nullptr) /*override*/ {
115 auto expected_memory
= this->ReadMemory(addr
.GetOffset(), dst_len
);
116 if (!expected_memory
) {
117 llvm::consumeError(expected_memory
.takeError());
120 const size_t bytes_read
= expected_memory
->size();
121 assert(bytes_read
<= dst_len
);
122 std::memcpy(dst
, expected_memory
->data(), bytes_read
);
127 TEST(DWARFExpression
, DW_OP_pick
) {
128 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1
, DW_OP_lit0
, DW_OP_pick
, 0}),
130 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1
, DW_OP_lit0
, DW_OP_pick
, 1}),
132 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1
, DW_OP_lit0
, DW_OP_pick
, 2}),
136 TEST(DWARFExpression
, DW_OP_const
) {
137 // Extend to address size.
138 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u
, 0x88}), llvm::HasValue(0x88));
139 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1s
, 0x88}),
140 llvm::HasValue(0xffffff88));
141 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u
, 0x47, 0x88}),
142 llvm::HasValue(0x8847));
143 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2s
, 0x47, 0x88}),
144 llvm::HasValue(0xffff8847));
145 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4u
, 0x44, 0x42, 0x47, 0x88}),
146 llvm::HasValue(0x88474244));
147 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4s
, 0x44, 0x42, 0x47, 0x88}),
148 llvm::HasValue(0x88474244));
150 // Truncate to address size.
151 EXPECT_THAT_EXPECTED(
152 Evaluate({DW_OP_const8u
, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
153 llvm::HasValue(0x33221100));
154 EXPECT_THAT_EXPECTED(
155 Evaluate({DW_OP_const8s
, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),
156 llvm::HasValue(0x33221100));
158 // Don't truncate to address size for compatibility with clang (pr48087).
159 EXPECT_THAT_EXPECTED(
160 Evaluate({DW_OP_constu
, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
161 llvm::HasValue(0x01010101010101));
162 EXPECT_THAT_EXPECTED(
163 Evaluate({DW_OP_consts
, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),
164 llvm::HasValue(0xffff010101010101));
167 TEST(DWARFExpression
, DW_OP_skip
) {
168 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u
, 0x42, DW_OP_skip
, 0x02, 0x00,
169 DW_OP_const1u
, 0xff}),
170 llvm::HasValue(0x42));
173 TEST(DWARFExpression
, DW_OP_bra
) {
174 EXPECT_THAT_EXPECTED(
177 DW_OP_const1u
, 0x42, // push 0x42
178 DW_OP_const1u
, 0x1, // push 0x1
179 DW_OP_bra
, 0x02, 0x00, // if 0x1 > 0, then skip 0x0002 opcodes
180 DW_OP_const1u
, 0xff, // push 0xff
183 llvm::HasValue(0x42));
185 EXPECT_THAT_ERROR(Evaluate({DW_OP_bra
, 0x01, 0x00}).takeError(),
189 TEST(DWARFExpression
, DW_OP_convert
) {
190 /// Auxiliary debug info.
191 const char *yamldata
= R
"(
202 Tag: DW_TAG_compile_unit
203 Children: DW_CHILDREN_yes
205 - Attribute: DW_AT_language
208 Tag: DW_TAG_base_type
209 Children: DW_CHILDREN_no
211 - Attribute: DW_AT_encoding
213 - Attribute: DW_AT_byte_size
221 - AbbrCode: 0x00000001
223 - Value: 0x000000000000000C
224 - AbbrCode: 0x00000000
230 - AbbrCode: 0x00000001
232 - Value: 0x000000000000000C
234 - AbbrCode: 0x00000002
236 - Value: 0x0000000000000007 # DW_ATE_unsigned
237 - Value: 0x0000000000000004
239 - AbbrCode: 0x00000002
241 - Value: 0x0000000000000007 # DW_ATE_unsigned
242 - Value: 0x0000000000000008
244 - AbbrCode: 0x00000002
246 - Value: 0x0000000000000005 # DW_ATE_signed
247 - Value: 0x0000000000000008
249 - AbbrCode: 0x00000002
251 - Value: 0x0000000000000008 # DW_ATE_unsigned_char
252 - Value: 0x0000000000000001
254 - AbbrCode: 0x00000002
256 - Value: 0x0000000000000006 # DW_ATE_signed_char
257 - Value: 0x0000000000000001
259 - AbbrCode: 0x00000002
261 - Value: 0x000000000000000b # DW_ATE_numeric_string
262 - Value: 0x0000000000000001
263 - AbbrCode: 0x00000000
266 // Compile unit relative offsets to each DW_TAG_base_type
267 uint8_t offs_uint32_t
= 0x0000000e;
268 uint8_t offs_uint64_t
= 0x00000011;
269 uint8_t offs_sint64_t
= 0x00000014;
270 uint8_t offs_uchar
= 0x00000017;
271 uint8_t offs_schar
= 0x0000001a;
273 DWARFExpressionTester
t(yamldata
, /*cu_index=*/1);
274 ASSERT_TRUE((bool)t
.GetDwarfUnit());
276 // Constant is given as little-endian.
277 bool is_signed
= true;
278 bool not_signed
= false;
285 EXPECT_THAT_EXPECTED(
286 t
.Eval({DW_OP_const4u
, 0x11, 0x22, 0x33, 0x44, //
287 DW_OP_convert
, offs_uint32_t
, DW_OP_stack_value
}),
288 llvm::HasValue(GetScalar(64, 0x44332211, not_signed
)));
290 // Zero-extend to 64 bits.
291 EXPECT_THAT_EXPECTED(
292 t
.Eval({DW_OP_const4u
, 0x11, 0x22, 0x33, 0x44, //
293 DW_OP_convert
, offs_uint64_t
, DW_OP_stack_value
}),
294 llvm::HasValue(GetScalar(64, 0x44332211, not_signed
)));
296 // Sign-extend to 64 bits.
297 EXPECT_THAT_EXPECTED(
298 t
.Eval({DW_OP_const4s
, 0xcc, 0xdd, 0xee, 0xff, //
299 DW_OP_convert
, offs_sint64_t
, DW_OP_stack_value
}),
300 llvm::HasValue(GetScalar(64, 0xffffffffffeeddcc, is_signed
)));
302 // Sign-extend, then truncate.
303 EXPECT_THAT_EXPECTED(
304 t
.Eval({DW_OP_const4s
, 0xcc, 0xdd, 0xee, 0xff, //
305 DW_OP_convert
, offs_sint64_t
, //
306 DW_OP_convert
, offs_uint32_t
, DW_OP_stack_value
}),
307 llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed
)));
309 // Truncate to default unspecified (pointer-sized) type.
310 EXPECT_THAT_EXPECTED(t
.Eval({DW_OP_const4s
, 0xcc, 0xdd, 0xee, 0xff, //
311 DW_OP_convert
, offs_sint64_t
, //
312 DW_OP_convert
, 0x00, DW_OP_stack_value
}),
313 llvm::HasValue(GetScalar(32, 0xffeeddcc, not_signed
)));
315 // Truncate to 8 bits.
316 EXPECT_THAT_EXPECTED(t
.Eval({DW_OP_const4s
, 'A', 'B', 'C', 'D', DW_OP_convert
,
317 offs_uchar
, DW_OP_stack_value
}),
318 llvm::HasValue(GetScalar(8, 'A', not_signed
)));
320 // Also truncate to 8 bits.
321 EXPECT_THAT_EXPECTED(t
.Eval({DW_OP_const4s
, 'A', 'B', 'C', 'D', DW_OP_convert
,
322 offs_schar
, DW_OP_stack_value
}),
323 llvm::HasValue(GetScalar(8, 'A', is_signed
)));
330 EXPECT_THAT_ERROR(Evaluate({DW_OP_const1s
, 'X', DW_OP_convert
, 0x00}, nullptr,
337 t
.Eval({DW_OP_const1s
, 'X', DW_OP_convert
, 0x01}).takeError(),
342 t
.Eval({DW_OP_const1s
, 'X', DW_OP_convert
, 0x1d}).takeError(),
346 TEST(DWARFExpression
, DW_OP_stack_value
) {
347 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_stack_value
}), llvm::Failed());
350 TEST(DWARFExpression
, DW_OP_piece
) {
351 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u
, 0x11, 0x22, DW_OP_piece
, 2,
352 DW_OP_const2u
, 0x33, 0x44, DW_OP_piece
, 2}),
353 llvm::HasValue(GetScalar(32, 0x44332211, true)));
354 EXPECT_THAT_EXPECTED(
355 Evaluate({DW_OP_piece
, 1, DW_OP_const1u
, 0xff, DW_OP_piece
, 1}),
356 // Note that the "00" should really be "undef", but we can't
357 // represent that yet.
358 llvm::HasValue(GetScalar(16, 0xff00, true)));
361 TEST(DWARFExpression
, DW_OP_implicit_value
) {
362 unsigned char bytes
= 4;
364 EXPECT_THAT_EXPECTED(
365 Evaluate({DW_OP_implicit_value
, bytes
, 0x11, 0x22, 0x33, 0x44}),
366 llvm::HasValue(GetScalar(8 * bytes
, 0x44332211, true)));
369 TEST(DWARFExpression
, DW_OP_unknown
) {
370 EXPECT_THAT_EXPECTED(
372 llvm::FailedWithMessage(
373 "Unhandled opcode DW_OP_unknown_ff in DWARFExpression"));
376 TEST_F(DWARFExpressionMockProcessTest
, DW_OP_deref
) {
377 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit0
, DW_OP_deref
}), llvm::Failed());
379 struct MockProcess
: Process
{
380 MockProcess(lldb::TargetSP target_sp
, lldb::ListenerSP listener_sp
)
381 : Process(target_sp
, listener_sp
) {}
383 llvm::StringRef
GetPluginName() override
{ return "mock process"; }
384 bool CanDebug(lldb::TargetSP target
,
385 bool plugin_specified_by_name
) override
{
388 Status
DoDestroy() override
{ return {}; }
389 void RefreshStateAfterStop() override
{}
390 bool DoUpdateThreadList(ThreadList
&old_thread_list
,
391 ThreadList
&new_thread_list
) override
{
394 size_t DoReadMemory(lldb::addr_t vm_addr
, void *buf
, size_t size
,
395 Status
&error
) override
{
396 for (size_t i
= 0; i
< size
; ++i
)
397 ((char *)buf
)[i
] = (vm_addr
+ i
) & 0xff;
403 // Set up a mock process.
404 ArchSpec
arch("i386-pc-linux");
405 Platform::SetHostPlatform(
406 platform_linux::PlatformLinux::CreateInstance(true, &arch
));
407 lldb::DebuggerSP debugger_sp
= Debugger::CreateInstance();
408 ASSERT_TRUE(debugger_sp
);
409 lldb::TargetSP target_sp
;
410 lldb::PlatformSP platform_sp
;
411 debugger_sp
->GetTargetList().CreateTarget(
412 *debugger_sp
, "", arch
, eLoadDependentsNo
, platform_sp
, target_sp
);
413 ASSERT_TRUE(target_sp
);
414 ASSERT_TRUE(target_sp
->GetArchitecture().IsValid());
415 ASSERT_TRUE(platform_sp
);
416 lldb::ListenerSP
listener_sp(Listener::MakeListener("dummy"));
417 lldb::ProcessSP process_sp
=
418 std::make_shared
<MockProcess
>(target_sp
, listener_sp
);
419 ASSERT_TRUE(process_sp
);
421 ExecutionContext
exe_ctx(process_sp
);
422 // Implicit location: *0x4.
423 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4
, DW_OP_deref
, DW_OP_stack_value
},
425 llvm::HasValue(GetScalar(32, 0x07060504, false)));
426 // Memory location: *(*0x4).
427 // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
428 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4
, DW_OP_deref
}, {}, {}, &exe_ctx
),
429 llvm::HasValue(Scalar(LLDB_INVALID_ADDRESS
)));
430 // Memory location: *0x4.
431 // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
432 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4
}, {}, {}, &exe_ctx
),
433 llvm::HasValue(Scalar(4)));
434 // Implicit location: *0x4.
435 // Evaluate returns LLDB_INVALID_ADDRESS for all load addresses.
436 EXPECT_THAT_EXPECTED(
437 Evaluate({DW_OP_lit4
, DW_OP_deref
, DW_OP_stack_value
}, {}, {}, &exe_ctx
),
438 llvm::HasValue(GetScalar(32, 0x07060504, false)));
441 TEST_F(DWARFExpressionMockProcessTest
, WASM_DW_OP_addr
) {
442 // Set up a wasm target
443 ArchSpec
arch("wasm32-unknown-unknown-wasm");
444 lldb::PlatformSP host_platform_sp
=
445 platform_linux::PlatformLinux::CreateInstance(true, &arch
);
446 ASSERT_TRUE(host_platform_sp
);
447 Platform::SetHostPlatform(host_platform_sp
);
448 lldb::DebuggerSP debugger_sp
= Debugger::CreateInstance();
449 ASSERT_TRUE(debugger_sp
);
450 lldb::TargetSP target_sp
;
451 lldb::PlatformSP platform_sp
;
452 debugger_sp
->GetTargetList().CreateTarget(*debugger_sp
, "", arch
,
453 lldb_private::eLoadDependentsNo
,
454 platform_sp
, target_sp
);
456 ExecutionContext
exe_ctx(target_sp
, false);
457 // DW_OP_addr takes a single operand of address size width:
458 uint8_t expr
[] = {DW_OP_addr
, 0x40, 0x0, 0x0, 0x0};
459 DataExtractor
extractor(expr
, sizeof(expr
), lldb::eByteOrderLittle
,
462 llvm::Expected
<Value
> result
= DWARFExpression::Evaluate(
463 &exe_ctx
, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor
,
464 /*unit*/ nullptr, lldb::eRegisterKindLLDB
,
465 /*initial_value_ptr*/ nullptr,
466 /*object_address_ptr*/ nullptr);
468 ASSERT_THAT_EXPECTED(result
, llvm::Succeeded());
469 ASSERT_EQ(result
->GetValueType(), Value::ValueType::LoadAddress
);
472 TEST_F(DWARFExpressionMockProcessTest
, WASM_DW_OP_addr_index
) {
473 const char *yamldata
= R
"(
484 Tag: DW_TAG_compile_unit
485 Children: DW_CHILDREN_no
487 - Attribute: DW_AT_addr_base
488 Form: DW_FORM_sec_offset
493 UnitType: DW_UT_compile
495 - AbbrCode: 0x00000001
497 - Value: 0x8 # Offset of the first Address past the header
508 // Can't use DWARFExpressionTester from above because subsystems overlap with
510 SubsystemRAII
<ObjectFileELF
, SymbolFileDWARF
> subsystems
;
511 llvm::Expected
<TestFile
> file
= TestFile::fromYaml(yamldata
);
512 EXPECT_THAT_EXPECTED(file
, llvm::Succeeded());
513 auto module_sp
= std::make_shared
<Module
>(file
->moduleSpec());
514 auto *dwarf_cu
= llvm::cast
<SymbolFileDWARF
>(module_sp
->GetSymbolFile())
517 ASSERT_TRUE(dwarf_cu
);
518 dwarf_cu
->ExtractDIEsIfNeeded();
520 // Set up a wasm target
521 ArchSpec
arch("wasm32-unknown-unknown-wasm");
522 lldb::PlatformSP host_platform_sp
=
523 platform_linux::PlatformLinux::CreateInstance(true, &arch
);
524 ASSERT_TRUE(host_platform_sp
);
525 Platform::SetHostPlatform(host_platform_sp
);
526 lldb::DebuggerSP debugger_sp
= Debugger::CreateInstance();
527 ASSERT_TRUE(debugger_sp
);
528 lldb::TargetSP target_sp
;
529 lldb::PlatformSP platform_sp
;
530 debugger_sp
->GetTargetList().CreateTarget(*debugger_sp
, "", arch
,
531 lldb_private::eLoadDependentsNo
,
532 platform_sp
, target_sp
);
534 ExecutionContext
exe_ctx(target_sp
, false);
536 auto evaluate
= [&](DWARFExpression
&expr
) -> llvm::Expected
<Value
> {
537 DataExtractor extractor
;
538 expr
.GetExpressionData(extractor
);
539 return DWARFExpression::Evaluate(&exe_ctx
, /*reg_ctx*/ nullptr,
540 /*module_sp*/ {}, extractor
, dwarf_cu
,
541 lldb::eRegisterKindLLDB
,
542 /*initial_value_ptr*/ nullptr,
543 /*object_address_ptr*/ nullptr);
546 // DW_OP_addrx takes a single leb128 operand, the index in the addr table:
547 uint8_t expr_data
[] = {DW_OP_addrx
, 0x01};
548 DataExtractor
extractor(expr_data
, sizeof(expr_data
), lldb::eByteOrderLittle
,
550 DWARFExpression
expr(extractor
);
552 llvm::Expected
<Value
> result
= evaluate(expr
);
553 ASSERT_THAT_EXPECTED(result
, llvm::Succeeded());
554 ASSERT_EQ(result
->GetValueType(), Value::ValueType::LoadAddress
);
555 ASSERT_EQ(result
->GetScalar().UInt(), 0x5678u
);
557 ASSERT_TRUE(expr
.Update_DW_OP_addr(dwarf_cu
, 0xdeadbeef));
558 result
= evaluate(expr
);
559 ASSERT_THAT_EXPECTED(result
, llvm::Succeeded());
560 ASSERT_EQ(result
->GetValueType(), Value::ValueType::LoadAddress
);
561 ASSERT_EQ(result
->GetScalar().UInt(), 0xdeadbeefu
);
564 class CustomSymbolFileDWARF
: public SymbolFileDWARF
{
568 using SymbolFileDWARF::SymbolFileDWARF
;
570 bool isA(const void *ClassID
) const override
{
571 return ClassID
== &ID
|| SymbolFile::isA(ClassID
);
573 static bool classof(const SymbolFile
*obj
) { return obj
->isA(&ID
); }
575 static llvm::StringRef
GetPluginNameStatic() { return "custom_dwarf"; }
577 static llvm::StringRef
GetPluginDescriptionStatic() {
578 return "Symbol file reader with expression extensions.";
581 static void Initialize() {
582 PluginManager::RegisterPlugin(GetPluginNameStatic(),
583 GetPluginDescriptionStatic(), CreateInstance
,
584 SymbolFileDWARF::DebuggerInitialize
);
587 static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance
); }
589 static lldb_private::SymbolFile
*
590 CreateInstance(lldb::ObjectFileSP objfile_sp
) {
591 return new CustomSymbolFileDWARF(std::move(objfile_sp
),
592 /*dwo_section_list*/ nullptr);
596 GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor
&data
,
597 const lldb::offset_t data_offset
,
598 const uint8_t op
) const final
{
599 auto offset
= data_offset
;
600 if (op
!= DW_OP_WASM_location
) {
601 return LLDB_INVALID_OFFSET
;
604 // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32
605 // Called with "arguments" 0x03 and 0x04
607 if (data
.GetU8(&offset
) != /* global */ 0x03) {
608 return LLDB_INVALID_OFFSET
;
612 if (data
.GetU32(&offset
) != 0x04) {
613 return LLDB_INVALID_OFFSET
;
616 // Report the skipped distance:
617 return offset
- data_offset
;
621 ParseVendorDWARFOpcode(uint8_t op
, const lldb_private::DataExtractor
&opcodes
,
622 lldb::offset_t
&offset
,
623 std::vector
<lldb_private::Value
> &stack
) const final
{
624 if (op
!= DW_OP_WASM_location
) {
628 // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32
629 // Called with "arguments" 0x03 and 0x04
631 if (opcodes
.GetU8(&offset
) != /* global */ 0x03) {
636 if (opcodes
.GetU32(&offset
) != 0x04) {
640 // Return some value:
641 stack
.push_back({GetScalar(32, 42, false)});
646 char CustomSymbolFileDWARF::ID
;
648 static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp
,
649 DWARFUnit
&dwarf_unit
) {
650 // Test that expression extensions can be evaluated, for example
651 // DW_OP_WASM_location which is not currently handled by DWARFExpression:
652 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_WASM_location
, 0x03, // WASM_GLOBAL:0x03
653 0x04, 0x00, 0x00, // index:u32
654 0x00, DW_OP_stack_value
},
655 module_sp
, &dwarf_unit
),
656 llvm::HasValue(GetScalar(32, 42, false)));
658 // Test that searches for opcodes work in the presence of extensions:
659 uint8_t expr
[] = {DW_OP_WASM_location
, 0x03, 0x04, 0x00, 0x00, 0x00,
660 DW_OP_form_tls_address
};
661 DataExtractor
extractor(expr
, sizeof(expr
), lldb::eByteOrderLittle
,
663 DWARFExpression
dwarf_expr(extractor
);
664 ASSERT_TRUE(dwarf_expr
.ContainsThreadLocalStorage(&dwarf_unit
));
667 TEST(DWARFExpression
, Extensions
) {
668 const char *yamldata
= R
"(
679 Tag: DW_TAG_compile_unit
680 Children: DW_CHILDREN_no
689 SubsystemRAII
<FileSystem
, HostInfo
, TypeSystemClang
, ObjectFileELF
,
690 CustomSymbolFileDWARF
>
693 llvm::Expected
<TestFile
> file
= TestFile::fromYaml(yamldata
);
694 EXPECT_THAT_EXPECTED(file
, llvm::Succeeded());
696 auto module_sp
= std::make_shared
<Module
>(file
->moduleSpec());
698 *llvm::cast
<CustomSymbolFileDWARF
>(module_sp
->GetSymbolFile());
699 auto *dwarf_unit
= symfile
.DebugInfo().GetUnitAtIndex(0);
701 testExpressionVendorExtensions(module_sp
, *dwarf_unit
);
704 TEST(DWARFExpression
, ExtensionsDWO
) {
705 const char *skeleton_yamldata
= R
"(
716 Tag: DW_TAG_skeleton_unit
717 Children: DW_CHILDREN_no
719 - Attribute: DW_AT_dwo_name
721 - Attribute: DW_AT_dwo_id
734 // .dwo sections aren't currently supported by dwarfyaml. The dwo_yamldata
735 // contents where generated by roundtripping the following yaml through
736 // yaml2obj | obj2yaml and renaming the sections. This works because the
737 // structure of the .dwo and non-.dwo sections is identical.
746 // debug_abbrev: #.dwo
748 // - Code: 0x00000001
749 // Tag: DW_TAG_compile_unit
750 // Children: DW_CHILDREN_no
752 // - Attribute: DW_AT_dwo_id
753 // Form: DW_FORM_data4
760 // - Value: 0x0120304
762 const char *dwo_yamldata
= R
"(
770 - Name: .debug_abbrev.dwo
773 Content: '0111007506000000'
774 - Name: .debug_info.dwo
777 Content: 0D00000004000000000004010403020100
780 SubsystemRAII
<FileSystem
, HostInfo
, ObjectFileELF
, CustomSymbolFileDWARF
>
783 llvm::Expected
<TestFile
> skeleton_file
=
784 TestFile::fromYaml(skeleton_yamldata
);
785 EXPECT_THAT_EXPECTED(skeleton_file
, llvm::Succeeded());
786 llvm::Expected
<TestFile
> dwo_file
= TestFile::fromYaml(dwo_yamldata
);
787 EXPECT_THAT_EXPECTED(dwo_file
, llvm::Succeeded());
789 auto skeleton_module_sp
=
790 std::make_shared
<Module
>(skeleton_file
->moduleSpec());
791 auto &skeleton_symfile
=
792 *llvm::cast
<CustomSymbolFileDWARF
>(skeleton_module_sp
->GetSymbolFile());
794 auto dwo_module_sp
= std::make_shared
<Module
>(dwo_file
->moduleSpec());
795 SymbolFileDWARFDwo
dwo_symfile(
796 skeleton_symfile
, dwo_module_sp
->GetObjectFile()->shared_from_this(),
798 auto *dwo_dwarf_unit
= dwo_symfile
.DebugInfo().GetUnitAtIndex(0);
800 testExpressionVendorExtensions(dwo_module_sp
, *dwo_dwarf_unit
);
803 TEST_F(DWARFExpressionMockProcessTest
, DW_OP_piece_file_addr
) {
804 using ::testing::ByMove
;
805 using ::testing::ElementsAre
;
806 using ::testing::Return
;
808 // Set up a mock process.
809 ArchSpec
arch("i386-pc-linux");
810 Platform::SetHostPlatform(
811 platform_linux::PlatformLinux::CreateInstance(true, &arch
));
812 lldb::DebuggerSP debugger_sp
= Debugger::CreateInstance();
813 ASSERT_TRUE(debugger_sp
);
814 lldb::PlatformSP platform_sp
;
816 std::make_shared
<MockTarget
>(*debugger_sp
, arch
, platform_sp
);
817 ASSERT_TRUE(target_sp
);
818 ASSERT_TRUE(target_sp
->GetArchitecture().IsValid());
820 EXPECT_CALL(*target_sp
, ReadMemory(0x40, 1))
821 .WillOnce(Return(ByMove(std::vector
<uint8_t>{0x11})));
822 EXPECT_CALL(*target_sp
, ReadMemory(0x50, 1))
823 .WillOnce(Return(ByMove(std::vector
<uint8_t>{0x22})));
825 ExecutionContext
exe_ctx(static_cast<lldb::TargetSP
>(target_sp
), false);
827 uint8_t expr
[] = {DW_OP_addr
, 0x40, 0x0, 0x0, 0x0, DW_OP_piece
, 1,
828 DW_OP_addr
, 0x50, 0x0, 0x0, 0x0, DW_OP_piece
, 1};
829 DataExtractor
extractor(expr
, sizeof(expr
), lldb::eByteOrderLittle
,
831 llvm::Expected
<Value
> result
= DWARFExpression::Evaluate(
832 &exe_ctx
, /*reg_ctx*/ nullptr, /*module_sp*/ {}, extractor
,
833 /*unit*/ nullptr, lldb::eRegisterKindLLDB
,
834 /*initial_value_ptr*/ nullptr,
835 /*object_address_ptr*/ nullptr);
837 ASSERT_THAT_EXPECTED(result
, llvm::Succeeded());
838 ASSERT_EQ(result
->GetValueType(), Value::ValueType::HostAddress
);
839 ASSERT_THAT(result
->GetBuffer().GetData(), ElementsAre(0x11, 0x22));