diff --git a/lldb/include/lldb/Utility/ArchSpec.h b/lldb/include/lldb/Utility/ArchSpec.h --- a/lldb/include/lldb/Utility/ArchSpec.h +++ b/lldb/include/lldb/Utility/ArchSpec.h @@ -188,6 +188,8 @@ eCore_arc, // little endian ARC + eCore_wasm32, + kNumCores, kCore_invalid, diff --git a/lldb/include/lldb/Utility/DataExtractor.h b/lldb/include/lldb/Utility/DataExtractor.h --- a/lldb/include/lldb/Utility/DataExtractor.h +++ b/lldb/include/lldb/Utility/DataExtractor.h @@ -809,6 +809,12 @@ /// The extracted unsigned integer value. uint64_t GetULEB128(lldb::offset_t *offset_ptr) const; + /// Extract a ULEB128 number with a specified max value. If the extracted + /// value exceeds "max_value" the offset will be left unchanged and llvm::None + /// will be returned. + llvm::Optional GetULEB128(lldb::offset_t *offset_ptr, + uint64_t max_value); + lldb::DataBufferSP &GetSharedDataBuffer() { return m_data_sp; } /// Peek at a C string at \a offset. 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 @@ -69,6 +69,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" @@ -173,6 +174,7 @@ ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); + wasm::ObjectFileWasm::Initialize(); ObjectContainerBSDArchive::Initialize(); ObjectContainerUniversalMachO::Initialize(); @@ -396,6 +398,7 @@ 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 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -3,3 +3,4 @@ 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 --- /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.h b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h new file mode 100644 --- /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/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -0,0 +1,432 @@ +//===-- 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/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; +} + +/// Reads a byte from the data extractor. +static llvm::Optional GetByte(DataExtractor §ion_header_data, + lldb::offset_t *offset_ptr) { + lldb::offset_t initial_offset = *offset_ptr; + uint8_t value = section_header_data.GetU8(offset_ptr); + if (*offset_ptr == initial_offset) + return llvm::None; + return static_cast(value); +} + +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 %", + 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); + + lldb::offset_t offset = 0; + + // Each section consists of: + // - a one-byte section id, + // - the u32 size of the contents, in bytes, + // - the actual contents. + llvm::Optional section_id = GetByte(section_header_data, &offset); + if (!section_id) + return false; + + llvm::Optional payload_len = + section_header_data.GetULEB128(&offset, uint64_t(1) << 32); + if (!payload_len) + return false; + + if (*section_id == 0) { + // 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) + 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; + m_sect_infos.push_back(section_info{*offset_ptr + offset, section_length, + *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, + static_cast(*payload_len), + *section_id, ConstString()}); + offset += *payload_len; + } else { + // Invalid section id. + return false; + } + *offset_ptr += offset; + 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; + vm_addr = IsInMemory() ? sect_info.offset & 0xffffffff : 0; + vm_size = IsInMemory() ? sect_info.size : 0; + } + + 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/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -104,9 +104,9 @@ {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"}, @@ -220,7 +220,10 @@ 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/source/Utility/DataExtractor.cpp b/lldb/source/Utility/DataExtractor.cpp --- a/lldb/source/Utility/DataExtractor.cpp +++ b/lldb/source/Utility/DataExtractor.cpp @@ -910,6 +910,18 @@ return 0; } +/// Extract an unsigned LEB128 number with a specified max value. If the +/// extracted value exceeds "max_value" the offset will be left unchanged and +/// llvm::None will be returned. +llvm::Optional DataExtractor::GetULEB128(offset_t *offset_ptr, + uint64_t max_value) { + offset_t initial_offset = *offset_ptr; + uint64_t value = GetULEB128(offset_ptr); + if (*offset_ptr == initial_offset || value > max_value) + return llvm::None; + return value; +} + // Extracts an signed LEB128 number from this object's data starting at the // offset pointed to by "offset_ptr". The offset pointed to by "offset_ptr" // will be updated with the offset of the byte following the last extracted diff --git a/lldb/test/Shell/ObjectFile/wasm/basic.yaml b/lldb/test/Shell/ObjectFile/wasm/basic.yaml new file mode 100644 --- /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 --- /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 --- /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 --- 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 @@ ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); + wasm::ObjectFileWasm::Initialize(); ScriptInterpreterNone::Initialize(); @@ -345,6 +347,7 @@ ObjectFileELF::Terminate(); ObjectFileMachO::Terminate(); ObjectFilePECOFF::Terminate(); + wasm::ObjectFileWasm::Terminate(); // Now shutdown the common parts, in reverse order. SystemInitializerCommon::Terminate();