From 4bafceced6a7641be7b090229c6ccef22cf55bff Mon Sep 17 00:00:00 2001 From: Paolo Severini Date: Wed, 15 Jan 2020 15:29:24 -0800 Subject: [PATCH] [LLDB] Add ObjectFileWasm plugin for WebAssembly debugging Summary: This is the first in a series of patches to enable LLDB debugging of WebAssembly targets. Current versions of Clang emit (partial) DWARF debug information in WebAssembly modules and we can leverage this debug information to give LLDB the ability to do source-level debugging of Wasm code that runs in a WebAssembly engine. A way to do this could be to use the remote debugging functionalities provided by LLDB via the GDB-remote protocol. Remote debugging can indeed be useful not only to connect a debugger to a process running on a remote machine, but also to connect the debugger to a managed VM or script engine that runs locally, provided that the engine implements a GDB-remote stub that offers the ability to access the engine runtime internal state. To make this work, the GDB-remote protocol would need to be extended with a few Wasm-specific custom query commands, used to access aspects of the Wasm engine state (like the Wasm memory, Wasm local and global variables, and so on). Furthermore, the DWARF format would need to be enriched with a few Wasm-specific extensions, here detailed: https://yurydelendik.github.io/webassembly-dwarf. This CL introduce classes **ObjectFileWasm**, a file plugin to represent a Wasm module loaded in a debuggee process. It knows how to parse Wasm modules and store the Code section and the DWARF-specific sections. Reviewers: jasonmolenda, clayborg, labath Tags: #lldb Differential Revision: https://reviews.llvm.org/D71575 --- lldb/include/lldb/Utility/ArchSpec.h | 2 + lldb/source/API/SystemInitializerFull.cpp | 3 + lldb/source/Plugins/ObjectFile/CMakeLists.txt | 1 + .../Plugins/ObjectFile/wasm/CMakeLists.txt | 11 + .../ObjectFile/wasm/ObjectFileWasm.cpp | 435 ++++++++++++++++++ .../Plugins/ObjectFile/wasm/ObjectFileWasm.h | 138 ++++++ lldb/source/Utility/ArchSpec.cpp | 9 +- lldb/test/Shell/ObjectFile/wasm/basic.yaml | 30 ++ .../wasm/embedded-debug-sections.yaml | 67 +++ .../wasm/stripped-debug-sections.yaml | 54 +++ .../tools/lldb-test/SystemInitializerTest.cpp | 3 + 11 files changed, 750 insertions(+), 3 deletions(-) create mode 100644 lldb/source/Plugins/ObjectFile/wasm/CMakeLists.txt create mode 100644 lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp create mode 100644 lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h create mode 100644 lldb/test/Shell/ObjectFile/wasm/basic.yaml create mode 100644 lldb/test/Shell/ObjectFile/wasm/embedded-debug-sections.yaml create mode 100644 lldb/test/Shell/ObjectFile/wasm/stripped-debug-sections.yaml diff --git a/lldb/include/lldb/Utility/ArchSpec.h b/lldb/include/lldb/Utility/ArchSpec.h index 15e2fdb10c32..6e209dfd2e46 100644 --- a/lldb/include/lldb/Utility/ArchSpec.h +++ b/lldb/include/lldb/Utility/ArchSpec.h @@ -188,6 +188,8 @@ public: eCore_arc, // little endian ARC + eCore_wasm32, + kNumCores, kCore_invalid, diff --git a/lldb/source/API/SystemInitializerFull.cpp b/lldb/source/API/SystemInitializerFull.cpp index 06f1a6cd3b75..2c567974891c 100644 --- a/lldb/source/API/SystemInitializerFull.cpp +++ b/lldb/source/API/SystemInitializerFull.cpp @@ -73,6 +73,7 @@ #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" #include "Plugins/OperatingSystem/Python/OperatingSystemPython.h" #include "Plugins/Platform/Android/PlatformAndroid.h" #include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h" @@ -177,6 +178,7 @@ llvm::Error SystemInitializerFull::Initialize() { ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); + wasm::ObjectFileWasm::Initialize(); ObjectContainerBSDArchive::Initialize(); ObjectContainerUniversalMachO::Initialize(); @@ -404,6 +406,7 @@ void SystemInitializerFull::Terminate() { ObjectFileELF::Terminate(); ObjectFileMachO::Terminate(); ObjectFilePECOFF::Terminate(); + wasm::ObjectFileWasm::Terminate(); ObjectContainerBSDArchive::Terminate(); ObjectContainerUniversalMachO::Terminate(); diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt index 4edd667b9723..76f6d7ad0d78 100644 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(ELF) add_subdirectory(Mach-O) add_subdirectory(PECOFF) add_subdirectory(JIT) +add_subdirectory(wasm) \ No newline at end of file diff --git a/lldb/source/Plugins/ObjectFile/wasm/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/wasm/CMakeLists.txt new file mode 100644 index 000000000000..5069b6b19b95 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/wasm/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginObjectFileWasm PLUGIN + ObjectFileWasm.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp new file mode 100644 index 000000000000..1f636b719bb6 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -0,0 +1,435 @@ +//===-- ObjectFileWasm.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 "ObjectFileWasm.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/BinaryFormat/Wasm.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Format.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::wasm; + +static const uint32_t kWasmHeaderSize = + sizeof(llvm::wasm::WasmMagic) + sizeof(llvm::wasm::WasmVersion); + +/// Checks whether the data buffer starts with a valid Wasm module header. +static bool ValidateModuleHeader(const DataBufferSP &data_sp) { + if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize) + return false; + + if (llvm::identify_magic(toStringRef(data_sp->GetData())) != + llvm::file_magic::wasm_object) + return false; + + uint8_t *Ptr = data_sp->GetBytes() + sizeof(llvm::wasm::WasmMagic); + + uint32_t version = llvm::support::endian::read32le(Ptr); + return version == llvm::wasm::WasmVersion; +} + +static llvm::Optional +GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) { + // A Wasm string is encoded as a vector of UTF-8 codes. + // Vectors are encoded with their u32 length followed by the element + // sequence. + uint64_t len = data.getULEB128(c); + if (!c) { + consumeError(c.takeError()); + return llvm::None; + } + + if (len >= (uint64_t(1) << 32)) { + return llvm::None; + } + + llvm::SmallVector str_storage; + data.getU8(c, str_storage, len); + if (!c) { + consumeError(c.takeError()); + return llvm::None; + } + + llvm::StringRef str = toStringRef(makeArrayRef(str_storage)); + return ConstString(str); +} + +void ObjectFileWasm::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications); +} + +void ObjectFileWasm::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ConstString ObjectFileWasm::GetPluginNameStatic() { + static ConstString g_name("wasm"); + return g_name; +} + +ObjectFile * +ObjectFileWasm::CreateInstance(const ModuleSP &module_sp, DataBufferSP &data_sp, + offset_t data_offset, const FileSpec *file, + offset_t file_offset, offset_t length) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); + + if (!data_sp) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) { + LLDB_LOGF(log, "Failed to create ObjectFileWasm instance for file %s", + file->GetPath().c_str()); + return nullptr; + } + data_offset = 0; + } + + assert(data_sp); + if (!ValidateModuleHeader(data_sp)) { + LLDB_LOGF(log, + "Failed to create ObjectFileWasm instance: invalid Wasm header"); + return nullptr; + } + + // Update the data to contain the entire file if it doesn't contain it + // already. + if (data_sp->GetByteSize() < length) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) { + LLDB_LOGF(log, + "Failed to create ObjectFileWasm instance: cannot read file %s", + file->GetPath().c_str()); + return nullptr; + } + data_offset = 0; + } + + std::unique_ptr objfile_up(new ObjectFileWasm( + module_sp, data_sp, data_offset, file, file_offset, length)); + ArchSpec spec = objfile_up->GetArchitecture(); + if (spec && objfile_up->SetModulesArchitecture(spec)) { + LLDB_LOGF(log, + "%p ObjectFileWasm::CreateInstance() module = %p (%s), file = %s", + static_cast(objfile_up.get()), + static_cast(objfile_up->GetModule().get()), + objfile_up->GetModule()->GetSpecificationDescription().c_str(), + file ? file->GetPath().c_str() : ""); + return objfile_up.release(); + } + + LLDB_LOGF(log, "Failed to create ObjectFileWasm instance"); + return nullptr; +} + +ObjectFile *ObjectFileWasm::CreateMemoryInstance(const ModuleSP &module_sp, + DataBufferSP &data_sp, + const ProcessSP &process_sp, + addr_t header_addr) { + if (!ValidateModuleHeader(data_sp)) + return nullptr; + + std::unique_ptr objfile_up( + new ObjectFileWasm(module_sp, data_sp, process_sp, header_addr)); + ArchSpec spec = objfile_up->GetArchitecture(); + if (spec && objfile_up->SetModulesArchitecture(spec)) + return objfile_up.release(); + return nullptr; +} + +bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) { + // Buffer sufficient to read a section header and find the pointer to the next + // section. + const uint32_t kBufferSize = 1024; + DataExtractor section_header_data = ReadImageData(*offset_ptr, kBufferSize); + + llvm::DataExtractor data = section_header_data.GetAsLLVM(); + llvm::DataExtractor::Cursor c(0); + + // Each section consists of: + // - a one-byte section id, + // - the u32 size of the contents, in bytes, + // - the actual contents. + uint8_t section_id = data.getU8(c); + uint64_t payload_len = data.getULEB128(c); + if (!c) + return !llvm::errorToBool(c.takeError()); + + if (payload_len >= (uint64_t(1) << 32)) + return false; + + if (section_id == llvm::wasm::WASM_SEC_CUSTOM) { + lldb::offset_t prev_offset = c.tell(); + llvm::Optional sect_name = GetWasmString(data, c); + if (!sect_name) + return false; + + if (payload_len < c.tell() - prev_offset) + return false; + + uint32_t section_length = payload_len - (c.tell() - prev_offset); + m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), section_length, + section_id, *sect_name}); + *offset_ptr += (c.tell() + section_length); + } else if (section_id <= llvm::wasm::WASM_SEC_EVENT) { + m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), + static_cast(payload_len), + section_id, ConstString()}); + *offset_ptr += (c.tell() + payload_len); + } else { + // Invalid section id. + return false; + } + return true; +} + +bool ObjectFileWasm::DecodeSections() { + lldb::offset_t offset = kWasmHeaderSize; + if (IsInMemory()) { + offset += m_memory_addr; + } + + while (DecodeNextSection(&offset)) + ; + return true; +} + +size_t ObjectFileWasm::GetModuleSpecifications( + const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset, + offset_t file_offset, offset_t length, ModuleSpecList &specs) { + if (!ValidateModuleHeader(data_sp)) { + return 0; + } + + ModuleSpec spec(file, ArchSpec("wasm32-unknown-unknown-wasm")); + specs.Append(spec); + return 1; +} + +ObjectFileWasm::ObjectFileWasm(const ModuleSP &module_sp, DataBufferSP &data_sp, + offset_t data_offset, const FileSpec *file, + offset_t offset, offset_t length) + : ObjectFile(module_sp, file, offset, length, data_sp, data_offset), + m_arch("wasm32-unknown-unknown-wasm"), m_code_section_offset(0) { + m_data.SetAddressByteSize(4); +} + +ObjectFileWasm::ObjectFileWasm(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &header_data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr) + : ObjectFile(module_sp, process_sp, header_addr, header_data_sp), + m_arch("wasm32-unknown-unknown-wasm"), m_code_section_offset(0) {} + +bool ObjectFileWasm::ParseHeader() { + // We already parsed the header during initialization. + return true; +} + +Symtab *ObjectFileWasm::GetSymtab() { return nullptr; } + +void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { + if (m_sections_up) + return; + + m_sections_up = std::make_unique(); + + if (m_sect_infos.empty()) { + DecodeSections(); + } + + for (const section_info §_info : m_sect_infos) { + SectionType section_type = eSectionTypeOther; + ConstString section_name; + offset_t file_offset = 0; + addr_t vm_addr = 0; + size_t vm_size = 0; + + if (llvm::wasm::WASM_SEC_CODE == sect_info.id) { + section_type = eSectionTypeCode; + section_name = ConstString("code"); + m_code_section_offset = sect_info.offset & 0xffffffff; + vm_size = sect_info.size; + } else { + section_type = + llvm::StringSwitch(sect_info.name.GetStringRef()) + .Case(".debug_abbrev", eSectionTypeDWARFDebugAbbrev) + .Case(".debug_addr", eSectionTypeDWARFDebugAddr) + .Case(".debug_aranges", eSectionTypeDWARFDebugAranges) + .Case(".debug_cu_index", eSectionTypeDWARFDebugCuIndex) + .Case(".debug_frame", eSectionTypeDWARFDebugFrame) + .Case(".debug_info", eSectionTypeDWARFDebugInfo) + .Case(".debug_line", eSectionTypeDWARFDebugLine) + .Case(".debug_line_str", eSectionTypeDWARFDebugLineStr) + .Case(".debug_loc", eSectionTypeDWARFDebugLoc) + .Case(".debug_loclists", eSectionTypeDWARFDebugLocLists) + .Case(".debug_macinfo", eSectionTypeDWARFDebugMacInfo) + .Case(".debug_macro", eSectionTypeDWARFDebugMacro) + .Case(".debug_names", eSectionTypeDWARFDebugNames) + .Case(".debug_pubnames", eSectionTypeDWARFDebugPubNames) + .Case(".debug_pubtypes", eSectionTypeDWARFDebugPubTypes) + .Case(".debug_ranges", eSectionTypeDWARFDebugRanges) + .Case(".debug_rnglists", eSectionTypeDWARFDebugRngLists) + .Case(".debug_str", eSectionTypeDWARFDebugStr) + .Case(".debug_str_offsets", eSectionTypeDWARFDebugStrOffsets) + .Case(".debug_types", eSectionTypeDWARFDebugTypes) + .Default(eSectionTypeOther); + if (section_type == eSectionTypeOther) + continue; + section_name = sect_info.name; + file_offset = sect_info.offset & 0xffffffff; + if (IsInMemory()) { + vm_addr = sect_info.offset & 0xffffffff; + vm_size = sect_info.size; + } + } + + SectionSP section_sp( + new Section(GetModule(), // Module to which this section belongs. + this, // ObjectFile to which this section belongs and + // should read section data from. + section_type, // Section ID. + section_name, // Section name. + section_type, // Section type. + vm_addr, // VM address. + vm_size, // VM size in bytes of this section. + file_offset, // Offset of this section in the file. + sect_info.size, // Size of the section as found in the file. + 0, // Alignment of the section + 0, // Flags for this section. + 1)); // Number of host bytes per target byte + m_sections_up->AddSection(section_sp); + unified_section_list.AddSection(section_sp); + } +} + +bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address, + bool value_is_offset) { + /// In WebAssembly, linear memory is disjointed from code space. The VM can + /// load multiple instances of a module, which logically share the same code. + /// We represent a wasm32 code address with 64-bits, like: + /// 63 32 31 0 + /// +---------------+---------------+ + /// + module_id | offset | + /// +---------------+---------------+ + /// where the lower 32 bits represent a module offset (relative to the module + /// start not to the beginning of the code section) and the higher 32 bits + /// uniquely identify the module in the WebAssembly VM. + /// In other words, we assume that each WebAssembly module is loaded by the + /// engine at a 64-bit address that starts at the boundary of 4GB pages, like + /// 0x0000000400000000 for module_id == 4. + /// These 64-bit addresses will be used to request code ranges for a specific + /// module from the WebAssembly engine. + ModuleSP module_sp = GetModule(); + if (!module_sp) + return false; + + DecodeSections(); + + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (!section_list) + return false; + + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (target.GetSectionLoadList().SetSectionLoadAddress( + section_sp, load_address | section_sp->GetFileAddress())) { + ++num_loaded_sections; + } + } + + return num_loaded_sections > 0; +} + +DataExtractor ObjectFileWasm::ReadImageData(uint64_t offset, size_t size) { + DataExtractor data; + if (m_file) { + if (offset < GetByteSize()) { + size = std::min(size, GetByteSize() - offset); + auto buffer_sp = MapFileData(m_file, size, offset); + return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize()); + } + } else { + ProcessSP process_sp(m_process_wp.lock()); + if (process_sp) { + auto data_up = std::make_unique(size, 0); + Status readmem_error; + size_t bytes_read = process_sp->ReadMemory( + offset, data_up->GetBytes(), data_up->GetByteSize(), readmem_error); + if (bytes_read > 0) { + DataBufferSP buffer_sp(data_up.release()); + data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); + } + } + } + + data.SetByteOrder(GetByteOrder()); + return data; +} + +void ObjectFileWasm::Dump(Stream *s) { + ModuleSP module_sp(GetModule()); + if (!module_sp) + return; + + std::lock_guard guard(module_sp->GetMutex()); + + llvm::raw_ostream &ostream = s->AsRawOstream(); + ostream << static_cast(this) << ": "; + s->Indent(); + ostream << "ObjectFileWasm, file = '"; + m_file.Dump(ostream); + ostream << "', arch = "; + ostream << GetArchitecture().GetArchitectureName() << "\n"; + + SectionList *sections = GetSectionList(); + if (sections) { + sections->Dump(s, nullptr, true, UINT32_MAX); + } + ostream << "\n"; + DumpSectionHeaders(ostream); + ostream << "\n"; +} + +void ObjectFileWasm::DumpSectionHeader(llvm::raw_ostream &ostream, + const section_info_t &sh) { + ostream << llvm::left_justify(sh.name.GetStringRef(), 16) << " " + << llvm::format_hex(sh.offset, 10) << " " + << llvm::format_hex(sh.size, 10) << " " << llvm::format_hex(sh.id, 6) + << "\n"; +} + +void ObjectFileWasm::DumpSectionHeaders(llvm::raw_ostream &ostream) { + ostream << "Section Headers\n"; + ostream << "IDX name addr size id\n"; + ostream << "==== ---------------- ---------- ---------- ------\n"; + + uint32_t idx = 0; + for (auto pos = m_sect_infos.begin(); pos != m_sect_infos.end(); + ++pos, ++idx) { + ostream << "[" << llvm::format_decimal(idx, 2) << "] "; + ObjectFileWasm::DumpSectionHeader(ostream, *pos); + } +} diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h new file mode 100644 index 000000000000..986f7f9f1679 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h @@ -0,0 +1,138 @@ +//===-- ObjectFileWasm.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 LLDB_PLUGINS_OBJECTFILE_WASM_OBJECTFILEWASM_H +#define LLDB_PLUGINS_OBJECTFILE_WASM_OBJECTFILEWASM_H + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" + +namespace lldb_private { +namespace wasm { + +/// Generic Wasm object file reader. +/// +/// This class provides a generic wasm32 reader plugin implementing the +/// ObjectFile protocol. +class ObjectFileWasm : public ObjectFile { +public: + static void Initialize(); + static void Terminate(); + + static ConstString GetPluginNameStatic(); + static const char *GetPluginDescriptionStatic() { + return "WebAssembly object file reader."; + } + + static ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static ObjectFile *CreateMemoryInstance(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + ModuleSpecList &specs); + + /// PluginInterface protocol. + /// \{ + ConstString GetPluginName() override { return GetPluginNameStatic(); } + uint32_t GetPluginVersion() override { return 1; } + /// \} + + /// ObjectFile Protocol. + /// \{ + bool ParseHeader() override; + + lldb::ByteOrder GetByteOrder() const override { + return m_arch.GetByteOrder(); + } + + bool IsExecutable() const override { return true; } + + uint32_t GetAddressByteSize() const override { + return m_arch.GetAddressByteSize(); + } + + AddressClass GetAddressClass(lldb::addr_t file_addr) override { + return AddressClass::eInvalid; + } + + Symtab *GetSymtab() override; + + bool IsStripped() override { return true; } + + void CreateSections(SectionList &unified_section_list) override; + + void Dump(Stream *s) override; + + ArchSpec GetArchitecture() override { return m_arch; } + + UUID GetUUID() override { return m_uuid; } + + uint32_t GetDependentModules(FileSpecList &files) override { return 0; } + + Type CalculateType() override { return eTypeExecutable; } + + Strata CalculateStrata() override { return eStrataUser; } + + bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, + bool value_is_offset) override; + + lldb_private::Address GetBaseAddress() override { + return IsInMemory() ? Address(m_memory_addr + m_code_section_offset) + : Address(m_code_section_offset); + } + /// \} + +private: + ObjectFileWasm(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + ObjectFileWasm(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &header_data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + /// Wasm section decoding routines. + /// \{ + bool DecodeNextSection(lldb::offset_t *offset_ptr); + bool DecodeSections(); + /// \} + + /// Read a range of bytes from the Wasm module. + DataExtractor ReadImageData(uint64_t offset, size_t size); + + typedef struct section_info { + lldb::offset_t offset; + uint32_t size; + uint32_t id; + ConstString name; + } section_info_t; + + /// Wasm section header dump routines. + /// \{ + void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info_t &sh); + void DumpSectionHeaders(llvm::raw_ostream &ostream); + /// \} + + std::vector m_sect_infos; + ArchSpec m_arch; + UUID m_uuid; + uint32_t m_code_section_offset; +}; + +} // namespace wasm +} // namespace lldb_private +#endif // LLDB_PLUGINS_OBJECTFILE_WASM_OBJECTFILEWASM_H diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index 3dae25ceacd6..c1de3b9c4b09 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -100,9 +100,9 @@ static const CoreDefinition g_core_definitions[] = { {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_armv8, "armv8"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, - ArchSpec::eCore_arm_armv8l, "armv8l"}, + ArchSpec::eCore_arm_armv8l, "armv8l"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::aarch64_32, - ArchSpec::eCore_arm_arm64_32, "arm64_32"}, + ArchSpec::eCore_arm_arm64_32, "arm64_32"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_aarch64, "aarch64"}, @@ -216,7 +216,10 @@ static const CoreDefinition g_core_definitions[] = { ArchSpec::eCore_uknownMach32, "unknown-mach-32"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::UnknownArch, ArchSpec::eCore_uknownMach64, "unknown-mach-64"}, - {eByteOrderLittle, 4, 2, 4, llvm::Triple::arc, ArchSpec::eCore_arc, "arc"} + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arc, ArchSpec::eCore_arc, "arc"}, + + {eByteOrderLittle, 4, 1, 4, llvm::Triple::wasm32, ArchSpec::eCore_wasm32, + "wasm32"}, }; // Ensure that we have an entry in the g_core_definitions for each core. If you diff --git a/lldb/test/Shell/ObjectFile/wasm/basic.yaml b/lldb/test/Shell/ObjectFile/wasm/basic.yaml new file mode 100644 index 000000000000..e7928f961590 --- /dev/null +++ b/lldb/test/Shell/ObjectFile/wasm/basic.yaml @@ -0,0 +1,30 @@ +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s + +# 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 + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 6 + Body: 238080808000210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B + diff --git a/lldb/test/Shell/ObjectFile/wasm/embedded-debug-sections.yaml b/lldb/test/Shell/ObjectFile/wasm/embedded-debug-sections.yaml new file mode 100644 index 000000000000..ac36c66df441 --- /dev/null +++ b/lldb/test/Shell/ObjectFile/wasm/embedded-debug-sections.yaml @@ -0,0 +1,67 @@ +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s + +# 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 + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 6 + Body: 238080808000210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B + - 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/stripped-debug-sections.yaml b/lldb/test/Shell/ObjectFile/wasm/stripped-debug-sections.yaml new file mode 100644 index 000000000000..0ec349e73be2 --- /dev/null +++ b/lldb/test/Shell/ObjectFile/wasm/stripped-debug-sections.yaml @@ -0,0 +1,54 @@ +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s + +# 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: 0x0 + +# 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 + +--- !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/tools/lldb-test/SystemInitializerTest.cpp b/lldb/tools/lldb-test/SystemInitializerTest.cpp index 8841e4d6583a..ab8a92e8955f 100644 --- a/lldb/tools/lldb-test/SystemInitializerTest.cpp +++ b/lldb/tools/lldb-test/SystemInitializerTest.cpp @@ -56,6 +56,7 @@ #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" #include "Plugins/Platform/Android/PlatformAndroid.h" #include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h" #include "Plugins/Platform/Linux/PlatformLinux.h" @@ -152,6 +153,7 @@ llvm::Error SystemInitializerTest::Initialize() { ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); + wasm::ObjectFileWasm::Initialize(); ScriptInterpreterNone::Initialize(); @@ -345,6 +347,7 @@ void SystemInitializerTest::Terminate() { ObjectFileELF::Terminate(); ObjectFileMachO::Terminate(); ObjectFilePECOFF::Terminate(); + wasm::ObjectFileWasm::Terminate(); // Now shutdown the common parts, in reverse order. SystemInitializerCommon::Terminate();