Index: lldb/include/lldb/Utility/ArchSpec.h =================================================================== --- lldb/include/lldb/Utility/ArchSpec.h +++ lldb/include/lldb/Utility/ArchSpec.h @@ -188,6 +188,8 @@ eCore_arc, // little endian ARC + eCore_wasm32, + kNumCores, kCore_invalid, Index: lldb/source/API/SystemInitializerFull.cpp =================================================================== --- lldb/source/API/SystemInitializerFull.cpp +++ 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(); Index: lldb/source/Plugins/ObjectFile/CMakeLists.txt =================================================================== --- lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ 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 Index: lldb/source/Plugins/ObjectFile/wasm/CMakeLists.txt =================================================================== --- /dev/null +++ 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 + ) Index: lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h @@ -0,0 +1,143 @@ +//===-- 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 { + +/// \class ObjectFileWasm +/// Generic Wasm object file reader. +/// +/// This class provides a generic wasm32 reader plugin implementing the +/// ObjectFile protocol. +class ObjectFileWasm : public ObjectFile { +public: + // Static Functions + 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 false; } + + 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 Address(m_memory_addr + 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(lldb::addr_t load_address); + /// \} + + /// 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); + /// \} + + typedef std::vector SectionInfoColl; + typedef SectionInfoColl::iterator SectionInfoCollIter; + typedef SectionInfoColl::const_iterator SectionInfoCollConstIter; + SectionInfoColl 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 Index: lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -0,0 +1,473 @@ +//===-- ObjectFileWasm.cpp --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#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/Wasm.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); + +bool ValidateModuleHeader(const DataBufferSP &data_sp) { + if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize) + return false; + + llvm::StringRef magic(reinterpret_cast(data_sp->GetBytes()), 4); + if (magic != llvm::StringRef(llvm::wasm::WasmMagic, + sizeof(llvm::wasm::WasmMagic) / sizeof(char))) + return false; + + const uint32_t *version = reinterpret_cast( + data_sp->GetBytes() + sizeof(llvm::wasm::WasmMagic)); + return *version == llvm::wasm::WasmVersion; +} + +/// Reads a LEB128 variable-length unsigned integer, limited to 7 bits. +llvm::Optional GetVaruint7(DataExtractor §ion_header_data, + lldb::offset_t *offset_ptr) { + lldb::offset_t initial_offset = *offset_ptr; + uint64_t value = section_header_data.GetULEB128(offset_ptr); + if (*offset_ptr == initial_offset || value > 127) + return llvm::None; + return static_cast(value); +} + +/// Reads a LEB128 variable-length unsigned integer, limited to 32 bits. +llvm::Optional GetVaruint32(DataExtractor §ion_header_data, + lldb::offset_t *offset_ptr) { + lldb::offset_t initial_offset = *offset_ptr; + uint64_t value = section_header_data.GetULEB128(offset_ptr); + if (*offset_ptr == initial_offset || value > uint64_t(1) << 32) + 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) { + static ConstString g_sect_name_external_debug_info("external_debug_info"); + + // 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 = + GetVaruint7(section_header_data, &offset); + if (!section_id) + return false; + + llvm::Optional payload_len = + GetVaruint32(section_header_data, &offset); + 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 = + GetVaruint32(section_header_data, &offset); + 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; + + ConstString sect_name(reinterpret_cast(name_bytes), + *name_len); + offset += *name_len; + + if (g_sect_name_external_debug_info == sect_name) { + // A Wasm file that has external DWARF debug information should + // contains a custom section named "external_debug_info", whose + // payload is an UTF-8 encoded URL string that points to the external + // DWARF file. + prev_offset = offset; + llvm::Optional url_len = + GetVaruint32(section_header_data, &offset); + if (!url_len) + return false; + uint32_t url_len_uleb_size = offset - prev_offset; + + const uint8_t *url_bytes = section_header_data.PeekData(offset, *url_len); + if (!url_bytes) { + return false; + } + + llvm::StringRef symbols_url(reinterpret_cast(url_bytes), + *url_len); + GetModule()->SetSymbolFileFileSpec(FileSpec(symbols_url)); + + uint32_t section_length = + *payload_len - *name_len - name_len_uleb_size - url_len_uleb_size; + offset += section_length; + } else { + 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, sect_name}); + offset += section_length; + } + } else if (*section_id <= llvm::wasm::WASM_SEC_EVENT) { + m_sect_infos.push_back(section_info{*offset_ptr + offset, *payload_len, + *section_id, ConstString()}); + offset += *payload_len; + } else { + // Invalid section id + return false; + } + *offset_ptr += offset; + return true; +} + +bool ObjectFileWasm::DecodeSections(lldb::addr_t load_address) { + lldb::offset_t offset = load_address + kWasmHeaderSize; + 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(0); + } + + for (const section_info §_info : m_sect_infos) { + if (llvm::wasm::WASM_SEC_CODE == sect_info.id) { + m_code_section_offset = sect_info.offset & 0xffffffff; + 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. + eSectionTypeCode, // Section ID. + ConstString("code"), // Section name. + eSectionTypeCode, // Section type. + 0, // sect_info.offset & 0xffffffff, // VM address. + sect_info.size, // VM size in bytes of this section. + 0, // sect_info.offset & 0xffffffff, // 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); + } else { + SectionType 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) { + 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. + ConstString(sect_info.name), // Section name. + section_type, // Section type. + sect_info.offset & 0xffffffff, // VM address. + sect_info.size, // VM size in bytes of this section. + sect_info.offset & + 0xffffffff, // 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); + } + } + } + + for (const auto §ion : *m_sections_up) { + unified_section_list.AddSection(section); + } +} + +/// 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. + +bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address, + bool value_is_offset) { + ModuleSP module_sp = GetModule(); + if (!module_sp) + return false; + + DecodeSections(load_address); + + 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->GetFileOffset())) { + ++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()); + } + } + } + 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"; +} + +// Dump a single Wasm section header to the specified output stream. +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"; +} + +// Dump all of the Wasm section headers to the specified output stream. +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 (SectionInfoCollIter pos = m_sect_infos.begin(); + pos != m_sect_infos.end(); ++pos, ++idx) { + ostream << "[" << llvm::format_decimal(idx, 2) << "] "; + ObjectFileWasm::DumpSectionHeader(ostream, *pos); + } +} Index: lldb/source/Utility/ArchSpec.cpp =================================================================== --- lldb/source/Utility/ArchSpec.cpp +++ 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,8 +220,11 @@ 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 // comment out an entry above, you will need to comment out the corresponding Index: lldb/unittests/ObjectFile/CMakeLists.txt =================================================================== --- lldb/unittests/ObjectFile/CMakeLists.txt +++ lldb/unittests/ObjectFile/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(Breakpad) add_subdirectory(ELF) add_subdirectory(PECOFF) +add_subdirectory(wasm) Index: lldb/unittests/ObjectFile/wasm/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/unittests/ObjectFile/wasm/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_unittest(ObjectFileWasmTests + TestObjectFileWasm.cpp + + LINK_LIBS + lldbUtilityHelpers + LLVMTestingSupport + lldbPluginObjectFileWasm + lldbPluginPlatformGDB + lldbPluginProcessGDBRemote + ) + Index: lldb/unittests/ObjectFile/wasm/TestObjectFileWasm.cpp =================================================================== --- /dev/null +++ lldb/unittests/ObjectFile/wasm/TestObjectFileWasm.cpp @@ -0,0 +1,281 @@ +//===-- TestObjectFileWasm.cpp ----------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "Plugins/ObjectFile/WASM/ObjectFileWasm.h" +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" +#include "TestingSupport/TestUtilities.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Reproducer.h" +#include "llvm/Testing/Support/Error.h" + +using namespace lldb_private; +using namespace lldb_private::repro; +using namespace lldb; + +class ObjectFileWasmTest : public testing::Test { +public: + void SetUp() override { + llvm::cantFail(Reproducer::Initialize(ReproducerMode::Off, llvm::None)); + FileSystem::Initialize(); + HostInfo::Initialize(); + wasm::ObjectFileWasm::Initialize(); + } + + void TearDown() override { + wasm::ObjectFileWasm::Terminate(); + HostInfo::Terminate(); + FileSystem::Terminate(); + Reproducer::Terminate(); + } +}; + +TEST_F(ObjectFileWasmTest, SectionsResolveConsistently) { + auto ExpectedFile = TestFile::fromYaml(R"( +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - I32 + ReturnTypes: + - I32 + - Type: FUNCTION + FunctionTypes: [ 0 ] + - Type: TABLE + Tables: + - ElemType: FUNCREF + Limits: + Flags: [ HAS_MAX ] + Initial: 0x00000001 + Maximum: 0x00000001 + - Type: MEMORY + Memories: + - Initial: 0x00000002 + - Type: GLOBAL + Globals: + - Index: 0 + Type: I32 + Mutable: true + InitExpr: + Opcode: I32_CONST + Value: 66560 + - Type: EXPORT + Exports: + - Name: memory + Kind: MEMORY + Index: 0 + - Name: square + Kind: FUNCTION + Index: 0 + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 6 + Body: 238080808000210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B + - Type: CUSTOM + Name: name + FunctionNames: + - Index: 0 + Name: square + - Type: CUSTOM + Name: producers + Languages: + - Name: C99 + Version: '' + Tools: + - Name: clang + Version: '10.0.0' + - Type: CUSTOM + Name: external_debug_info + Payload: 0F7371756172655F73796D2E7761736D +... +)"); + + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + ArchSpec arch("wasm32-unknown-unknown-wasm"); + Platform::SetHostPlatform( + platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(true, + &arch)); + + DebuggerSP debugger_sp = Debugger::CreateInstance(); + ASSERT_TRUE(debugger_sp); + + TargetSP target_sp; + PlatformSP platform_sp; + Status error = debugger_sp->GetTargetList().CreateTarget( + *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp); + ASSERT_TRUE(target_sp); + ASSERT_TRUE(target_sp->GetArchitecture().IsValid()); + ASSERT_TRUE(platform_sp); + + ModuleSpec spec{FileSpec(ExpectedFile->name())}; + auto module_sp = std::make_shared(spec); + + ObjectFile *object_file = module_sp->GetObjectFile(); + ASSERT_NE(object_file, nullptr); + object_file->SetLoadAddress(*target_sp, 4ULL << 32, false); + + UUID uuid = object_file->GetUUID(); + ASSERT_FALSE(uuid.IsValid()); + + ArchSpec arch_spec = object_file->GetArchitecture(); + ASSERT_EQ(llvm::Triple::wasm32, arch_spec.GetTriple().getArch()); + + uint32_t address_size = object_file->GetAddressByteSize(); + ASSERT_EQ(4, address_size); + + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto code_sp = list->FindSectionByName(ConstString("code")); + ASSERT_NE(nullptr, code_sp); + ASSERT_EQ(0, code_sp->GetFileAddress()); +} + +TEST_F(ObjectFileWasmTest, DwarfSectionsResolveConsistently) { + auto ExpectedFile = TestFile::fromYaml(R"( +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - I32 + ReturnTypes: + - I32 + - Type: FUNCTION + FunctionTypes: [ 0 ] + - Type: TABLE + Tables: + - ElemType: FUNCREF + Limits: + Flags: [ HAS_MAX ] + Initial: 0x00000001 + Maximum: 0x00000001 + - Type: MEMORY + Memories: + - Initial: 0x00000002 + - Type: GLOBAL + Globals: + - Index: 0 + Type: I32 + Mutable: true + InitExpr: + Opcode: I32_CONST + Value: 66560 + - Type: EXPORT + Exports: + - Name: memory + Kind: MEMORY + Index: 0 + - Name: square + Kind: FUNCTION + Index: 0 + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 6 + Body: 238080808000210141102102200120026B21032003200036020C200328020C2104200328020C2105200420056C210620060F0B + - Type: CUSTOM + Name: .debug_info + Payload: 4C0000000400000000000401000000000C005D00000000000000750000000200000036000000020200000036000000960000000101480000000302230CA100000001014800000000049D000000050400 + - Type: CUSTOM + Name: .debug_abbrev + Payload: 011101250E1305030E10171B0E110112060000022E0111011206030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042400030E3E0B0B0B000000 + - Type: CUSTOM + Name: .debug_line + Payload: 5100000004002F000000010101FB0E0D000101010100000001000001673A5C746573745C73717561726500007371756172652E6300010000000005020200000001050C0A08BB051406740512740505740204000101 + - Type: CUSTOM + Name: .debug_str + Payload: 636C616E672076657273696F6E2031302E302E302028473A2F4C4C564D31302F6C6C766D2D70726F6A6563742F636C616E6720393338313731623361643737336433366133313362336366626164633730623234323565636133662900673A5C746573745C7371756172655C7371756172652E6300473A5C4C4C564D31305C6C6C766D2D70726F6A6563745C64656275675F7836340073717561726500696E740076616C756500 + - Type: CUSTOM + Name: name + FunctionNames: + - Index: 0 + Name: square + - Type: CUSTOM + Name: producers + Languages: + - Name: C99 + Version: '' + Tools: + - Name: clang + Version: '10.0.0' +... +)"); + + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + ArchSpec arch("wasm32-unknown-unknown-wasm"); + Platform::SetHostPlatform( + platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(true, + &arch)); + + DebuggerSP debugger_sp = Debugger::CreateInstance(); + ASSERT_TRUE(debugger_sp); + + TargetSP target_sp; + PlatformSP platform_sp; + Status error = debugger_sp->GetTargetList().CreateTarget( + *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp); + ASSERT_TRUE(target_sp); + ASSERT_TRUE(target_sp->GetArchitecture().IsValid()); + ASSERT_TRUE(platform_sp); + + ModuleSpec spec{FileSpec(ExpectedFile->name())}; + auto module_sp = std::make_shared(spec); + + ObjectFile *object_file = module_sp->GetObjectFile(); + ASSERT_NE(object_file, nullptr); + object_file->SetLoadAddress(*target_sp, 4ULL << 32, false); + + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto code_sp = list->FindSectionByName(ConstString("code")); + ASSERT_NE(nullptr, code_sp); + ASSERT_EQ(0, code_sp->GetFileAddress()); + + auto debug_info_sp = list->FindSectionByName(ConstString(".debug_info")); + ASSERT_NE(nullptr, debug_info_sp); + ASSERT_GT(4ULL << 32, debug_info_sp->GetFileAddress()); + + auto debug_abbrev_sp = list->FindSectionByName(ConstString(".debug_abbrev")); + ASSERT_NE(nullptr, debug_abbrev_sp); + ASSERT_GT(4ULL << 32, debug_abbrev_sp->GetFileAddress()); + + auto debug_line_sp = list->FindSectionByName(ConstString(".debug_line")); + ASSERT_NE(nullptr, debug_line_sp); + ASSERT_GT(4ULL << 32, debug_line_sp->GetFileAddress()); + + auto debug_str_sp = list->FindSectionByName(ConstString(".debug_str")); + ASSERT_NE(nullptr, debug_str_sp); + ASSERT_GT(4ULL << 32, debug_str_sp->GetFileAddress()); +}