diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -75,14 +75,15 @@ lldb::addr_t GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu, uint32_t op_addr_idx, bool &error) const; - bool Update_DW_OP_addr(lldb::addr_t file_addr); + bool Update_DW_OP_addr(const DWARFUnit *dwarf_cu, lldb::addr_t file_addr); void UpdateValue(uint64_t const_value, lldb::offset_t const_value_byte_size, uint8_t addr_byte_size); - bool ContainsThreadLocalStorage() const; + bool ContainsThreadLocalStorage(const DWARFUnit *dwarf_cu) const; bool LinkThreadLocalStorage( + const DWARFUnit *dwarf_cu, std::function const &link_address_callback); diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -131,7 +131,7 @@ /// are made on the state of \p data after this call. static offset_t GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset, - const uint8_t op) { + const uint8_t op, const DWARFUnit *dwarf_cu) { lldb::offset_t offset = data_offset; switch (op) { case DW_OP_addr: @@ -333,9 +333,12 @@ } default: - break; + if (!dwarf_cu) { + return LLDB_INVALID_OFFSET; + } + return dwarf_cu->GetSymbolFileDWARF().GetVendorDWARFOpcodeSize( + data, data_offset, op); } - return LLDB_INVALID_OFFSET; } lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(const DWARFUnit *dwarf_cu, @@ -364,7 +367,8 @@ } ++curr_op_addr_idx; } else { - const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); + const offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) { error = true; break; @@ -375,7 +379,8 @@ return LLDB_INVALID_ADDRESS; } -bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) { +bool DWARFExpression::Update_DW_OP_addr(const DWARFUnit *dwarf_cu, + lldb::addr_t file_addr) { lldb::offset_t offset = 0; while (m_data.ValidOffset(offset)) { const uint8_t op = m_data.GetU8(&offset); @@ -402,7 +407,8 @@ m_data.SetData(encoder.GetDataBuffer()); return true; } else { - const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); + const offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) break; offset += op_arg_size; @@ -411,14 +417,16 @@ return false; } -bool DWARFExpression::ContainsThreadLocalStorage() const { +bool DWARFExpression::ContainsThreadLocalStorage( + const DWARFUnit *dwarf_cu) const { lldb::offset_t offset = 0; while (m_data.ValidOffset(offset)) { const uint8_t op = m_data.GetU8(&offset); if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address) return true; - const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); + const offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) return false; offset += op_arg_size; @@ -426,6 +434,7 @@ return false; } bool DWARFExpression::LinkThreadLocalStorage( + const DWARFUnit *dwarf_cu, std::function const &link_address_callback) { const uint32_t addr_byte_size = m_data.GetAddressByteSize(); @@ -496,7 +505,8 @@ } if (!decoded_data) { - const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op); + const offset_t op_arg_size = + GetOpcodeDataSize(m_data, offset, op, dwarf_cu); if (op_arg_size == LLDB_INVALID_OFFSET) return false; else @@ -2556,6 +2566,12 @@ } default: + if (dwarf_cu) { + if (dwarf_cu->GetSymbolFileDWARF().ParseVendorDWARFOpcode( + op, opcodes, offset, stack)) { + break; + } + } if (error_ptr) error_ptr->SetErrorStringWithFormatv( "Unhandled opcode {0} in DWARFExpression", LocationAtom(op)); diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp --- a/lldb/source/Expression/DWARFExpressionList.cpp +++ b/lldb/source/Expression/DWARFExpressionList.cpp @@ -90,7 +90,7 @@ return false; const DWARFExpression &expr = m_exprs.GetEntryRef(0).data; - return expr.ContainsThreadLocalStorage(); + return expr.ContainsThreadLocalStorage(m_dwarf_cu); } bool DWARFExpressionList::LinkThreadLocalStorage( @@ -107,7 +107,7 @@ // If we linked the TLS address correctly, update the module so that when the // expression is evaluated it can resolve the file address to a load address // and read the TLS data - if (expr.LinkThreadLocalStorage(link_address_callback)) + if (expr.LinkThreadLocalStorage(m_dwarf_cu, link_address_callback)) m_module_wp = new_module_sp; return true; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -330,6 +330,20 @@ return m_parse_time; } + virtual lldb::offset_t + GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) const { + return LLDB_INVALID_OFFSET; + } + + virtual bool + ParseVendorDWARFOpcode(uint8_t op, const lldb_private::DataExtractor &opcodes, + lldb::offset_t &offset, + std::vector &stack) const { + return false; + } + lldb_private::ConstString ConstructFunctionDemangledName(const DWARFDIE &die); protected: diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -3492,7 +3492,8 @@ if (exe_file_addr != LLDB_INVALID_ADDRESS) { DWARFExpression *location = location_list.GetMutableExpressionAtAddress(); - if (location->Update_DW_OP_addr(exe_file_addr)) { + if (location->Update_DW_OP_addr(die.GetCU(), + exe_file_addr)) { linked_oso_file_addr = true; symbol_context_scope = exe_symbol; } @@ -3512,7 +3513,7 @@ // Update the file address for this variable DWARFExpression *location = location_list.GetMutableExpressionAtAddress(); - location->Update_DW_OP_addr(exe_file_addr); + location->Update_DW_OP_addr(die.GetCU(), exe_file_addr); } else { // Variable didn't make it into the final executable return nullptr; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h @@ -42,6 +42,16 @@ llvm::Optional GetDwoNum() override { return GetID() >> 32; } + lldb::offset_t + GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) const override; + + bool ParseVendorDWARFOpcode( + uint8_t op, const lldb_private::DataExtractor &opcodes, + lldb::offset_t &offset, + std::vector &stack) const override; + protected: DIEToTypePtr &GetDIEToType() override; @@ -60,7 +70,7 @@ const DWARFDIE &die, lldb_private::ConstString type_name, bool must_be_implementation) override; - SymbolFileDWARF &GetBaseSymbolFile() { return m_base_symbol_file; } + SymbolFileDWARF &GetBaseSymbolFile() const { return m_base_symbol_file; } /// If this file contains exactly one compile unit, this function will return /// it. Otherwise it returns nullptr. diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp @@ -77,6 +77,18 @@ return cu; } +lldb::offset_t SymbolFileDWARFDwo::GetVendorDWARFOpcodeSize( + const lldb_private::DataExtractor &data, const lldb::offset_t data_offset, + const uint8_t op) const { + return GetBaseSymbolFile().GetVendorDWARFOpcodeSize(data, data_offset, op); +} + +bool SymbolFileDWARFDwo::ParseVendorDWARFOpcode( + uint8_t op, const lldb_private::DataExtractor &opcodes, + lldb::offset_t &offset, std::vector &stack) const { + return GetBaseSymbolFile().ParseVendorDWARFOpcode(op, opcodes, offset, stack); +} + SymbolFileDWARF::DIEToTypePtr &SymbolFileDWARFDwo::GetDIEToType() { return GetBaseSymbolFile().GetDIEToType(); } diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -9,9 +9,11 @@ #include "lldb/Expression/DWARFExpression.h" #include "Plugins/Platform/Linux/PlatformLinux.h" #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "TestingSupport/Symbol/YAMLModuleTester.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Core/Value.h" #include "lldb/Core/dwarf.h" #include "lldb/Host/HostInfo.h" @@ -514,3 +516,242 @@ ASSERT_EQ(result.GetValueType(), Value::ValueType::LoadAddress); ASSERT_EQ(result.GetScalar().UInt(), 0x5678u); } + +class CustomSymbolFileDWARF : public SymbolFileDWARF { + static char ID; + +public: + using SymbolFileDWARF::SymbolFileDWARF; + + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFile::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + + static llvm::StringRef GetPluginNameStatic() { return "custom_dwarf"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "Symbol file reader with expression extensions."; + } + + static void Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + SymbolFileDWARF::DebuggerInitialize); + } + + static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } + + static lldb_private::SymbolFile * + CreateInstance(lldb::ObjectFileSP objfile_sp) { + return new CustomSymbolFileDWARF(std::move(objfile_sp), + /*dwo_section_list*/ nullptr); + } + + lldb::offset_t + GetVendorDWARFOpcodeSize(const lldb_private::DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) const final { + auto offset = data_offset; + if (op != DW_OP_WASM_location) { + return LLDB_INVALID_OFFSET; + } + + // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32 + // Called with "arguments" 0x03 and 0x04 + // Location type: + if (data.GetU8(&offset) != /* global */ 0x03) { + return LLDB_INVALID_OFFSET; + } + + // Index + if (data.GetU32(&offset) != 0x04) { + return LLDB_INVALID_OFFSET; + } + + // Report the skipped distance: + return offset - data_offset; + } + + bool + ParseVendorDWARFOpcode(uint8_t op, const lldb_private::DataExtractor &opcodes, + lldb::offset_t &offset, + std::vector &stack) const final { + if (op != DW_OP_WASM_location) { + return false; + } + + // DW_OP_WASM_location WASM_GLOBAL:0x03 index:u32 + // Called with "arguments" 0x03 and 0x04 + // Location type: + if (opcodes.GetU8(&offset) != /* global */ 0x03) { + return false; + } + + // Index: + if (opcodes.GetU32(&offset) != 0x04) { + return false; + } + + // Return some value: + stack.push_back({GetScalar(32, 42, false)}); + return true; + } +}; + +char CustomSymbolFileDWARF::ID; + +static auto testExpressionVendorExtensions(lldb::ModuleSP module_sp, + DWARFUnit &dwarf_unit) { + // Test that expression extensions can be evaluated, for example + // DW_OP_WASM_location which is not currently handled by DWARFExpression: + EXPECT_THAT_EXPECTED(Evaluate({DW_OP_WASM_location, 0x03, // WASM_GLOBAL:0x03 + 0x04, 0x00, 0x00, // index:u32 + 0x00, DW_OP_stack_value}, + module_sp, &dwarf_unit), + llvm::HasValue(GetScalar(32, 42, false))); + + // Test that searches for opcodes work in the presence of extensions: + uint8_t expr[] = {DW_OP_WASM_location, 0x03, 0x04, 0x00, 0x00, 0x00, + DW_OP_form_tls_address}; + DataExtractor extractor(expr, sizeof(expr), lldb::eByteOrderLittle, + /*addr_size*/ 4); + DWARFExpression dwarf_expr(extractor); + ASSERT_TRUE(dwarf_expr.ContainsThreadLocalStorage(&dwarf_unit)); +} + +TEST(DWARFExpression, Extensions) { + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + debug_info: + - Version: 4 + AddrSize: 4 + Entries: + - AbbrCode: 0x1 + - AbbrCode: 0x0 +)"; + + SubsystemRAII + subsystems; + + llvm::Expected file = TestFile::fromYaml(yamldata); + EXPECT_THAT_EXPECTED(file, llvm::Succeeded()); + + auto module_sp = std::make_shared(file->moduleSpec()); + auto &symfile = + *llvm::cast(module_sp->GetSymbolFile()); + auto *dwarf_unit = symfile.DebugInfo().GetUnitAtIndex(0); + + testExpressionVendorExtensions(module_sp, *dwarf_unit); +} + +TEST(DWARFExpression, ExtensionsDWO) { + const char *skeleton_yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_skeleton_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_dwo_name + Form: DW_FORM_string + - Attribute: DW_AT_dwo_id + Form: DW_FORM_data4 + debug_info: + - Version: 4 + AddrSize: 4 + Entries: + - AbbrCode: 0x1 + Values: + - CStr: "dwo_unit" + - Value: 0x01020304 + - AbbrCode: 0x0 +)"; + + // .dwo sections aren't currently supported by dwarfyaml. The dwo_yamldata + // contents where generated by roundtripping the following yaml through + // yaml2obj | obj2yaml and renaming the sections. This works because the + // structure of the .dwo and non-.dwo sections is identical. + // + // --- !ELF + // FileHeader: + // Class: ELFCLASS64 + // Data: ELFDATA2LSB + // Type: ET_EXEC + // Machine: EM_386 + // DWARF: + // debug_abbrev: #.dwo + // - Table: + // - Code: 0x00000001 + // Tag: DW_TAG_compile_unit + // Children: DW_CHILDREN_no + // Attributes: + // - Attribute: DW_AT_dwo_id + // Form: DW_FORM_data4 + // debug_info: #.dwo + // - Version: 4 + // AddrSize: 4 + // Entries: + // - AbbrCode: 0x1 + // Values: + // - Value: 0x01020304 + // - AbbrCode: 0x0 + const char *dwo_yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +Sections: + - Name: .debug_abbrev.dwo + Type: SHT_PROGBITS + AddressAlign: 0x1 + Content: '0111007506000000' + - Name: .debug_info.dwo + Type: SHT_PROGBITS + AddressAlign: 0x1 + Content: 0D00000004000000000004010403020100 +)"; + + SubsystemRAII + subsystems; + + llvm::Expected skeleton_file = + TestFile::fromYaml(skeleton_yamldata); + EXPECT_THAT_EXPECTED(skeleton_file, llvm::Succeeded()); + llvm::Expected dwo_file = TestFile::fromYaml(dwo_yamldata); + EXPECT_THAT_EXPECTED(dwo_file, llvm::Succeeded()); + + auto skeleton_module_sp = + std::make_shared(skeleton_file->moduleSpec()); + auto &skeleton_symfile = + *llvm::cast(skeleton_module_sp->GetSymbolFile()); + + auto dwo_module_sp = std::make_shared(dwo_file->moduleSpec()); + SymbolFileDWARFDwo dwo_symfile( + skeleton_symfile, dwo_module_sp->GetObjectFile()->shared_from_this(), + 0x01020304); + auto *dwo_dwarf_unit = dwo_symfile.DebugInfo().GetUnitAtIndex(0); + + testExpressionVendorExtensions(dwo_module_sp, *dwo_dwarf_unit); +}