diff --git a/lldb/source/API/SystemInitializerFull.cpp b/lldb/source/API/SystemInitializerFull.cpp --- a/lldb/source/API/SystemInitializerFull.cpp +++ b/lldb/source/API/SystemInitializerFull.cpp @@ -91,6 +91,7 @@ #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" #include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h" +#include "Plugins/SymbolVendor/wasm/SymbolVendorWasm.h" #include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h" #include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" #include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h" @@ -234,6 +235,7 @@ SymbolFileDWARF::Initialize(); SymbolFilePDB::Initialize(); SymbolFileSymtab::Initialize(); + wasm::SymbolVendorWasm::Initialize(); UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); @@ -326,6 +328,7 @@ ThreadSanitizerRuntime::Terminate(); UndefinedBehaviorSanitizerRuntime::Terminate(); MainThreadCheckerRuntime::Terminate(); + wasm::SymbolVendorWasm::Terminate(); SymbolVendorELF::Terminate(); breakpad::SymbolFileBreakpad::Terminate(); SymbolFileDWARF::Terminate(); diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h @@ -52,6 +52,15 @@ uint32_t GetPluginVersion() override { return 1; } /// \} + /// LLVM RTTI support + /// \{ + static char ID; + bool isA(const void *ClassID) const override { + return ClassID == &ID || ObjectFile::isA(ClassID); + } + static bool classof(const ObjectFile *obj) { return obj->isA(&ID); } + /// \} + /// ObjectFile Protocol. /// \{ bool ParseHeader() override; @@ -97,6 +106,12 @@ } /// \} + /// A Wasm module that has external DWARF debug information should contain a + /// custom section named "external_debug_info", whose payload is an UTF-8 + /// encoded string that points to a Wasm module that contains the debug + /// information for this module. + llvm::Optional GetExternalDebugInfoFileSpec(); + private: ObjectFileWasm(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, const FileSpec *file, diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -55,6 +55,29 @@ return static_cast(value); } +static llvm::Optional +GetWasmString(DataExtractor §ion_header_data, lldb::offset_t *offset_ptr) { + // A Wasm string is encoded as a vector of UTF-8 codes. + // Vectors are encoded with their u32 length followed by the element + // sequence. + lldb::offset_t offset = *offset_ptr; + llvm::Optional len = + section_header_data.GetULEB128(&offset, uint64_t(1) << 32); + if (!len) + return llvm::None; + + const uint8_t *bytes = section_header_data.PeekData(offset, *len); + if (!bytes) + return llvm::None; + + llvm::StringRef str(reinterpret_cast(bytes), *len); + offset += *len; + *offset_ptr = offset; + return str; +} + +char ObjectFileWasm::ID; + void ObjectFileWasm::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, @@ -159,33 +182,19 @@ if (!payload_len) return false; - if (*section_id == 0) { + if (*section_id == llvm::wasm::WASM_SEC_CUSTOM) { // Custom sections have the id 0. Their contents consist of a name // identifying the custom section, followed by an uninterpreted sequence // of bytes. - // The name is encoded as a vector of UTF-8 codes. - // Vectors are encoded with their u32 length followed by the element - // sequence. lldb::offset_t prev_offset = offset; - llvm::Optional name_len = - section_header_data.GetULEB128(&offset, uint64_t(1) << 32); - if (!name_len) + llvm::Optional sect_name = + GetWasmString(section_header_data, &offset); + if (!sect_name) return false; - uint32_t name_len_uleb_size = offset - prev_offset; - const uint8_t *name_bytes = section_header_data.PeekData(offset, *name_len); - // If a custom section has a name longer than the allocated buffer or longer - // than the data left in the image, ignore this section. - if (!name_bytes) - return false; - - llvm::StringRef sect_name(reinterpret_cast(name_bytes), - *name_len); - offset += *name_len; - - uint32_t section_length = *payload_len - *name_len - name_len_uleb_size; + uint32_t section_length = *payload_len - (offset - prev_offset); m_sect_infos.push_back(section_info{*offset_ptr + offset, section_length, - *section_id, ConstString(sect_name)}); + *section_id, ConstString(*sect_name)}); offset += section_length; } else if (*section_id <= llvm::wasm::WASM_SEC_EVENT) { m_sect_infos.push_back(section_info{*offset_ptr + offset, @@ -388,6 +397,24 @@ return data; } +llvm::Optional ObjectFileWasm::GetExternalDebugInfoFileSpec() { + static ConstString g_sect_name_external_debug_info("external_debug_info"); + + for (const section_info §_info : m_sect_infos) { + if (g_sect_name_external_debug_info == sect_info.name) { + const uint32_t kBufferSize = 1024; + DataExtractor section_header_data = + ReadImageData(sect_info.offset, kBufferSize); + lldb::offset_t offset = 0; + llvm::Optional symbols_url = + GetWasmString(section_header_data, &offset); + if (symbols_url) + return FileSpec(*symbols_url); + } + } + return llvm::None; +} + void ObjectFileWasm::Dump(Stream *s) { ModuleSP module_sp(GetModule()); if (!module_sp) diff --git a/lldb/source/Plugins/SymbolVendor/CMakeLists.txt b/lldb/source/Plugins/SymbolVendor/CMakeLists.txt --- a/lldb/source/Plugins/SymbolVendor/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolVendor/CMakeLists.txt @@ -3,3 +3,4 @@ endif() add_subdirectory(ELF) +add_subdirectory(wasm) diff --git a/lldb/source/Plugins/SymbolVendor/wasm/CMakeLists.txt b/lldb/source/Plugins/SymbolVendor/wasm/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/wasm/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbPluginSymbolVendorWasm PLUGIN + SymbolVendorWasm.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbPluginObjectFileWasm + ) diff --git a/lldb/source/Plugins/SymbolVendor/wasm/SymbolVendorWasm.h b/lldb/source/Plugins/SymbolVendor/wasm/SymbolVendorWasm.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/wasm/SymbolVendorWasm.h @@ -0,0 +1,44 @@ +//===-- SymbolVendorWasm.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolVendorWasm_h_ +#define liblldb_SymbolVendorWasm_h_ + +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { +namespace wasm { + +class SymbolVendorWasm : public lldb_private::SymbolVendor { +public: + SymbolVendorWasm(const lldb::ModuleSP &module_sp); + + static void Initialize(); + static void Terminate(); + static lldb_private::ConstString GetPluginNameStatic(); + static const char *GetPluginDescriptionStatic(); + + static lldb_private::SymbolVendor * + CreateInstance(const lldb::ModuleSP &module_sp, + lldb_private::Stream *feedback_strm); + + /// PluginInterface protocol. + /// \{ + lldb_private::ConstString GetPluginName() override; + uint32_t GetPluginVersion() override; + /// \} + +private: + DISALLOW_COPY_AND_ASSIGN(SymbolVendorWasm); +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // liblldb_SymbolVendorWasm_h_ diff --git a/lldb/source/Plugins/SymbolVendor/wasm/SymbolVendorWasm.cpp b/lldb/source/Plugins/SymbolVendor/wasm/SymbolVendorWasm.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/wasm/SymbolVendorWasm.cpp @@ -0,0 +1,145 @@ +//===-- SymbolVendorWasm.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SymbolVendorWasm.h" + +#include + +#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/LocateSymbolFile.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::wasm; + +// SymbolVendorWasm constructor +SymbolVendorWasm::SymbolVendorWasm(const lldb::ModuleSP &module_sp) + : SymbolVendor(module_sp) {} + +void SymbolVendorWasm::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void SymbolVendorWasm::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString SymbolVendorWasm::GetPluginNameStatic() { + static ConstString g_name("WASM"); + return g_name; +} + +const char *SymbolVendorWasm::GetPluginDescriptionStatic() { + return "Symbol vendor for WASM that looks for dwo files that match " + "executables."; +} + +// CreateInstance +// +// Platforms can register a callback to use when creating symbol vendors to +// allow for complex debug information file setups, and to also allow for +// finding separate debug information files. +SymbolVendor * +SymbolVendorWasm::CreateInstance(const lldb::ModuleSP &module_sp, + lldb_private::Stream *feedback_strm) { + if (!module_sp) + return nullptr; + + ObjectFileWasm *obj_file = + llvm::dyn_cast_or_null(module_sp->GetObjectFile()); + if (!obj_file) + return nullptr; + + // If the main object file already contains debug info, then we are done. + if (obj_file->GetSectionList()->FindSectionByType( + lldb::eSectionTypeDWARFDebugInfo, true)) + return nullptr; + + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "SymbolVendorWasm::CreateInstance (module = %s)", + module_sp->GetFileSpec().GetPath().c_str()); + + ModuleSpec module_spec; + module_spec.GetFileSpec() = obj_file->GetFileSpec(); + FileSystem::Instance().Resolve(module_spec.GetFileSpec()); + module_spec.GetUUID() = obj_file->GetUUID(); + + // A Wasm module may have a custom section named "external_debug_info" whose + // content is the absolute or relative path of the Wasm module that contains + // debug symbols for this module. + llvm::Optional symbol_file_spec = + obj_file->GetExternalDebugInfoFileSpec(); + if (!symbol_file_spec) + return nullptr; + module_spec.GetSymbolFileSpec() = *symbol_file_spec; + + FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); + FileSpec sym_fspec = + Symbols::LocateExecutableSymbolFile(module_spec, search_paths); + if (!sym_fspec) + return nullptr; + + DataBufferSP sym_file_data_sp; + lldb::offset_t sym_file_data_offset = 0; + ObjectFileSP sym_objfile_sp = ObjectFile::FindPlugin( + module_sp, &sym_fspec, 0, FileSystem::Instance().GetByteSize(sym_fspec), + sym_file_data_sp, sym_file_data_offset); + if (!sym_objfile_sp) + return nullptr; + + // This objfile is for debugging purposes. + sym_objfile_sp->SetType(ObjectFile::eTypeDebugInfo); + + SymbolVendorWasm *symbol_vendor = new SymbolVendorWasm(module_sp); + + // Get the module unified section list and add our debug sections to + // that. + SectionList *module_section_list = module_sp->GetSectionList(); + SectionList *objfile_section_list = sym_objfile_sp->GetSectionList(); + + static const SectionType g_sections[] = { + eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugAddr, + eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugCuIndex, + eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugInfo, + eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugLineStr, + eSectionTypeDWARFDebugLoc, eSectionTypeDWARFDebugLocLists, + eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugMacro, + eSectionTypeDWARFDebugPubNames, eSectionTypeDWARFDebugPubTypes, + eSectionTypeDWARFDebugRanges, eSectionTypeDWARFDebugRngLists, + eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets, + eSectionTypeDWARFDebugTypes}; + for (SectionType section_type : g_sections) { + if (SectionSP section_sp = + objfile_section_list->FindSectionByType(section_type, true)) { + if (SectionSP module_section_sp = + module_section_list->FindSectionByType(section_type, true)) + module_section_list->ReplaceSection(module_section_sp->GetID(), + section_sp); + else + module_section_list->AddSection(section_sp); + } + } + + symbol_vendor->AddSymbolFileRepresentation(sym_objfile_sp); + return symbol_vendor; +} + +// PluginInterface protocol +ConstString SymbolVendorWasm::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t SymbolVendorWasm::GetPluginVersion() { return 1; } diff --git a/lldb/test/Shell/ObjectFile/wasm/Inputs/wasm-external_debug_info.yaml b/lldb/test/Shell/ObjectFile/wasm/Inputs/wasm-external_debug_info.yaml new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/ObjectFile/wasm/Inputs/wasm-external_debug_info.yaml @@ -0,0 +1,15 @@ +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 6 + Body: 238080808000210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B + - Type: CUSTOM + Name: external_debug_info + Payload: 0D746573745F73796D2E7761736D # test_sym.wasm +... diff --git a/lldb/test/Shell/ObjectFile/wasm/Inputs/wasm-stripped-debug-info.yaml b/lldb/test/Shell/ObjectFile/wasm/Inputs/wasm-stripped-debug-info.yaml new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/ObjectFile/wasm/Inputs/wasm-stripped-debug-info.yaml @@ -0,0 +1,18 @@ +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + + - Type: CUSTOM + Name: .debug_info + Payload: 4C00 + - Type: CUSTOM + Name: .debug_abbrev + Payload: 0111 + - Type: CUSTOM + Name: .debug_line + Payload: 5100 + - Type: CUSTOM + Name: .debug_str + Payload: 636CFF +... diff --git a/lldb/test/Shell/ObjectFile/wasm/unified-debug-sections.yaml b/lldb/test/Shell/ObjectFile/wasm/unified-debug-sections.yaml new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/ObjectFile/wasm/unified-debug-sections.yaml @@ -0,0 +1,50 @@ +# RUN: rm -rf %t.dir +# RUN: mkdir %t.dir +# RUN: cd %t.dir +# RUN: yaml2obj %p/Inputs/wasm-external_debug_info.yaml > %t.dir/test.wasm +# RUN: yaml2obj %p/Inputs/wasm-stripped-debug-info.yaml > %t.dir/test_sym.wasm +# RUN: lldb-test object-file %t.dir/test.wasm | FileCheck %s + +# This test checks that SymbolVendorWasm correctly loads DWARF debug sections +# that have been stripped out into a separated Wasm module. The original Wasm +# module contains a "external_debug_info" custom section with the absolute or +# relative path of the debug module. + +# CHECK: Plugin name: wasm +# CHECK: Architecture: wasm32-unknown-unknown-wasm +# CHECK: UUID: +# CHECK: Executable: true +# CHECK: Stripped: true +# CHECK: Type: executable +# CHECK: Strata: user +# CHECK: Base VM address: 0xa + +# CHECK: Name: code +# CHECK: Type: code +# CHECK: VM address: 0x0 +# CHECK: VM size: 56 +# CHECK: File size: 56 + +# CHECK: Name: .debug_info +# CHECK: Type: dwarf-info +# CHECK: VM address: 0x0 +# CHECK: VM size: 0 +# CHECK: File size: 2 + +# CHECK: Name: .debug_abbrev +# CHECK: Type: dwarf-abbrev +# CHECK: VM address: 0x0 +# CHECK: VM size: 0 +# CHECK: File size: 2 + +# CHECK: Name: .debug_line +# CHECK: Type: dwarf-line +# CHECK: VM address: 0x0 +# CHECK: VM size: 0 +# CHECK: File size: 2 + +# CHECK: Name: .debug_str +# CHECK: Type: dwarf-str +# CHECK: VM address: 0x0 +# CHECK: VM size: 0 +# CHECK: File size: 3 \ No newline at end of file diff --git a/lldb/tools/lldb-test/SystemInitializerTest.cpp b/lldb/tools/lldb-test/SystemInitializerTest.cpp --- a/lldb/tools/lldb-test/SystemInitializerTest.cpp +++ b/lldb/tools/lldb-test/SystemInitializerTest.cpp @@ -76,6 +76,7 @@ #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" #include "Plugins/SymbolVendor/ELF/SymbolVendorELF.h" +#include "Plugins/SymbolVendor/wasm/SymbolVendorWasm.h" #include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h" #include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" #include "Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h" @@ -201,6 +202,7 @@ SymbolFileDWARF::Initialize(); SymbolFilePDB::Initialize(); SymbolFileSymtab::Initialize(); + wasm::SymbolVendorWasm::Initialize(); UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); EmulateInstructionARM64::Initialize(); @@ -288,6 +290,7 @@ SymbolFileDWARF::Terminate(); SymbolFilePDB::Terminate(); SymbolFileSymtab::Terminate(); + wasm::SymbolVendorWasm::Terminate(); UnwindAssembly_x86::Terminate(); UnwindAssemblyInstEmulation::Terminate(); EmulateInstructionARM64::Terminate();