Index: lldb/include/lldb/Utility/ArchSpec.h =================================================================== --- lldb/include/lldb/Utility/ArchSpec.h +++ lldb/include/lldb/Utility/ArchSpec.h @@ -188,6 +188,7 @@ 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 @@ -44,6 +44,7 @@ #include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" #include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" +#include "Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.h" #include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" #include "Plugins/Instruction/ARM/EmulateInstructionARM.h" #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" @@ -69,6 +70,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" @@ -90,6 +92,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" @@ -173,6 +176,7 @@ ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); + wasm::ObjectFileWasm::Initialize(); ObjectContainerBSDArchive::Initialize(); ObjectContainerUniversalMachO::Initialize(); @@ -232,6 +236,7 @@ SymbolFileDWARF::Initialize(); SymbolFilePDB::Initialize(); SymbolFileSymtab::Initialize(); + SymbolVendorWasm::Initialize(); UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); @@ -280,6 +285,7 @@ DynamicLoaderMacOSXDYLD::Initialize(); DynamicLoaderMacOS::Initialize(); DynamicLoaderPOSIXDYLD::Initialize(); + DynamicLoaderWasmDYLD::Initialize(); DynamicLoaderStatic::Initialize(); DynamicLoaderWindowsDYLD::Initialize(); @@ -324,6 +330,7 @@ ThreadSanitizerRuntime::Terminate(); UndefinedBehaviorSanitizerRuntime::Terminate(); MainThreadCheckerRuntime::Terminate(); + SymbolVendorWasm::Terminate(); SymbolVendorELF::Terminate(); breakpad::SymbolFileBreakpad::Terminate(); SymbolFileDWARF::Terminate(); @@ -372,6 +379,7 @@ DynamicLoaderMacOSXDYLD::Terminate(); DynamicLoaderMacOS::Terminate(); DynamicLoaderPOSIXDYLD::Terminate(); + DynamicLoaderWasmDYLD::Terminate(); DynamicLoaderStatic::Terminate(); DynamicLoaderWindowsDYLD::Terminate(); @@ -396,6 +404,7 @@ ObjectFileELF::Terminate(); ObjectFileMachO::Terminate(); ObjectFilePECOFF::Terminate(); + wasm::ObjectFileWasm::Terminate(); ObjectContainerBSDArchive::Terminate(); ObjectContainerUniversalMachO::Terminate(); Index: lldb/source/Plugins/DynamicLoader/CMakeLists.txt =================================================================== --- lldb/source/Plugins/DynamicLoader/CMakeLists.txt +++ lldb/source/Plugins/DynamicLoader/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(Static) add_subdirectory(Hexagon-DYLD) add_subdirectory(Windows-DYLD) +add_subdirectory(WASM-DYLD) \ No newline at end of file Index: lldb/source/Plugins/DynamicLoader/WASM-DYLD/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/source/Plugins/DynamicLoader/WASM-DYLD/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbPluginDynamicLoaderWasmDYLD PLUGIN + DynamicLoaderWasmDYLD.cpp + + LINK_LIBS + lldbCore + lldbTarget + LINK_COMPONENTS + Support + ) Index: lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.h =================================================================== --- /dev/null +++ lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.h @@ -0,0 +1,51 @@ +//===-- DynamicLoaderWasmDYLD.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_Plugins_Process_Wasm_DynamicLoaderWasmDYLD_h_ +#define liblldb_Plugins_Process_Wasm_DynamicLoaderWasmDYLD_h_ + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/lldb-forward.h" + +#include + +namespace lldb_private { + +class DynamicLoaderWasmDYLD : public DynamicLoader { +public: + DynamicLoaderWasmDYLD(Process *process); + + ~DynamicLoaderWasmDYLD() override; + + static void Initialize(); + static void Terminate(); + static ConstString GetPluginNameStatic(); + static const char *GetPluginDescriptionStatic(); + + static DynamicLoader *CreateInstance(Process *process, bool force); + + lldb::ModuleSP LoadModuleAtAddress(const lldb_private::FileSpec &file, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset) override; + void DidAttach() override; + void DidLaunch() override; + Status CanLoadImage() override; + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + bool stop) override; + + ConstString GetPluginName() override; + uint32_t GetPluginVersion() override; + +private: + std::map m_loaded_modules; +}; + +} // namespace lldb_private + +#endif // liblldb_Plugins_Process_Wasm_DynamicLoaderWasmDYLD_h_ Index: lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.cpp @@ -0,0 +1,177 @@ +//===-- DynamicLoaderWasmDYLD.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 "DynamicLoaderWasmDYLD.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +DynamicLoaderWasmDYLD::DynamicLoaderWasmDYLD(Process *process) + : DynamicLoader(process) {} + +DynamicLoaderWasmDYLD::~DynamicLoaderWasmDYLD() {} + +void DynamicLoaderWasmDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderWasmDYLD::Terminate() {} + +ConstString DynamicLoaderWasmDYLD::GetPluginNameStatic() { + static ConstString g_plugin_name("wasm-dyld"); + return g_plugin_name; +} + +const char *DynamicLoaderWasmDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in WebAssembly engines."; +} + +DynamicLoader *DynamicLoaderWasmDYLD::CreateInstance(Process *process, + bool force) { + bool should_create = force; + if (!should_create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getArch() == llvm::Triple::wasm32) + should_create = true; + } + + if (should_create) + return new DynamicLoaderWasmDYLD(process); + + return nullptr; +} + +// In WebAssembly, linear memory is disjointed from code space. The VM can load +// multiple instances of a module, which logically share the same code. +// Currently we only support wasm32, which uses a 32-bit address space for the +// code. +// We represent a code address in LLDB as a 64-bit address with the format: +// 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. +// This method should be called for each Wasm module loaded in the debuggee, +// with base_addr = 0x{module_id}|00000000 (for example 0x0000000100000000). + +ModuleSP DynamicLoaderWasmDYLD::LoadModuleAtAddress(const FileSpec &file, + addr_t link_map_addr, + addr_t base_addr, + bool base_addr_is_offset) { + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSpec module_spec(file, target.GetArchitecture()); + ModuleSP module_sp; + + if ((module_sp = modules.FindFirstModule(module_spec))) { + UpdateLoadedSections(module_sp, link_map_addr, base_addr, + base_addr_is_offset); + return module_sp; + } + + if ((module_sp = m_process->ReadModuleFromMemory(file, base_addr))) { + UpdateLoadedSections(module_sp, link_map_addr, base_addr, false); + target.GetImages().AppendIfNeeded(module_sp); + } + + return module_sp; +} + +void DynamicLoaderWasmDYLD::DidAttach() { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + LLDB_LOGF(log, "DynamicLoaderWasmDYLD::%s()", __FUNCTION__); + + // Ask the process for the list of loaded WebAssembly modules. + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "Couldn't load modules: {0}"); + + ModuleList loaded_module_list; + const ModuleList &module_list = m_process->GetTarget().GetImages(); + const size_t num_modules = module_list.GetSize(); + for (uint32_t idx = 0; idx < num_modules; ++idx) { + ModuleSP module_sp(module_list.GetModuleAtIndexUnlocked(idx)); + ObjectFile *image_object_file = module_sp->GetObjectFile(); + lldb::addr_t code_load_address = + image_object_file->GetBaseAddress().GetOffset(); + lldb::addr_t image_load_address = + image_object_file->GetBaseAddress().GetOffset() & 0xffffffff00000000; + if (module_sp) { + bool changed = false; + if (image_object_file) { + SectionList *section_list = image_object_file->GetSectionList(); + if (section_list) { + // Fixes the section load address for each section. + const size_t num_sections = section_list->GetSize(); + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp) { + // Code section load address is offsetted by the code section + // offset in the Wasm module. + if (section_sp->GetName() == "code") { + if (m_process->GetTarget().SetSectionLoadAddress( + section_sp, + code_load_address | section_sp->GetFileAddress())) { + changed = true; + } + } else { + if (m_process->GetTarget().SetSectionLoadAddress( + section_sp, + image_load_address | section_sp->GetFileAddress())) { + changed = true; + } + } + } + } + } + } + + if (changed) + loaded_module_list.AppendIfNeeded(module_sp); + } + } + + m_process->GetTarget().ModulesDidLoad(loaded_module_list); +} + +void DynamicLoaderWasmDYLD::DidLaunch() {} + +Status DynamicLoaderWasmDYLD::CanLoadImage() { return Status(); } + +ConstString DynamicLoaderWasmDYLD::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t DynamicLoaderWasmDYLD::GetPluginVersion() { return 1; } + +ThreadPlanSP DynamicLoaderWasmDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + auto arch = m_process->GetTarget().GetArchitecture(); + if (arch.GetMachine() != llvm::Triple::wasm32) { + return ThreadPlanSP(); + } + + // TODO(paolosev) - What should we do here? + return ThreadPlanSP(); +} 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) 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,135 @@ +//===-- 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 : 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); + + static bool GetVaruint7(DataExtractor §ion_header_data, + lldb::offset_t *offset_ptr, uint8_t *result); + static bool GetVaruint32(DataExtractor §ion_header_data, + lldb::offset_t *offset_ptr, uint32_t *result); + + bool DecodeNextSection(lldb::offset_t *offset_ptr); + bool DecodeSections(lldb::addr_t load_address); + + DataExtractor ReadImageData(uint64_t offset, size_t size); + + typedef struct section_info { + lldb::offset_t offset; + uint32_t size; + uint32_t id; + std::string name; + } section_info_t; + + void DumpSectionHeader(Stream *s, const section_info_t &sh); + void DumpSectionHeaders(Stream *s); + + typedef std::vector SectionInfoColl; + typedef SectionInfoColl::iterator SectionInfoCollIter; + typedef SectionInfoColl::const_iterator SectionInfoCollConstIter; + SectionInfoColl m_sect_infos; + + ArchSpec m_arch; + UUID m_uuid; + ConstString m_symbols_url; + 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,486 @@ +//===-- 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 "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/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/BinaryFormat/Wasm.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) { + + const char *magic = reinterpret_cast(data_sp->GetBytes()); + if (strncmp(magic, llvm::wasm::WasmMagic, sizeof(llvm::wasm::WasmMagic)) != + 0) { + return false; + } + + const uint32_t *version = reinterpret_cast( + data_sp->GetBytes() + sizeof(llvm::wasm::WasmMagic)); + return *version == llvm::wasm::WasmVersion; + } + return false; +} + +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) { + if (!data_sp) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) { + return nullptr; + } + data_offset = 0; + } + + assert(data_sp); + if (!ValidateModuleHeader(data_sp)) { + return nullptr; + } + + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) { + 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)) { + return objfile_up.release(); + } + + return nullptr; +} + +ObjectFile *ObjectFileWasm::CreateMemoryInstance(const ModuleSP &module_sp, + DataBufferSP &data_sp, + const ProcessSP &process_sp, + addr_t header_addr) { + if (ValidateModuleHeader(data_sp)) { + 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; +} + +// static +bool ObjectFileWasm::GetVaruint7(DataExtractor §ion_header_data, + lldb::offset_t *offset_ptr, uint8_t *result) { + lldb::offset_t initial_offset = *offset_ptr; + uint64_t value = section_header_data.GetULEB128(offset_ptr); + if (*offset_ptr == initial_offset || value > 127) { + return false; + } + *result = static_cast(value); + return true; +} + +// static +bool ObjectFileWasm::GetVaruint32(DataExtractor §ion_header_data, + lldb::offset_t *offset_ptr, + uint32_t *result) { + 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 false; + } + *result = static_cast(value); + return true; +} + +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; + uint8_t section_id = 0; + uint32_t payload_len = 0; + // Each section consists of: + // - a one-byte section id, + // - the u32 size of the contents, in bytes, + // - the actual contents. + if (GetVaruint7(section_header_data, &offset, §ion_id) && + GetVaruint32(section_header_data, &offset, &payload_len)) { + + 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. + uint32_t name_len = 0; + lldb::offset_t prev_offset = offset; + if (GetVaruint32(section_header_data, &offset, &name_len)) { + uint32_t name_len_uleb_size = offset - prev_offset; + + const uint8_t *name_bytes = + section_header_data.PeekData(offset, name_len); + if (!name_bytes) { + // If a custom section has a name longer than the allocated buffer or + // longer than the data left in the image, ignore this section. + return false; + } + std::string sect_name(reinterpret_cast(name_bytes), + name_len); + offset += name_len; + + if (g_sect_name_external_debug_info == sect_name.c_str()) { + // 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. + uint32_t url_len = 0; + prev_offset = offset; + if (GetVaruint32(section_header_data, &offset, &url_len)) { + 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; + } + m_symbols_url = + ConstString(reinterpret_cast(url_bytes), url_len); + FileSpec file_spec(m_symbols_url.GetStringRef()); + GetModule()->SetSymbolFileFileSpec(FileSpec(file_spec)); + + 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, ""}); + offset += payload_len; + } else { + // Invalid section id + return false; + } + *offset_ptr += offset; + return true; + } + return false; +} + +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) { + static ConstString g_sect_name_dwarf_debug_abbrev(".debug_abbrev"); + static ConstString g_sect_name_dwarf_debug_aranges(".debug_aranges"); + static ConstString g_sect_name_dwarf_debug_frame(".debug_frame"); + static ConstString g_sect_name_dwarf_debug_info(".debug_info"); + static ConstString g_sect_name_dwarf_debug_line(".debug_line"); + static ConstString g_sect_name_dwarf_debug_loc(".debug_loc"); + static ConstString g_sect_name_dwarf_debug_loclists(".debug_loclists"); + static ConstString g_sect_name_dwarf_debug_macinfo(".debug_macinfo"); + static ConstString g_sect_name_dwarf_debug_names(".debug_names"); + static ConstString g_sect_name_dwarf_debug_pubnames(".debug_pubnames"); + static ConstString g_sect_name_dwarf_debug_pubtypes(".debug_pubtypes"); + static ConstString g_sect_name_dwarf_debug_ranges(".debug_ranges"); + static ConstString g_sect_name_dwarf_debug_str(".debug_str"); + static ConstString g_sect_name_dwarf_debug_types(".debug_types"); + static ConstString g_sect_name_dwarf_debug_addr("dwarf-addr"); + static ConstString g_sect_name_dwarf_debug_cuindex("dwarf-cu-index"); + static ConstString g_sect_name_dwarf_debug_macro("dwarf-macro"); + static ConstString g_sect_name_dwarf_debug_stroffsets("dwarf-str-offsets"); + + if (m_sections_up) + return; + + m_sections_up = std::make_unique(); + + if (m_sect_infos.empty()) { + DecodeSections(0); + } + + for (SectionInfoCollConstIter it = m_sect_infos.begin(); + it != m_sect_infos.end(); ++it) { + const section_info §_info = *it; + + SectionType section_type = eSectionTypeInvalid; + + if (llvm::wasm::WASM_SEC_CODE == sect_info.id) + section_type = eSectionTypeCode; + else if (g_sect_name_dwarf_debug_abbrev == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugAbbrev; + else if (g_sect_name_dwarf_debug_aranges == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugAranges; + else if (g_sect_name_dwarf_debug_frame == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugFrame; + else if (g_sect_name_dwarf_debug_info == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugInfo; + else if (g_sect_name_dwarf_debug_line == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugLine; + else if (g_sect_name_dwarf_debug_loc == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugLoc; + else if (g_sect_name_dwarf_debug_loclists == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugLocLists; + else if (g_sect_name_dwarf_debug_macinfo == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugMacInfo; + else if (g_sect_name_dwarf_debug_names == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugNames; + else if (g_sect_name_dwarf_debug_pubnames == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugPubNames; + else if (g_sect_name_dwarf_debug_pubtypes == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugPubTypes; + else if (g_sect_name_dwarf_debug_ranges == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugRanges; + else if (g_sect_name_dwarf_debug_str == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugStr; + else if (g_sect_name_dwarf_debug_types == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugTypes; + else if (g_sect_name_dwarf_debug_addr == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugAddr; + else if (g_sect_name_dwarf_debug_cuindex == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugCuIndex; + else if (g_sect_name_dwarf_debug_macro == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugMacro; + else if (g_sect_name_dwarf_debug_stroffsets == sect_info.name.c_str()) + section_type = eSectionTypeDWARFDebugStrOffsets; + + if (section_type == eSectionTypeCode) { + 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. + section_type, // Section ID. + ConstString("code"), // Section name. + section_type, // 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 if (section_type != eSectionTypeInvalid) { + 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.c_str()), // 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) { + DecodeSections(load_address); + + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList(); + if (section_list) { + 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; + } + } + return false; +} + +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) { + std::lock_guard guard(module_sp->GetMutex()); + s->Printf("%p: ", static_cast(this)); + s->Indent(); + s->PutCString("ObjectFileWasm"); + + ArchSpec header_arch = GetArchitecture(); + + *s << ", file = '" << m_file + << "', arch = " << header_arch.GetArchitectureName() << "\n"; + + SectionList *sections = GetSectionList(); + if (sections) { + sections->Dump(s, nullptr, true, UINT32_MAX); + } + s->EOL(); + DumpSectionHeaders(s); + s->EOL(); + } +} + +// Dump a single Wasm section header to the specified output stream. +void ObjectFileWasm::DumpSectionHeader(Stream *s, const section_info_t &sh) { + s->Printf("%-16s 0x%8.8x 0x%8.8x 0x%4.4x\n", sh.name.c_str(), sh.offset, + sh.size, sh.id); +} + +// Dump all of the Wasm section header to the specified output stream. +void ObjectFileWasm::DumpSectionHeaders(Stream *s) { + s->PutCString("Section Headers\n"); + s->PutCString("IDX name addr size id\n"); + s->PutCString("==== ---------------- ---------- ---------- ------\n"); + + uint32_t idx = 0; + SectionInfoCollIter pos, end = m_sect_infos.end(); + for (pos = m_sect_infos.begin(); pos != end; ++pos, ++idx) { + s->Printf("[%2u] ", idx); + ObjectFileWasm::DumpSectionHeader(s, *pos); + } +} Index: lldb/source/Plugins/SymbolVendor/CMakeLists.txt =================================================================== --- lldb/source/Plugins/SymbolVendor/CMakeLists.txt +++ lldb/source/Plugins/SymbolVendor/CMakeLists.txt @@ -3,3 +3,4 @@ endif() add_subdirectory(ELF) +add_subdirectory(WASM) Index: lldb/source/Plugins/SymbolVendor/WASM/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolVendor/WASM/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbPluginSymbolVendorWasm PLUGIN + SymbolVendorWasm.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbPluginObjectFileWasm + ) Index: lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWasm.h =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWasm.h @@ -0,0 +1,36 @@ +//===-- SymbolVendorWasm.h ---------------------------------------*- C++ +//-*-===// SymbolVendorWasm +// 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" + +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); +}; + +#endif // liblldb_SymbolVendorWasm_h_ Index: lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWasm.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWasm.cpp @@ -0,0 +1,135 @@ +//===-- SymbolVendorWasm.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 "SymbolVendorWasm.h" + +#include + +#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; + +// 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; + + ObjectFile *obj_file = 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(); + + const FileSpec fspec = module_sp->GetSymbolFileFileSpec(); + + FileSystem::Instance().Resolve(module_spec.GetFileSpec()); + module_spec.GetSymbolFileSpec() = fspec; + module_spec.GetUUID() = obj_file->GetUUID(); + 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, eSectionTypeDWARFDebugLoc, + eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugPubNames, + eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges, + eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugStrOffsets, + eSectionTypeELFSymbolTable, eSectionTypeDWARFGNUDebugAltLink, + }; + 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; } Index: lldb/source/Utility/ArchSpec.cpp =================================================================== --- lldb/source/Utility/ArchSpec.cpp +++ lldb/source/Utility/ArchSpec.cpp @@ -103,10 +103,10 @@ ArchSpec::eCore_arm_arm64, "arm64"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_armv8, "armv8"}, - {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, - ArchSpec::eCore_arm_armv8l, "armv8l"}, + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, 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,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 // comment out an entry above, you will need to comment out the corresponding @@ -448,7 +450,9 @@ {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON {ArchSpec::eCore_arc, llvm::ELF::EM_ARC_COMPACT2, LLDB_INVALID_CPUTYPE, - 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARC + 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARC + {ArchSpec::eCore_wasm32, llvm::ELF::EM_WASM, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // WebAssembly 32bit }; static const ArchDefinition g_elf_arch_def = { 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,282 @@ +//===-- 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()); +} Index: llvm/include/llvm/BinaryFormat/ELF.h =================================================================== --- llvm/include/llvm/BinaryFormat/ELF.h +++ llvm/include/llvm/BinaryFormat/ELF.h @@ -311,6 +311,7 @@ EM_RISCV = 243, // RISC-V EM_LANAI = 244, // Lanai 32-bit processor EM_BPF = 247, // Linux kernel bpf virtual machine + EM_WASM = 248, // WebAssembly }; // Object file classes.