1220 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8#include "lldb/Expression/DWARFExpression.h"9#include "ValueMatcher.h"10#include <unordered_map>11#ifdef ARCH_AARCH6412#include "Plugins/ABI/AArch64/ABISysV_arm64.h"13#endif14#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"15#include "Plugins/Platform/Linux/PlatformLinux.h"16#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"17#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"18#include "Plugins/SymbolFile/DWARF/SymbolFileWasm.h"19#include "Plugins/SymbolVendor/wasm/SymbolVendorWasm.h"20#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"21#include "TestingSupport/Symbol/YAMLModuleTester.h"22#include "lldb/Core/Debugger.h"23#include "lldb/Core/PluginManager.h"24#include "lldb/Core/Value.h"25#include "lldb/Core/dwarf.h"26#include "lldb/Host/HostInfo.h"27#include "lldb/Symbol/ObjectFile.h"28#include "lldb/Target/ABI.h"29#include "lldb/Target/RegisterContext.h"30#include "lldb/Utility/RegisterValue.h"31#include "lldb/Utility/StreamString.h"32#include "llvm/ADT/StringExtras.h"33#include "llvm/Support/TargetSelect.h"34#include "llvm/Testing/Support/Error.h"35#include "gtest/gtest.h"36 37using namespace lldb_private::plugin::dwarf;38using namespace lldb_private::wasm;39using namespace lldb_private;40using namespace llvm::dwarf;41 42namespace {43/// A mock implementation of DWARFExpression::Delegate for testing.44/// This class provides default implementations of all delegate methods,45/// with the DWARF version being configurable via the constructor.46class MockDwarfDelegate : public DWARFExpression::Delegate {47public:48 static constexpr uint16_t DEFAULT_DWARF_VERSION = 5;49 static MockDwarfDelegate Dwarf5() { return MockDwarfDelegate(5); }50 static MockDwarfDelegate Dwarf2() { return MockDwarfDelegate(2); }51 52 MockDwarfDelegate() : MockDwarfDelegate(DEFAULT_DWARF_VERSION) {}53 explicit MockDwarfDelegate(uint16_t version) : m_dwarf_version(version) {}54 55 uint16_t GetVersion() const override { return m_dwarf_version; }56 57 dw_addr_t GetBaseAddress() const override { return 0; }58 59 uint8_t GetAddressByteSize() const override { return 4; }60 61 llvm::Expected<std::pair<uint64_t, bool>>62 GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override {63 return llvm::createStringError(llvm::inconvertibleErrorCode(),64 "GetDIEBitSizeAndSign not implemented");65 }66 67 dw_addr_t ReadAddressFromDebugAddrSection(uint32_t index) const override {68 return 0;69 }70 71 lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data,72 const lldb::offset_t data_offset,73 const uint8_t op) const override {74 return LLDB_INVALID_OFFSET;75 }76 77 bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes,78 lldb::offset_t &offset, RegisterContext *reg_ctx,79 lldb::RegisterKind reg_kind,80 DWARFExpression::Stack &stack) const override {81 return false;82 }83 84private:85 uint16_t m_dwarf_version;86};87 88/// Mock memory implementation for testing.89/// Stores predefined memory contents indexed by {address, size} pairs.90class MockMemory {91public:92 /// Represents a memory read request with an address and size.93 /// Used as a key in the memory map to look up predefined test data.94 struct Request {95 lldb::addr_t addr;96 size_t size;97 98 bool operator==(const Request &other) const {99 return addr == other.addr && size == other.size;100 }101 102 /// Hash function for Request to enable its use in unordered_map.103 struct Hash {104 size_t operator()(const Request &req) const {105 size_t h1 = std::hash<lldb::addr_t>{}(req.addr);106 size_t h2 = std::hash<size_t>{}(req.size);107 return h1 ^ (h2 << 1);108 }109 };110 };111 112 typedef std::unordered_map<Request, std::vector<uint8_t>, Request::Hash> Map;113 MockMemory() = default;114 MockMemory(Map memory) : m_memory(std::move(memory)) {115 // Make sure the requested memory size matches the returned value.116 for ([[maybe_unused]] auto &[req, bytes] : m_memory) {117 assert(bytes.size() == req.size);118 }119 }120 121 llvm::Expected<std::vector<uint8_t>> ReadMemory(lldb::addr_t addr,122 size_t size) {123 if (!m_memory.count({addr, size})) {124 return llvm::createStringError(125 llvm::inconvertibleErrorCode(),126 "MockMemory::ReadMemory {address=0x%" PRIx64 ", size=%zu} not found",127 addr, size);128 }129 return m_memory[{addr, size}];130 }131 132private:133 std::unordered_map<Request, std::vector<uint8_t>, Request::Hash> m_memory;134};135 136/// A Process whose `ReadMemory` override queries MockMemory.137struct MockProcess : Process {138 using addr_t = lldb::addr_t;139 140 MockMemory m_memory;141 142 MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,143 MockMemory memory)144 : Process(target_sp, listener_sp), m_memory(std::move(memory)) {}145 size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size,146 Status &error) override {147 auto expected_memory = m_memory.ReadMemory(vm_addr, size);148 if (!expected_memory) {149 error = Status::FromError(expected_memory.takeError());150 return 0;151 }152 assert(expected_memory->size() == size);153 std::memcpy(buf, expected_memory->data(), expected_memory->size());154 return size;155 }156 size_t ReadMemory(addr_t addr, void *buf, size_t size,157 Status &status) override {158 return DoReadMemory(addr, buf, size, status);159 }160 bool CanDebug(lldb::TargetSP, bool) override { return true; }161 Status DoDestroy() override { return Status(); }162 llvm::StringRef GetPluginName() override { return ""; }163 void RefreshStateAfterStop() override {}164 bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; }165};166 167class MockThread : public Thread {168public:169 MockThread(Process &process) : Thread(process, /*tid=*/1), m_reg_ctx_sp() {}170 ~MockThread() override { DestroyThread(); }171 172 void RefreshStateAfterStop() override {}173 174 lldb::RegisterContextSP GetRegisterContext() override { return m_reg_ctx_sp; }175 176 lldb::RegisterContextSP177 CreateRegisterContextForFrame(StackFrame *frame) override {178 return m_reg_ctx_sp;179 }180 181 bool CalculateStopInfo() override { return false; }182 183 void SetRegisterContext(lldb::RegisterContextSP reg_ctx_sp) {184 m_reg_ctx_sp = reg_ctx_sp;185 }186 187private:188 lldb::RegisterContextSP m_reg_ctx_sp;189};190 191class MockRegisterContext : public RegisterContext {192public:193 MockRegisterContext(Thread &thread, const RegisterValue ®_value)194 : RegisterContext(thread, 0 /*concrete_frame_idx*/),195 m_reg_value(reg_value) {}196 197 void InvalidateAllRegisters() override {}198 199 size_t GetRegisterCount() override { return 0; }200 201 const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override {202 return &m_reg_info;203 }204 205 size_t GetRegisterSetCount() override { return 0; }206 207 const RegisterSet *GetRegisterSet(size_t reg_set) override { return nullptr; }208 209 lldb::ByteOrder GetByteOrder() override {210 return lldb::ByteOrder::eByteOrderLittle;211 }212 213 bool ReadRegister(const RegisterInfo *reg_info,214 RegisterValue ®_value) override {215 reg_value = m_reg_value;216 return true;217 }218 219 bool WriteRegister(const RegisterInfo *reg_info,220 const RegisterValue ®_value) override {221 return false;222 }223 224 uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,225 uint32_t num) override {226 return num;227 }228 229private:230 RegisterInfo m_reg_info{};231 RegisterValue m_reg_value{};232};233} // namespace234 235static llvm::Expected<Value> Evaluate(llvm::ArrayRef<uint8_t> expr,236 lldb::ModuleSP module_sp = {},237 DWARFExpression::Delegate *unit = nullptr,238 ExecutionContext *exe_ctx = nullptr,239 RegisterContext *reg_ctx = nullptr) {240 DataExtractor extractor(241 expr.data(), expr.size(), lldb::eByteOrderLittle,242 /*addr_size*/ exe_ctx ? exe_ctx->GetAddressByteSize() : 4);243 244 return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, extractor, unit,245 lldb::eRegisterKindLLDB,246 /*initial_value_ptr=*/nullptr,247 /*object_address_ptr=*/nullptr);248}249 250class DWARFExpressionTester : public YAMLModuleTester {251public:252 DWARFExpressionTester(llvm::StringRef yaml_data, size_t cu_index)253 : YAMLModuleTester(yaml_data, cu_index) {}254 255 using YAMLModuleTester::YAMLModuleTester;256 llvm::Expected<Value> Eval(llvm::ArrayRef<uint8_t> expr) {257 return ::Evaluate(expr, m_module_sp, m_dwarf_unit);258 }259};260 261/// This is needed for the tests that use a mock process.262class DWARFExpressionMockProcessTest : public ::testing::Test {263public:264 void SetUp() override {265 FileSystem::Initialize();266 HostInfo::Initialize();267 platform_linux::PlatformLinux::Initialize();268 }269 void TearDown() override {270 platform_linux::PlatformLinux::Terminate();271 HostInfo::Terminate();272 FileSystem::Terminate();273 }274};275 276/// Mock target implementation for testing.277/// Provides predefined memory contents via MockMemory instead of reading from278/// a real process.279class MockTarget : public Target {280public:281 MockTarget(Debugger &debugger, const ArchSpec &target_arch,282 const lldb::PlatformSP &platform_sp, MockMemory memory)283 : Target(debugger, target_arch, platform_sp, true),284 m_memory(std::move(memory)) {}285 286 size_t ReadMemory(const Address &addr, void *dst, size_t dst_len,287 Status &error, bool force_live_memory = false,288 lldb::addr_t *load_addr_ptr = nullptr,289 bool *did_read_live_memory = nullptr) override {290 auto expected_memory = m_memory.ReadMemory(addr.GetOffset(), dst_len);291 if (!expected_memory) {292 error = Status::FromError(expected_memory.takeError());293 return 0;294 }295 const size_t bytes_read = expected_memory->size();296 assert(bytes_read <= dst_len);297 std::memcpy(dst, expected_memory->data(), bytes_read);298 return bytes_read;299 }300 301private:302 MockMemory m_memory;303};304 305struct TestContext {306 lldb::PlatformSP platform_sp;307 lldb::TargetSP target_sp;308 lldb::DebuggerSP debugger_sp;309 lldb::ProcessSP process_sp;310 lldb::ThreadSP thread_sp;311 lldb::RegisterContextSP reg_ctx_sp;312};313 314/// A helper function to create TestContext objects with the315/// given triple, memory, and register contents.316static bool CreateTestContext(TestContext *ctx, llvm::StringRef triple,317 std::optional<RegisterValue> reg_value = {},318 std::optional<MockMemory> process_memory = {},319 std::optional<MockMemory> target_memory = {}) {320 ArchSpec arch(triple);321 lldb::PlatformSP platform_sp =322 platform_linux::PlatformLinux::CreateInstance(true, &arch);323 Platform::SetHostPlatform(platform_sp);324 lldb::TargetSP target_sp;325 lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();326 327 Status status;328 if (target_memory)329 target_sp = std::make_shared<MockTarget>(*debugger_sp, arch, platform_sp,330 std::move(*target_memory));331 else332 status = debugger_sp->GetTargetList().CreateTarget(333 *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);334 335 EXPECT_TRUE(status.Success());336 if (!status.Success())337 return false;338 339 lldb::ProcessSP process_sp;340 if (!process_memory)341 process_memory = MockMemory();342 process_sp = std::make_shared<MockProcess>(343 target_sp, Listener::MakeListener("dummy"), std::move(*process_memory));344 345 auto thread_sp = std::make_shared<MockThread>(*process_sp);346 347 process_sp->GetThreadList().AddThread(thread_sp);348 349 lldb::RegisterContextSP reg_ctx_sp;350 if (reg_value) {351 reg_ctx_sp = std::make_shared<MockRegisterContext>(*thread_sp, *reg_value);352 thread_sp->SetRegisterContext(reg_ctx_sp);353 }354 355 *ctx = TestContext{platform_sp, target_sp, debugger_sp,356 process_sp, thread_sp, reg_ctx_sp};357 return true;358}359 360TEST(DWARFExpression, DW_OP_pick) {361 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 0}),362 ExpectScalar(0));363 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 1}),364 ExpectScalar(1));365 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1, DW_OP_lit0, DW_OP_pick, 2}),366 llvm::Failed());367}368 369TEST(DWARFExpression, DW_OP_const) {370 // Extend to address size.371 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x88}), ExpectScalar(0x88));372 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1s, 0x88}),373 ExpectScalar(0xffffff88));374 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x47, 0x88}),375 ExpectScalar(0x8847));376 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2s, 0x47, 0x88}),377 ExpectScalar(0xffff8847));378 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4u, 0x44, 0x42, 0x47, 0x88}),379 ExpectScalar(0x88474244));380 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const4s, 0x44, 0x42, 0x47, 0x88}),381 ExpectScalar(0x88474244));382 383 // Truncate to address size.384 EXPECT_THAT_EXPECTED(385 Evaluate({DW_OP_const8u, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),386 ExpectScalar(0x33221100));387 EXPECT_THAT_EXPECTED(388 Evaluate({DW_OP_const8s, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}),389 ExpectScalar(0x33221100));390 391 // Don't truncate to address size for compatibility with clang (pr48087).392 EXPECT_THAT_EXPECTED(393 Evaluate({DW_OP_constu, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),394 ExpectScalar(0x01010101010101));395 EXPECT_THAT_EXPECTED(396 Evaluate({DW_OP_consts, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}),397 ExpectScalar(0xffff010101010101));398}399 400TEST(DWARFExpression, DW_OP_skip) {401 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const1u, 0x42, DW_OP_skip, 0x02, 0x00,402 DW_OP_const1u, 0xff}),403 ExpectScalar(0x42));404}405 406TEST(DWARFExpression, DW_OP_bra) {407 EXPECT_THAT_EXPECTED(408 // clang-format off409 Evaluate({410 DW_OP_const1u, 0x42, // push 0x42411 DW_OP_const1u, 0x1, // push 0x1412 DW_OP_bra, 0x02, 0x00, // if 0x1 > 0, then skip 0x0002 opcodes413 DW_OP_const1u, 0xff, // push 0xff414 }),415 // clang-format on416 ExpectScalar(0x42));417 418 EXPECT_THAT_ERROR(Evaluate({DW_OP_bra, 0x01, 0x00}).takeError(),419 llvm::Failed());420}421 422TEST(DWARFExpression, DW_OP_convert) {423 /// Auxiliary debug info.424 const char *yamldata = R"(425--- !ELF426FileHeader:427 Class: ELFCLASS64428 Data: ELFDATA2LSB429 Type: ET_EXEC430 Machine: EM_386431DWARF:432 debug_abbrev:433 - Table:434 - Code: 0x00000001435 Tag: DW_TAG_compile_unit436 Children: DW_CHILDREN_yes437 Attributes:438 - Attribute: DW_AT_language439 Form: DW_FORM_data2440 - Code: 0x00000002441 Tag: DW_TAG_base_type442 Children: DW_CHILDREN_no443 Attributes:444 - Attribute: DW_AT_encoding445 Form: DW_FORM_data1446 - Attribute: DW_AT_byte_size447 Form: DW_FORM_data1448 debug_info:449 - Version: 4450 AddrSize: 8451 AbbrevTableID: 0452 AbbrOffset: 0x0453 Entries:454 - AbbrCode: 0x00000001455 Values:456 - Value: 0x000000000000000C457 - AbbrCode: 0x00000000458 - Version: 4459 AddrSize: 8460 AbbrevTableID: 0461 AbbrOffset: 0x0462 Entries:463 - AbbrCode: 0x00000001464 Values:465 - Value: 0x000000000000000C466 # 0x0000000e:467 - AbbrCode: 0x00000002468 Values:469 - Value: 0x0000000000000007 # DW_ATE_unsigned470 - Value: 0x0000000000000004471 # 0x00000011:472 - AbbrCode: 0x00000002473 Values:474 - Value: 0x0000000000000007 # DW_ATE_unsigned475 - Value: 0x0000000000000008476 # 0x00000014:477 - AbbrCode: 0x00000002478 Values:479 - Value: 0x0000000000000005 # DW_ATE_signed480 - Value: 0x0000000000000008481 # 0x00000017:482 - AbbrCode: 0x00000002483 Values:484 - Value: 0x0000000000000008 # DW_ATE_unsigned_char485 - Value: 0x0000000000000001486 # 0x0000001a:487 - AbbrCode: 0x00000002488 Values:489 - Value: 0x0000000000000006 # DW_ATE_signed_char490 - Value: 0x0000000000000001491 # 0x0000001d:492 - AbbrCode: 0x00000002493 Values:494 - Value: 0x000000000000000b # DW_ATE_numeric_string495 - Value: 0x0000000000000001496 - AbbrCode: 0x00000000497 498)";499 // Compile unit relative offsets to each DW_TAG_base_type500 uint8_t offs_uint32_t = 0x0000000e;501 uint8_t offs_uint64_t = 0x00000011;502 uint8_t offs_sint64_t = 0x00000014;503 uint8_t offs_uchar = 0x00000017;504 uint8_t offs_schar = 0x0000001a;505 506 DWARFExpressionTester t(yamldata, /*cu_index=*/1);507 ASSERT_TRUE((bool)t.GetDwarfUnit());508 509 // Constant is given as little-endian.510 bool is_signed = true;511 bool not_signed = false;512 513 //514 // Positive tests.515 //516 517 // Leave as is.518 EXPECT_THAT_EXPECTED(519 t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //520 DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),521 ExpectScalar(64, 0x44332211, not_signed));522 523 // Zero-extend to 64 bits.524 EXPECT_THAT_EXPECTED(525 t.Eval({DW_OP_const4u, 0x11, 0x22, 0x33, 0x44, //526 DW_OP_convert, offs_uint64_t, DW_OP_stack_value}),527 ExpectScalar(64, 0x44332211, not_signed));528 529 // Sign-extend to 64 bits.530 EXPECT_THAT_EXPECTED(531 t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //532 DW_OP_convert, offs_sint64_t, DW_OP_stack_value}),533 ExpectScalar(64, 0xffffffffffeeddcc, is_signed));534 535 // Sign-extend, then truncate.536 EXPECT_THAT_EXPECTED(537 t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //538 DW_OP_convert, offs_sint64_t, //539 DW_OP_convert, offs_uint32_t, DW_OP_stack_value}),540 ExpectScalar(32, 0xffeeddcc, not_signed));541 542 // Truncate to default unspecified (pointer-sized) type.543 EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 0xcc, 0xdd, 0xee, 0xff, //544 DW_OP_convert, offs_sint64_t, //545 DW_OP_convert, 0x00, DW_OP_stack_value}),546 ExpectScalar(32, 0xffeeddcc, not_signed));547 548 // Truncate to 8 bits.549 EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,550 offs_uchar, DW_OP_stack_value}),551 ExpectScalar(8, 'A', not_signed));552 553 // Also truncate to 8 bits.554 EXPECT_THAT_EXPECTED(t.Eval({DW_OP_const4s, 'A', 'B', 'C', 'D', DW_OP_convert,555 offs_schar, DW_OP_stack_value}),556 ExpectScalar(8, 'A', is_signed));557 558 //559 // Errors.560 //561 562 // No Module.563 EXPECT_THAT_ERROR(Evaluate({DW_OP_const1s, 'X', DW_OP_convert, 0x00}, nullptr,564 t.GetDwarfUnit())565 .takeError(),566 llvm::Failed());567 568 // No DIE.569 EXPECT_THAT_ERROR(570 t.Eval({DW_OP_const1s, 'X', DW_OP_convert, 0x01}).takeError(),571 llvm::Failed());572 573 // Unsupported.574 EXPECT_THAT_ERROR(575 t.Eval({DW_OP_const1s, 'X', DW_OP_convert, 0x1d}).takeError(),576 llvm::Failed());577}578 579TEST(DWARFExpression, DW_OP_stack_value) {580 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_stack_value}), llvm::Failed());581}582 583// This test shows that the dwarf version is used by the expression evaluation.584// Note that the different behavior tested here is not meant to imply that this585// is the correct interpretation of dwarf2 vs. dwarf5, but rather it was picked586// as an easy example that evaluates differently based on the dwarf version.587TEST(DWARFExpression, dwarf_version) {588 MockDwarfDelegate dwarf2 = MockDwarfDelegate::Dwarf2();589 MockDwarfDelegate dwarf5 = MockDwarfDelegate::Dwarf5();590 591 // In dwarf2 the constant on top of the stack is treated as a value.592 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1}, {}, &dwarf2), ExpectScalar(1));593 594 // In dwarf5 the constant on top of the stack is implicitly converted to an595 // address.596 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit1}, {}, &dwarf5),597 ExpectLoadAddress(1));598}599 600TEST(DWARFExpression, DW_OP_piece) {601 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_const2u, 0x11, 0x22, DW_OP_piece, 2,602 DW_OP_const2u, 0x33, 0x44, DW_OP_piece, 2}),603 ExpectHostAddress({0x11, 0x22, 0x33, 0x44}));604 EXPECT_THAT_EXPECTED(605 Evaluate({DW_OP_piece, 1, DW_OP_const1u, 0xff, DW_OP_piece, 1}),606 // Note that the "00" should really be "undef", but we can't607 // represent that yet.608 ExpectHostAddress({0x00, 0xff}));609 610 // This tests if ap_int is extended to the right width.611 // expect 40*8 = 320 bits size.612 std::vector<uint8_t> expected_host_buffer(40, 0);613 expected_host_buffer[0] = 2;614 615 EXPECT_THAT_EXPECTED(616 Evaluate({{DW_OP_lit2, DW_OP_stack_value, DW_OP_piece, 40}}),617 ExpectHostAddress(expected_host_buffer));618}619 620TEST(DWARFExpression, DW_OP_implicit_value) {621 unsigned char bytes = 4;622 623 EXPECT_THAT_EXPECTED(624 Evaluate({DW_OP_implicit_value, bytes, 0x11, 0x22, 0x33, 0x44}),625 ExpectHostAddress({0x11, 0x22, 0x33, 0x44}));626}627 628TEST(DWARFExpression, DW_OP_unknown) {629 EXPECT_THAT_EXPECTED(630 Evaluate({0xff}),631 llvm::FailedWithMessage(632 "Unhandled opcode DW_OP_unknown_ff in DWARFExpression"));633}634 635TEST_F(DWARFExpressionMockProcessTest, DW_OP_deref) {636 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit0, DW_OP_deref}), llvm::Failed());637 638 // Set up a mock process.639 MockMemory::Map memory = {640 {{0x4, 4}, {0x4, 0x5, 0x6, 0x7}},641 };642 TestContext test_ctx;643 ASSERT_TRUE(644 CreateTestContext(&test_ctx, "i386-pc-linux", {}, std::move(memory)));645 646 ExecutionContext exe_ctx(test_ctx.process_sp);647 // Implicit location: *0x4.648 EXPECT_THAT_EXPECTED(649 Evaluate({DW_OP_lit4, DW_OP_deref, DW_OP_stack_value}, {}, {}, &exe_ctx),650 ExpectScalar(32, 0x07060504, false));651 // Memory location: *(*0x4).652 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4, DW_OP_deref}, {}, {}, &exe_ctx),653 ExpectLoadAddress(0x07060504));654 // Memory location: *0x4.655 EXPECT_THAT_EXPECTED(Evaluate({DW_OP_lit4}, {}, {}, &exe_ctx),656 ExpectScalar(Scalar(4)));657}658 659TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr) {660 // Set up a wasm target661 TestContext test_ctx;662 ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm"));663 664 ExecutionContext exe_ctx(test_ctx.target_sp, false);665 // DW_OP_addr takes a single operand of address size width:666 EXPECT_THAT_EXPECTED(667 Evaluate({DW_OP_addr, 0x40, 0x0, 0x0, 0x0}, {}, {}, &exe_ctx),668 ExpectLoadAddress(0x40));669}670 671TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) {672 const char *yamldata = R"(673--- !ELF674FileHeader:675 Class: ELFCLASS64676 Data: ELFDATA2LSB677 Type: ET_EXEC678 Machine: EM_386679DWARF:680 debug_abbrev:681 - Table:682 - Code: 0x00000001683 Tag: DW_TAG_compile_unit684 Children: DW_CHILDREN_no685 Attributes:686 - Attribute: DW_AT_addr_base687 Form: DW_FORM_sec_offset688 689 debug_info:690 - Version: 5691 AddrSize: 4692 UnitType: DW_UT_compile693 Entries:694 - AbbrCode: 0x00000001695 Values:696 - Value: 0x8 # Offset of the first Address past the header697 - AbbrCode: 0x0698 699 debug_addr:700 - Version: 5701 AddressSize: 4702 Entries:703 - Address: 0x1234704 - Address: 0x5678705)";706 707 // Can't use DWARFExpressionTester from above because subsystems overlap with708 // the fixture.709 SubsystemRAII<ObjectFileELF, SymbolFileDWARF> subsystems;710 llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata);711 EXPECT_THAT_EXPECTED(file, llvm::Succeeded());712 auto module_sp = std::make_shared<Module>(file->moduleSpec());713 auto *dwarf_cu = llvm::cast<SymbolFileDWARF>(module_sp->GetSymbolFile())714 ->DebugInfo()715 .GetUnitAtIndex(0);716 ASSERT_TRUE(dwarf_cu);717 dwarf_cu->ExtractDIEsIfNeeded();718 719 // Set up a wasm target720 TestContext test_ctx;721 ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm"));722 ExecutionContext exe_ctx(test_ctx.target_sp, false);723 724 auto evaluate = [&](DWARFExpression &expr) -> llvm::Expected<Value> {725 DataExtractor extractor;726 expr.GetExpressionData(extractor);727 return DWARFExpression::Evaluate(&exe_ctx, /*reg_ctx*/ nullptr,728 /*module_sp*/ {}, extractor, dwarf_cu,729 lldb::eRegisterKindLLDB,730 /*initial_value_ptr*/ nullptr,731 /*object_address_ptr*/ nullptr);732 };733 734 // DW_OP_addrx takes a single leb128 operand, the index in the addr table:735 uint8_t expr_data[] = {DW_OP_addrx, 0x01};736 DataExtractor extractor(expr_data, sizeof(expr_data), lldb::eByteOrderLittle,737 /*addr_size*/ 4);738 DWARFExpression expr(extractor);739 740 llvm::Expected<Value> result = evaluate(expr);741 EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0x5678u));742 743 ASSERT_TRUE(expr.Update_DW_OP_addr(dwarf_cu, 0xdeadbeef));744 result = evaluate(expr);745 EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0xdeadbeefu));746}747 748class CustomSymbolFileDWARF : public SymbolFileDWARF {749 static char ID;750 751public:752 using SymbolFileDWARF::SymbolFileDWARF;753 754 bool isA(const void *ClassID) const override {755 return ClassID == &ID || SymbolFile::isA(ClassID);756 }757 static bool classof(const SymbolFile *obj) { return obj->isA(&ID); }758 759 static llvm::StringRef GetPluginNameStatic() { return "custom_dwarf"; }760 761 static llvm::StringRef GetPluginDescriptionStatic() {762 return "Symbol file reader with expression extensions.";763 }764 765 static void Initialize() {766 PluginManager::RegisterPlugin(GetPluginNameStatic(),767 GetPluginDescriptionStatic(), CreateInstance,768 SymbolFileDWARF::DebuggerInitialize);769 }770 771 static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }772 773 static lldb_private::SymbolFile *774 CreateInstance(lldb::ObjectFileSP objfile_sp) {775 return new CustomSymbolFileDWARF(std::move(objfile_sp),776 /*dwo_section_list*/ nullptr);777 }778 779 lldb::offset_t780 GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor &data,781 const lldb::offset_t data_offset,782 const uint8_t op) const final {783 auto offset = data_offset;784 if (op != DW_OP_WASM_location) {785 return LLDB_INVALID_OFFSET;786 }787 788 // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32789 // Called with "arguments" 0x03 and 0x04790 // Location type:791 if (data.GetU8(&offset) != /* global */ 0x03) {792 return LLDB_INVALID_OFFSET;793 }794 795 // Index796 if (data.GetU32(&offset) != 0x04) {797 return LLDB_INVALID_OFFSET;798 }799 800 // Report the skipped distance:801 return offset - data_offset;802 }803 804 virtual bool ParseVendorDWARFOpcode(805 uint8_t op, const lldb_private::DataExtractor &opcodes,806 lldb::offset_t &offset,807 808 RegisterContext *reg_ctx, lldb::RegisterKind reg_kind,809 std::vector<lldb_private::Value> &stack) const override {810 if (op != DW_OP_WASM_location) {811 return false;812 }813 814 // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32815 // Called with "arguments" 0x03 and 0x04816 // Location type:817 if (opcodes.GetU8(&offset) != /* global */ 0x03) {818 return false;819 }820 821 // Index:822 if (opcodes.GetU32(&offset) != 0x04) {823 return false;824 }825 826 // Return some value:827 stack.push_back({GetScalar(32, 42, false)});828 return true;829 }830};831 832char CustomSymbolFileDWARF::ID;833 834static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp,835 DWARFUnit &dwarf_unit,836 RegisterContext *reg_ctx) {837 // Test that expression extensions can be evaluated, for example838 // DW_OP_WASM_location which is not currently handled by DWARFExpression:839 EXPECT_THAT_EXPECTED(840 Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03841 0x04, 0x00, 0x00, // index:u32842 0x00, DW_OP_stack_value},843 module_sp, &dwarf_unit, nullptr, reg_ctx),844 ExpectScalar(32, 42, false, Value::ContextType::RegisterInfo));845 846 // Test that searches for opcodes work in the presence of extensions:847 uint8_t expr[] = {DW_OP_WASM_location, 0x03, 0x04, 0x00, 0x00, 0x00,848 DW_OP_form_tls_address};849 DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle,850 /*addr_size*/ 4);851 DWARFExpression dwarf_expr(extractor);852 ASSERT_TRUE(dwarf_expr.ContainsThreadLocalStorage(&dwarf_unit));853}854 855TEST(DWARFExpression, Extensions) {856 const char *yamldata = R"(857--- !WASM858FileHeader:859 Version: 0x1860Sections:861 - Type: TYPE862 Signatures:863 - Index: 0864 ParamTypes:865 - I32866 ReturnTypes:867 - I32868 - Type: FUNCTION869 FunctionTypes: [ 0 ]870 - Type: TABLE871 Tables:872 - Index: 0873 ElemType: FUNCREF874 Limits:875 Flags: [ HAS_MAX ]876 Minimum: 0x1877 Maximum: 0x1878 - Type: MEMORY879 Memories:880 - Flags: [ HAS_MAX ]881 Minimum: 0x100882 Maximum: 0x100883 - Type: GLOBAL884 Globals:885 - Index: 0886 Type: I32887 Mutable: true888 InitExpr:889 Opcode: I32_CONST890 Value: 65536891 - Type: EXPORT892 Exports:893 - Name: memory894 Kind: MEMORY895 Index: 0896 - Name: square897 Kind: FUNCTION898 Index: 0899 - Name: __indirect_function_table900 Kind: TABLE901 Index: 0902 - Type: CODE903 Functions:904 - Index: 0905 Locals:906 - Type: I32907 Count: 1908 - Type: I32909 Count: 1910 - Type: I32911 Count: 1912 - Type: I32913 Count: 1914 - Type: I32915 Count: 1916 - Type: I32917 Count: 1918 Body: 2300210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B919 - Type: CUSTOM920 Name: name921 FunctionNames:922 - Index: 0923 Name: square924 GlobalNames:925 - Index: 0926 Name: __stack_pointer927 - Type: CUSTOM928 Name: .debug_abbrev929 Payload: 011101250E1305030E10171B0E110112060000022E01110112064018030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042400030E3E0B0B0B000000930 - Type: CUSTOM931 Name: .debug_info932 Payload: 510000000400000000000401670000001D005E000000000000000A000000020000003C00000002020000003C00000004ED00039F5700000001014D0000000302910C0400000001014D000000000400000000050400933 - Type: CUSTOM934 Name: .debug_str935 Payload: 696E740076616C756500513A5C70616F6C6F7365764D5346545C6C6C766D2D70726F6A6563745C6C6C64625C746573745C4150495C66756E6374696F6E616C69746965735C6764625F72656D6F74655F636C69656E745C737175617265007371756172652E6300636C616E672076657273696F6E2031382E302E30202868747470733A2F2F6769746875622E636F6D2F6C6C766D2F6C6C766D2D70726F6A65637420373535303166353336323464653932616166636532663164613639386232343961373239336463372900936 - Type: CUSTOM937 Name: .debug_line938 Payload: 64000000040020000000010101FB0E0D000101010100000001000001007371756172652E6300000000000005020200000001000502250000000301050A0A010005022C00000005120601000502330000000510010005023A0000000503010005023E000000000101939)";940 941 SubsystemRAII<FileSystem, HostInfo, ObjectFileWasm, SymbolVendorWasm>942 subsystems;943 944 // Set up a wasm target.945 TestContext test_ctx;946 const uint32_t kExpectedValue = 42;947 ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm",948 RegisterValue(kExpectedValue)));949 950 llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata);951 EXPECT_THAT_EXPECTED(file, llvm::Succeeded());952 auto module_sp = std::make_shared<Module>(file->moduleSpec());953 auto obj_file_sp = module_sp->GetObjectFile()->shared_from_this();954 SymbolFileWasm sym_file_wasm(obj_file_sp, nullptr);955 auto *dwarf_unit = sym_file_wasm.DebugInfo().GetUnitAtIndex(0);956 957 testExpressionVendorExtensions(module_sp, *dwarf_unit,958 test_ctx.reg_ctx_sp.get());959}960 961TEST(DWARFExpression, ExtensionsSplitSymbols) {962 const char *skeleton_yamldata = R"(963--- !WASM964FileHeader:965 Version: 0x1966Sections:967 - Type: TYPE968 Signatures:969 - Index: 0970 ParamTypes:971 - I32972 ReturnTypes:973 - I32974 - Type: FUNCTION975 FunctionTypes: [ 0 ]976 - Type: TABLE977 Tables:978 - Index: 0979 ElemType: FUNCREF980 Limits:981 Flags: [ HAS_MAX ]982 Minimum: 0x1983 Maximum: 0x1984 - Type: MEMORY985 Memories:986 - Flags: [ HAS_MAX ]987 Minimum: 0x100988 Maximum: 0x100989 - Type: GLOBAL990 Globals:991 - Index: 0992 Type: I32993 Mutable: true994 InitExpr:995 Opcode: I32_CONST996 Value: 65536997 - Type: EXPORT998 Exports:999 - Name: memory1000 Kind: MEMORY1001 Index: 01002 - Name: square1003 Kind: FUNCTION1004 Index: 01005 - Name: __indirect_function_table1006 Kind: TABLE1007 Index: 01008 - Type: CODE1009 Functions:1010 - Index: 01011 Locals:1012 - Type: I321013 Count: 11014 - Type: I321015 Count: 11016 - Type: I321017 Count: 11018 - Type: I321019 Count: 11020 - Type: I321021 Count: 11022 - Type: I321023 Count: 11024 Body: 2300210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B1025 - Type: CUSTOM1026 Name: name1027 FunctionNames:1028 - Index: 01029 Name: square1030 GlobalNames:1031 - Index: 01032 Name: __stack_pointer1033 - Type: CUSTOM1034 Name: external_debug_info1035 Payload: 167371756172652E7761736D2E64656275672E7761736D1036)";1037 1038 const char *sym_yamldata = R"(1039--- !WASM1040FileHeader:1041 Version: 0x11042Sections:1043 - Type: TYPE1044 Signatures:1045 - Index: 01046 ParamTypes:1047 - I321048 ReturnTypes:1049 - I321050 - Type: FUNCTION1051 FunctionTypes: [ 0 ]1052 - Type: TABLE1053 Tables:1054 - Index: 01055 ElemType: FUNCREF1056 Limits:1057 Flags: [ HAS_MAX ]1058 Minimum: 0x11059 Maximum: 0x11060 - Type: MEMORY1061 Memories:1062 - Flags: [ HAS_MAX ]1063 Minimum: 0x1001064 Maximum: 0x1001065 - Type: GLOBAL1066 Globals:1067 - Index: 01068 Type: I321069 Mutable: true1070 InitExpr:1071 Opcode: I32_CONST1072 Value: 655361073 - Type: EXPORT1074 Exports:1075 - Name: memory1076 Kind: MEMORY1077 Index: 01078 - Name: square1079 Kind: FUNCTION1080 Index: 01081 - Name: __indirect_function_table1082 Kind: TABLE1083 Index: 01084 - Type: CODE1085 Functions:1086 - Index: 01087 Locals:1088 - Type: I321089 Count: 11090 - Type: I321091 Count: 11092 - Type: I321093 Count: 11094 - Type: I321095 Count: 11096 - Type: I321097 Count: 11098 - Type: I321099 Count: 11100 Body: 2300210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B1101 - Type: CUSTOM1102 Name: name1103 FunctionNames:1104 - Index: 01105 Name: square1106 GlobalNames:1107 - Index: 01108 Name: __stack_pointer1109 - Type: CUSTOM1110 Name: .debug_abbrev1111 Payload: 011101250E1305030E10171B0E110112060000022E01110112064018030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042400030E3E0B0B0B0000001112 - Type: CUSTOM1113 Name: .debug_info1114 Payload: 510000000400000000000401670000001D005E0000000000000004000000020000003C00000002020000003C00000004ED00039F5700000001014D0000000302910C5100000001014D0000000004000000000504001115 - Type: CUSTOM1116 Name: .debug_str1117 Payload: 696E7400513A5C70616F6C6F7365764D5346545C6C6C766D2D70726F6A6563745C6C6C64625C746573745C4150495C66756E6374696F6E616C69746965735C6764625F72656D6F74655F636C69656E740076616C756500737175617265007371756172652E6300636C616E672076657273696F6E2031382E302E30202868747470733A2F2F6769746875622E636F6D2F6C6C766D2F6C6C766D2D70726F6A656374203735353031663533363234646539326161666365326631646136393862323439613732393364633729001118 - Type: CUSTOM1119 Name: .debug_line1120 Payload: 64000000040020000000010101FB0E0D000101010100000001000001007371756172652E6300000000000005020200000001000502250000000301050A0A010005022C00000005120601000502330000000510010005023A0000000503010005023E0000000001011121)";1122 1123 SubsystemRAII<FileSystem, HostInfo, ObjectFileWasm, SymbolVendorWasm>1124 subsystems;1125 1126 // Set up a wasm target.1127 TestContext test_ctx;1128 const uint32_t kExpectedValue = 42;1129 ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm",1130 RegisterValue(kExpectedValue)));1131 1132 llvm::Expected<TestFile> skeleton_file =1133 TestFile::fromYaml(skeleton_yamldata);1134 EXPECT_THAT_EXPECTED(skeleton_file, llvm::Succeeded());1135 auto skeleton_module_sp =1136 std::make_shared<Module>(skeleton_file->moduleSpec());1137 1138 llvm::Expected<TestFile> sym_file = TestFile::fromYaml(sym_yamldata);1139 EXPECT_THAT_EXPECTED(sym_file, llvm::Succeeded());1140 auto sym_module_sp = std::make_shared<Module>(sym_file->moduleSpec());1141 1142 auto obj_file_sp = sym_module_sp->GetObjectFile()->shared_from_this();1143 SymbolFileWasm sym_file_wasm(obj_file_sp, nullptr);1144 auto *dwarf_unit = sym_file_wasm.DebugInfo().GetUnitAtIndex(0);1145 1146 testExpressionVendorExtensions(sym_module_sp, *dwarf_unit,1147 test_ctx.reg_ctx_sp.get());1148}1149 1150TEST_F(DWARFExpressionMockProcessTest, DW_OP_piece_file_addr) {1151 using ::testing::ByMove;1152 using ::testing::ElementsAre;1153 using ::testing::Return;1154 1155 // Set up a mock process.1156 TestContext test_ctx;1157 MockMemory::Map memory = {1158 {{0x40, 1}, {0x11}},1159 {{0x50, 1}, {0x22}},1160 };1161 ASSERT_TRUE(1162 CreateTestContext(&test_ctx, "i386-pc-linux", {}, {}, std::move(memory)));1163 ASSERT_TRUE(test_ctx.target_sp->GetArchitecture().IsValid());1164 1165 ExecutionContext exe_ctx(test_ctx.target_sp, false);1166 1167 uint8_t expr[] = {DW_OP_addr, 0x40, 0x0, 0x0, 0x0, DW_OP_piece, 1,1168 DW_OP_addr, 0x50, 0x0, 0x0, 0x0, DW_OP_piece, 1};1169 EXPECT_THAT_EXPECTED(Evaluate(expr, {}, {}, &exe_ctx),1170 ExpectHostAddress({0x11, 0x22}));1171}1172 1173class DWARFExpressionMockProcessTestWithAArch1174 : public DWARFExpressionMockProcessTest {1175public:1176 void SetUp() override {1177 DWARFExpressionMockProcessTest::SetUp();1178#ifdef ARCH_AARCH641179 LLVMInitializeAArch64TargetInfo();1180 LLVMInitializeAArch64TargetMC();1181 ABISysV_arm64::Initialize();1182#endif1183 }1184 void TearDown() override {1185 DWARFExpressionMockProcessTest::TearDown();1186#ifdef ARCH_AARCH641187 ABISysV_arm64::Terminate();1188#endif1189 }1190};1191 1192/// Sets the value of register x22 to "42".1193/// Creates a process whose memory address 42 contains the value1194/// memory[42] = ((0xffULL) << 56) | 0xabcdef;1195/// The expression DW_OP_breg22, 0, DW_OP_deref should produce that same value,1196/// without clearing the top byte 0xff.1197TEST_F(DWARFExpressionMockProcessTestWithAArch, DW_op_deref_no_ptr_fixing) {1198 constexpr lldb::addr_t expected_value = ((0xffULL) << 56) | 0xabcdefULL;1199 constexpr lldb::addr_t addr = 42;1200 MockMemory::Map memory = {1201 {{addr, sizeof(addr)}, {0xef, 0xcd, 0xab, 0x00, 0x00, 0x00, 0x00, 0xff}}};1202 1203 TestContext test_ctx;1204 ASSERT_TRUE(CreateTestContext(&test_ctx, "aarch64-pc-linux",1205 RegisterValue(addr), std::move(memory)));1206 1207 auto evaluate_expr = [&](auto &expr_data) {1208 ExecutionContext exe_ctx(test_ctx.process_sp);1209 return Evaluate(expr_data, {}, {}, &exe_ctx, test_ctx.reg_ctx_sp.get());1210 };1211 1212 uint8_t expr_reg[] = {DW_OP_breg22, 0};1213 llvm::Expected<Value> result_reg = evaluate_expr(expr_reg);1214 EXPECT_THAT_EXPECTED(result_reg, ExpectLoadAddress(addr));1215 1216 uint8_t expr_deref[] = {DW_OP_breg22, 0, DW_OP_deref};1217 llvm::Expected<Value> result_deref = evaluate_expr(expr_deref);1218 EXPECT_THAT_EXPECTED(result_deref, ExpectLoadAddress(expected_value));1219}1220