diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h index 44015b4e418f5..97e9f39cebe6c 100644 --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -161,14 +161,14 @@ class DWARFExpression { bool Evaluate(ExecutionContextScope *exe_scope, lldb::addr_t loclist_base_load_addr, const Value *initial_value_ptr, const Value *object_address_ptr, - Value &result, Status *error_ptr) const; + uint64_t byte_size, Value &result, Status *error_ptr) const; /// Wrapper for the static evaluate function that uses member variables to /// populate many operands bool Evaluate(ExecutionContext *exe_ctx, RegisterContext *reg_ctx, lldb::addr_t loclist_base_load_addr, const Value *initial_value_ptr, const Value *object_address_ptr, - Value &result, Status *error_ptr) const; + uint64_t byte_size, Value &result, Status *error_ptr) const; /// Evaluate a DWARF location expression in a particular context /// @@ -211,6 +211,10 @@ class DWARFExpression { /// A value to put on top of the interpreter stack before evaluating /// the expression, if the expression is parametrized. Can be NULL. /// + /// \param[in] byte_size + /// The size, in bytes, of the result of the expression to evaluate. + /// Currently only used for WebAssembly targets. + /// /// \param[in] result /// A value into which the result of evaluating the expression is /// to be placed. @@ -226,8 +230,8 @@ class DWARFExpression { const DWARFUnit *dwarf_cu, const lldb::RegisterKind reg_set, const Value *initial_value_ptr, - const Value *object_address_ptr, Value &result, - Status *error_ptr); + const Value *object_address_ptr, uint64_t byte_size, + Value &result, Status *error_ptr); bool GetExpressionData(DataExtractor &data) const { data = m_data; diff --git a/lldb/include/lldb/Utility/ArchSpec.h b/lldb/include/lldb/Utility/ArchSpec.h index 3bfc988abf0be..30f3e8eac8b44 100644 --- a/lldb/include/lldb/Utility/ArchSpec.h +++ b/lldb/include/lldb/Utility/ArchSpec.h @@ -186,6 +186,8 @@ class ArchSpec { eCore_arc, // little endian ARC + eCore_wasm32, + kNumCores, kCore_invalid, diff --git a/lldb/source/API/SystemInitializerFull.cpp b/lldb/source/API/SystemInitializerFull.cpp index 0acc496ff8796..4824ee4da3f86 100644 --- a/lldb/source/API/SystemInitializerFull.cpp +++ b/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 @@ llvm::Error SystemInitializerFull::Initialize() { ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); + wasm::ObjectFileWASM::Initialize(); ObjectContainerBSDArchive::Initialize(); ObjectContainerUniversalMachO::Initialize(); @@ -232,6 +236,7 @@ llvm::Error SystemInitializerFull::Initialize() { SymbolFileDWARF::Initialize(); SymbolFilePDB::Initialize(); SymbolFileSymtab::Initialize(); + SymbolVendorWasm::Initialize(); UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); @@ -280,6 +285,7 @@ llvm::Error SystemInitializerFull::Initialize() { DynamicLoaderMacOSXDYLD::Initialize(); DynamicLoaderMacOS::Initialize(); DynamicLoaderPOSIXDYLD::Initialize(); + DynamicLoaderWasmDYLD::Initialize(); DynamicLoaderStatic::Initialize(); DynamicLoaderWindowsDYLD::Initialize(); @@ -324,6 +330,7 @@ void SystemInitializerFull::Terminate() { ThreadSanitizerRuntime::Terminate(); UndefinedBehaviorSanitizerRuntime::Terminate(); MainThreadCheckerRuntime::Terminate(); + SymbolVendorWasm::Terminate(); SymbolVendorELF::Terminate(); breakpad::SymbolFileBreakpad::Terminate(); SymbolFileDWARF::Terminate(); @@ -372,6 +379,7 @@ void SystemInitializerFull::Terminate() { DynamicLoaderMacOSXDYLD::Terminate(); DynamicLoaderMacOS::Terminate(); DynamicLoaderPOSIXDYLD::Terminate(); + DynamicLoaderWasmDYLD::Terminate(); DynamicLoaderStatic::Terminate(); DynamicLoaderWindowsDYLD::Terminate(); @@ -396,6 +404,7 @@ void SystemInitializerFull::Terminate() { ObjectFileELF::Terminate(); ObjectFileMachO::Terminate(); ObjectFilePECOFF::Terminate(); + wasm::ObjectFileWASM::Terminate(); ObjectContainerBSDArchive::Terminate(); ObjectContainerUniversalMachO::Terminate(); diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp index c70ab98dcdfad..df628f0d362ff 100644 --- a/lldb/source/Core/Value.cpp +++ b/lldb/source/Core/Value.cpp @@ -17,6 +17,7 @@ #include "lldb/Symbol/Variable.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ConstString.h" @@ -30,6 +31,9 @@ #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" + #include #include @@ -563,8 +567,29 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data, Process *process = exe_ctx->GetProcessPtr(); if (process) { - const size_t bytes_read = - process->ReadMemory(address, dst, byte_size, error); + bool isWasm = false; + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) { + const llvm::Triple::ArchType machine = + frame->CalculateTarget()->GetArchitecture().GetMachine(); + isWasm = (machine == llvm::Triple::wasm32); // wasm64 not supported + } + + size_t bytes_read = 0; + + if (isWasm) { + process_gdb_remote::GDBRemoteCommunicationClient *gdb_comm = + &((process_gdb_remote::ProcessGDBRemote *)process) + ->GetGDBRemote(); + int frame_index = frame->GetConcreteFrameIndex(); + if (gdb_comm->WasmReadMemory(frame_index, address, dst, + byte_size)) { + bytes_read = byte_size; + } + } else { + bytes_read = process->ReadMemory(address, dst, byte_size, error); + } + if (bytes_read != byte_size) error.SetErrorStringWithFormat( "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)", diff --git a/lldb/source/Core/ValueObjectVariable.cpp b/lldb/source/Core/ValueObjectVariable.cpp index 240ebe1fdff37..38d8c6245e41e 100644 --- a/lldb/source/Core/ValueObjectVariable.cpp +++ b/lldb/source/Core/ValueObjectVariable.cpp @@ -155,8 +155,9 @@ bool ValueObjectVariable::UpdateValue() { target); } Value old_value(m_value); + uint64_t byte_size = GetByteSize(); if (expr.Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, nullptr, - nullptr, m_value, &m_error)) { + nullptr, byte_size, m_value, &m_error)) { m_resolved_value = m_value; m_value.SetContext(Value::eContextTypeVariable, variable); diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 3789d91477375..b2dfa8c99bd29 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -36,6 +36,8 @@ #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" #include "Plugins/SymbolFile/DWARF/DWARFUnit.h" using namespace lldb; @@ -914,7 +916,8 @@ static bool Evaluate_DW_OP_entry_value(std::vector &stack, parent_frame->GetRegisterContext().get(), /*loclist_base_addr=*/LLDB_INVALID_ADDRESS, /*initial_value_ptr=*/nullptr, - /*object_address_ptr=*/nullptr, result, error_ptr)) { + /*object_address_ptr=*/nullptr, 0, result, + error_ptr)) { LLDB_LOG(log, "Evaluate_DW_OP_entry_value: call site param evaluation failed"); return false; @@ -927,18 +930,20 @@ static bool Evaluate_DW_OP_entry_value(std::vector &stack, bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope, lldb::addr_t loclist_base_load_addr, const Value *initial_value_ptr, - const Value *object_address_ptr, Value &result, + const Value *object_address_ptr, + uint64_t byte_size, Value &result, Status *error_ptr) const { ExecutionContext exe_ctx(exe_scope); return Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, initial_value_ptr, - object_address_ptr, result, error_ptr); + object_address_ptr, byte_size, result, error_ptr); } bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx, RegisterContext *reg_ctx, lldb::addr_t loclist_base_load_addr, const Value *initial_value_ptr, - const Value *object_address_ptr, Value &result, + const Value *object_address_ptr, + uint64_t byte_size, Value &result, Status *error_ptr) const { ModuleSP module_sp = m_module_wp.lock(); @@ -994,7 +999,8 @@ bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx, return DWARFExpression::Evaluate( exe_ctx, reg_ctx, module_sp, DataExtractor(m_data, offset, length), m_dwarf_cu, m_reg_kind, - initial_value_ptr, object_address_ptr, result, error_ptr); + initial_value_ptr, object_address_ptr, byte_size, result, + error_ptr); } offset += length; } @@ -1007,7 +1013,8 @@ bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx, // Not a location list, just a single expression. return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, m_data, m_dwarf_cu, m_reg_kind, initial_value_ptr, - object_address_ptr, result, error_ptr); + object_address_ptr, byte_size, result, + error_ptr); } bool DWARFExpression::Evaluate( @@ -1015,7 +1022,7 @@ bool DWARFExpression::Evaluate( lldb::ModuleSP module_sp, const DataExtractor &opcodes, const DWARFUnit *dwarf_cu, const lldb::RegisterKind reg_kind, const Value *initial_value_ptr, const Value *object_address_ptr, - Value &result, Status *error_ptr) { + uint64_t byte_size, Value &result, Status *error_ptr) { if (opcodes.GetByteSize() == 0) { if (error_ptr) @@ -1032,9 +1039,17 @@ bool DWARFExpression::Evaluate( process = exe_ctx->GetProcessPtr(); frame = exe_ctx->GetFramePtr(); } + if (reg_ctx == nullptr && frame) reg_ctx = frame->GetRegisterContext().get(); + const llvm::Triple::ArchType machine = + frame->CalculateTarget()->GetArchitecture().GetMachine(); + bool isWasm = (machine == llvm::Triple::wasm32); // wasm64 not supported yet. + process_gdb_remote::GDBRemoteCommunicationClient *gdb_comm = isWasm + ? &((process_gdb_remote::ProcessGDBRemote *)process)->GetGDBRemote() + : nullptr; + if (initial_value_ptr) stack.push_back(*initial_value_ptr); @@ -1690,6 +1705,38 @@ bool DWARFExpression::Evaluate( // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 // constant operand and pushes the result. case DW_OP_plus_uconst: + if (isWasm) { + if (byte_size < 4) { + byte_size = 4; // default to 32 bit + } + + const uint64_t uconst_value = opcodes.GetULEB128(&offset); + + // Get current user-space stack frame pointer + Scalar value; + if (!frame || !frame->GetFrameBaseValue(value, error_ptr) || + value.GetType() != Scalar::e_ulonglong) { + return false; + } + + if (byte_size <= 4096) { + uint8_t buffer[4096]; + int frame_index = frame->GetConcreteFrameIndex(); + if (!gdb_comm->WasmReadMemory( + frame_index, value.ULongLong() + uconst_value, buffer, + byte_size)) { + return false; + } + stack.push_back({buffer, static_cast(byte_size)}); + } else { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_plus_uconst value size shouldn't " + "be bigger than 4096 bytes."); + return false; + } + break; + } + if (stack.empty()) { if (error_ptr) error_ptr->SetErrorString( @@ -2572,6 +2619,37 @@ bool DWARFExpression::Evaluate( stack.back().SetValueType(Value::eValueTypeLoadAddress); } break; + case DW_OP_WASM_location: { + if (isWasm) { + int frame_index = frame->GetConcreteFrameIndex(); + uint64_t wasm_op = opcodes.GetULEB128(&offset); + uint64_t index = opcodes.GetULEB128(&offset); + uint64_t value = 0; + switch (wasm_op) { + case 0: // Local + if (!gdb_comm->GetWasmLocal(frame_index, index, value)) { + return false; + } + break; + case 1: // Global + if (!gdb_comm->GetWasmGlobal(frame_index, index, value)) { + return false; + } + break; + case 2: // Operand Stack + if (!gdb_comm->GetWasmStackValue(frame_index, index, value)) { + return false; + } + break; + default: + return false; + } + stack.push_back(Scalar(value)); + } else { + return false; + } + } break; + // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.) // OPERANDS: 1 // ULEB128: index to the .debug_addr section diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt index 9f3b2ab0e50fd..660cb8730d42b 100644 --- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt +++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(POSIX-DYLD) add_subdirectory(Static) add_subdirectory(Hexagon-DYLD) add_subdirectory(Windows-DYLD) +add_subdirectory(WASM-DYLD) \ No newline at end of file diff --git a/lldb/source/Plugins/DynamicLoader/WASM-DYLD/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/WASM-DYLD/CMakeLists.txt new file mode 100644 index 0000000000000..a4a4ac7b44e73 --- /dev/null +++ b/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 + ) diff --git a/lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.cpp b/lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.cpp new file mode 100644 index 0000000000000..02acbc50dbd8c --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.cpp @@ -0,0 +1,162 @@ +//===-- 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 "llvm/ADT/Triple.h" +#include "lldb/Utility/Log.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; +} + +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(); +} diff --git a/lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.h b/lldb/source/Plugins/DynamicLoader/WASM-DYLD/DynamicLoaderWasmDYLD.h new file mode 100644 index 0000000000000..5a62e53ca1762 --- /dev/null +++ b/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_ diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/CMakeLists.txt index 4edd667b9723d..148a9bc122883 100644 --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(ELF) add_subdirectory(Mach-O) add_subdirectory(PECOFF) add_subdirectory(JIT) +add_subdirectory(WASM) \ No newline at end of file diff --git a/lldb/source/Plugins/ObjectFile/WASM/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/WASM/CMakeLists.txt new file mode 100644 index 0000000000000..5069b6b19b95c --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/WASM/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginObjectFileWasm PLUGIN + ObjectFileWasm.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbUtility + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/ObjectFile/WASM/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/WASM/ObjectFileWasm.cpp new file mode 100644 index 0000000000000..c508c46844de4 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/WASM/ObjectFileWasm.cpp @@ -0,0 +1,423 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::wasm; + +// Binary encoding of the module header. +constexpr uint32_t kWasmMagic = 0x6d736100; // '\0asm' +constexpr uint32_t kWasmVersion = 0x01; +static const uint32_t kHeaderSize = sizeof(kWasmMagic) + sizeof(kWasmVersion); +static const uint32_t kWasmCodeSectionId = 10; + +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) { + return new ObjectFileWASM(module_sp, data_sp, data_offset, file, file_offset, + length); +} + +ObjectFile *ObjectFileWASM::CreateMemoryInstance(const ModuleSP &module_sp, + DataBufferSP &data_sp, + const ProcessSP &process_sp, + addr_t header_addr) { + if (data_sp && data_sp->GetByteSize() > 8) { + const uint32_t *magic = + reinterpret_cast(data_sp->GetBytes()); + const uint32_t *version = reinterpret_cast( + data_sp->GetBytes() + sizeof(kWasmMagic)); + if (*magic == kWasmMagic && *version == kWasmVersion) { + 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_source_mapping_url("sourceMappingURL"); + + // 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); + size_t len = section_header_data.BytesLeft(0); + if (len > 0) { + const uint8_t *data = + section_header_data.PeekData(0, section_header_data.BytesLeft(0)); + m_hash.update(llvm::ArrayRef(data, len)); + } + + lldb::offset_t offset = 0; + uint8_t section_id = 0; + uint32_t payload_len = 0; + if (GetVaruint7(section_header_data, &offset, §ion_id) && + GetVaruint32(section_header_data, &offset, &payload_len)) { + + if (section_id == 0) { + 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; + std::string sect_name(section_header_data.PeekCStr(offset), name_len); + offset += name_len; + + if (g_sect_name_source_mapping_url == sect_name.c_str()) { + 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; + m_symbols_url = + ConstString(section_header_data.PeekCStr(offset), url_len); + GetModule()->SetSymbolFileFileSpec( + FileSpec(m_symbols_url.GetStringRef())); + + 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 <= 11) { + 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 + kHeaderSize; + while (DecodeNextSection(&offset)) + ; + + // The UUID should be retrieved by a custom section in the Wasm module (still + // to be standardized). For the moment, just calculate a UUID from a MD5 hash. + llvm::MD5::MD5Result md5_res; + m_hash.final(md5_res); + m_uuid = UUID::fromData(md5_res.Bytes.data(), md5_res.Bytes.size()); + + 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 (data_sp->GetByteSize() < sizeof(kWasmMagic)) { + return 0; + } + + uint32_t magic_number = *(uint32_t *)data_sp->GetBytes(); + if (magic_number != kWasmMagic) { + 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) { +} + +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(); + + int index = 1; + for (SectionInfoCollConstIter it = m_sect_infos.begin(); + it != m_sect_infos.end(); ++it) { + const section_info §_info = *it; + + SectionType section_type = eSectionTypeInvalid; + + if (kWasmCodeSectionId == 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. + index++, // 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. + index++, // 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); + } + } + + unified_section_list = *m_sections_up; +} + +bool ObjectFileWASM::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + ModuleSP module_sp = GetModule(); + if (module_sp) { + DecodeSections(value); + + 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, value | section_sp->GetFileOffset())) { + ++num_loaded_sections; + } + } + return num_loaded_sections > 0; + } + } + return false; +} + +DataExtractor ObjectFileWASM::ReadImageData(uint64_t offset, size_t size) { + if (m_file) { + auto buffer_sp = MapFileData(m_file, size, offset); + return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize()); + } + ProcessSP process_sp(m_process_wp.lock()); + DataExtractor data; + 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); +} + +lldb::offset_t offset; +uint32_t size; +uint32_t id; +std::string name; + + +// 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); + } +} diff --git a/lldb/source/Plugins/ObjectFile/WASM/ObjectFileWasm.h b/lldb/source/Plugins/ObjectFile/WASM/ObjectFileWasm.h new file mode 100644 index 0000000000000..1c0b35bc7744a --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/WASM/ObjectFileWasm.h @@ -0,0 +1,137 @@ +//===-- 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" +#include "llvm/Support/MD5.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; + llvm::MD5 m_hash; + 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 diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index 91838d0a8d4a4..8164ec5466c88 100644 --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -52,6 +52,7 @@ add_lldb_library(lldbPluginProcessUtility PLUGIN ThreadMemory.cpp UnwindLLDB.cpp UnwindMacOSXFrameBackchain.cpp + UnwindWasm.cpp LINK_LIBS lldbBreakpoint diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp index 49a589f14989d..b9e9975bc57c5 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -1507,7 +1507,7 @@ RegisterContextLLDB::SavedLocationForRegister( cfa_val.SetValueType(Value::eValueTypeLoadAddress); Value result; Status error; - if (dwarfexpr.Evaluate(&exe_ctx, this, 0, &cfa_val, nullptr, result, + if (dwarfexpr.Evaluate(&exe_ctx, this, 0, &cfa_val, nullptr, 0, result, &error)) { addr_t val; val = result.GetScalar().ULongLong(); @@ -1858,7 +1858,7 @@ bool RegisterContextLLDB::ReadFrameAddress( dwarfexpr.SetRegisterKind(row_register_kind); Value result; Status error; - if (dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr, result, + if (dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr, 0, result, &error)) { address = result.GetScalar().ULongLong(); diff --git a/lldb/source/Plugins/Process/Utility/UnwindWasm.cpp b/lldb/source/Plugins/Process/Utility/UnwindWasm.cpp new file mode 100644 index 0000000000000..86671b652b140 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindWasm.cpp @@ -0,0 +1,77 @@ +//===-- UnwindWasm.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 "UnwindWasm.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_gdb_remote; + +lldb::RegisterContextSP +UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) { + if (m_frames.size() <= frame->GetFrameIndex()) { + return lldb::RegisterContextSP(); + } + + ProcessGDBRemote *gdb_process = + static_cast(frame->CalculateProcess().get()); + ThreadGDBRemote *gdb_thread = + static_cast(frame->CalculateThread().get()); + std::shared_ptr reg_ctx_sp = + std::make_shared( + *gdb_thread, frame->GetFrameIndex(), gdb_process->m_register_info, + false, false); + reg_ctx_sp->PrivateSetRegisterValue(0, m_frames[frame->GetFrameIndex()]); + return reg_ctx_sp; +} + +uint32_t UnwindWasm::DoGetFrameCount() { + if (!m_unwind_complete) { + m_unwind_complete = true; + m_frames.clear(); + + process_gdb_remote::ProcessGDBRemote *process = + (process_gdb_remote::ProcessGDBRemote *)GetThread().GetProcess().get(); + if (process) { + process_gdb_remote::GDBRemoteCommunicationClient *gdb_comm = + &process->GetGDBRemote(); + if (gdb_comm) { + if (!gdb_comm->GetWasmCallStack(m_frames)) { + m_frames.clear(); + } + } + } + } + return m_frames.size(); +} + +bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) { + cfa = 0; + + if (m_frames.size() == 0) { + DoGetFrameCount(); + } + + if (frame_idx == 0) { + lldb::RegisterContextSP reg_ctx_sp = GetThread().GetRegisterContext(); + pc = reg_ctx_sp->GetPC(); + return true; + } else if (frame_idx < m_frames.size()) { + pc = m_frames[frame_idx]; + return true; + } else { + pc = 0; + return false; + } +} \ No newline at end of file diff --git a/lldb/source/Plugins/Process/Utility/UnwindWasm.h b/lldb/source/Plugins/Process/Utility/UnwindWasm.h new file mode 100644 index 0000000000000..278014ca0f426 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/UnwindWasm.h @@ -0,0 +1,49 @@ +//===-- UnwindWasm.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_UnwindWasm_h_ +#define lldb_UnwindWasm_h_ + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Unwind.h" +#include + +namespace lldb_private { + +class UnwindWasm : public lldb_private::Unwind { +public: + UnwindWasm(lldb_private::Thread &thread) + : Unwind(thread), m_frames(), m_unwind_complete(false) {} + + ~UnwindWasm() override = default; + +protected: + void DoClear() override { + m_frames.clear(); + m_unwind_complete = false; + } + + uint32_t DoGetFrameCount() override; + + bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) override; + lldb::RegisterContextSP + DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + +private: + std::vector m_frames; + bool m_unwind_complete; + + // For UnwindWasm only + DISALLOW_COPY_AND_ASSIGN(UnwindWasm); +}; + +} // namespace lldb_private + +#endif // lldb_UnwindWasm_h_ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 905ebe24a6840..b571e8a660c34 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2680,6 +2680,138 @@ bool GDBRemoteCommunicationClient::GetThreadStopInfo( return false; } +bool GDBRemoteCommunicationClient::GetWasmGlobal(int frame_index, int index, + uint64_t &value) { + StreamString packet; + packet.PutCString("qWasmGlobal:"); + packet.Printf("%d;%d", frame_index, index); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != + PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + if (buffer_sp->GetByteSize() == sizeof(value)) { + memcpy((uint8_t *)(&value), buffer_sp->GetBytes(), + buffer_sp->GetByteSize()); + return true; + } + + return false; +} + +bool GDBRemoteCommunicationClient::GetWasmLocal(int frame_index, int index, + uint64_t &value) { + StreamString packet; + packet.Printf("qWasmLocal:"); + packet.Printf("%d;%d", frame_index, index); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != + PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + if (buffer_sp->GetByteSize() == sizeof(value)) { + memcpy((uint8_t *)(&value), buffer_sp->GetBytes(), + buffer_sp->GetByteSize()); + return true; + } + + return false; +} + +bool GDBRemoteCommunicationClient::GetWasmStackValue(int frame_index, int index, + uint64_t &value) { + StreamString packet; + packet.PutCString("qWasmStackValue:"); + packet.Printf("%d;%d", frame_index, index); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != + PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + DataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + if (buffer_sp->GetByteSize() == sizeof(value)) { + memcpy((uint8_t *)(&value), buffer_sp->GetBytes(), + buffer_sp->GetByteSize()); + return true; + } + + return false; +} + +bool GDBRemoteCommunicationClient::WasmReadMemory(int frame_index, + lldb::addr_t addr, void *buf, + size_t size) { + char packet[64]; + int packet_len = + ::snprintf(packet, sizeof(packet), "qWasmMem:%d;%" PRIx64 ";%" PRIx64, + frame_index, static_cast(addr), + static_cast(size)); + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response, true) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + return size == + response.GetHexBytes(llvm::MutableArrayRef( + static_cast(buf), size), + '\xdd'); + } + } + return false; +} + +bool GDBRemoteCommunicationClient::GetWasmCallStack( + std::vector &call_stack_pcs) { + call_stack_pcs.clear(); + StreamString packet; + packet.Printf("qWasmCallStack"); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != + PacketResult::Success) { + return false; + } + + if (!response.IsNormalResponse()) { + return false; + } + + addr_t buf[1024 / sizeof(addr_t)]; + size_t bytes = response.GetHexBytes( + llvm::MutableArrayRef((uint8_t *)buf, sizeof(buf)), '\xdd'); + if (bytes == 0) { + return false; + } + + for (size_t i = 0; i < bytes / sizeof(addr_t); i++) { + call_stack_pcs.push_back(buf[i]); + } + return true; +} + uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket( GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 6539286b1a45d..a88a2c3852c69 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -280,6 +280,14 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { bool GetThreadStopInfo(lldb::tid_t tid, StringExtractorGDBRemote &response); + // WebAssembly-specific commands + bool GetWasmGlobal(int frame_index, int index, uint64_t &value); + bool GetWasmLocal(int frame_index, int index, uint64_t &value); + bool GetWasmStackValue(int frame_index, int index, uint64_t &value); + bool WasmReadMemory(int frame_index, lldb::addr_t vm_addr, void *buf, + size_t size); + bool GetWasmCallStack(std::vector &call_stack_pcs); + bool SupportsGDBStoppointPacket(GDBStoppointType type) { switch (type) { case eBreakpointSoftware: diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h index b42c87b5991bc..cc2776ae587fc 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -75,6 +75,7 @@ class GDBRemoteRegisterContext : public RegisterContext { protected: friend class ThreadGDBRemote; + friend class UnwindWasm; bool ReadRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 9ea3940103b6d..9e6341e19cba3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -235,6 +235,7 @@ class ProcessGDBRemote : public Process, friend class ThreadGDBRemote; friend class GDBRemoteCommunicationClient; friend class GDBRemoteRegisterContext; + friend class UnwindWasm; /// Broadcaster event bits definitions. enum { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 42e25f727e4b8..0b763e62c0a1a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2540,7 +2540,7 @@ bool DWARFASTParserClang::ParseChildMembers( DataExtractor(debug_info_data, block_offset, block_length), die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr, - memberOffset, nullptr)) { + 0, memberOffset, nullptr)) { member_byte_offset = memberOffset.ResolveValue(nullptr).UInt(); } @@ -2978,7 +2978,7 @@ bool DWARFASTParserClang::ParseChildMembers( DataExtractor(debug_info_data, block_offset, block_length), die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr, - memberOffset, nullptr)) { + 0, memberOffset, nullptr)) { member_byte_offset = memberOffset.ResolveValue(nullptr).UInt(); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index c982d59c2830c..5ecc9037d0648 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -1687,7 +1687,7 @@ SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() { Value location_result; Status error; if (location.Evaluate(nullptr, LLDB_INVALID_ADDRESS, nullptr, - nullptr, location_result, &error)) { + nullptr, 0, location_result, &error)) { if (location_result.GetValueType() == Value::eValueTypeFileAddress) { lldb::addr_t file_addr = diff --git a/lldb/source/Plugins/SymbolVendor/CMakeLists.txt b/lldb/source/Plugins/SymbolVendor/CMakeLists.txt index 94862d5887271..2f11ad77cd3a2 100644 --- a/lldb/source/Plugins/SymbolVendor/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolVendor/CMakeLists.txt @@ -3,3 +3,4 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin") endif() add_subdirectory(ELF) +add_subdirectory(WASM) \ No newline at end of file diff --git a/lldb/source/Plugins/SymbolVendor/WASM/CMakeLists.txt b/lldb/source/Plugins/SymbolVendor/WASM/CMakeLists.txt new file mode 100644 index 0000000000000..49f6012c7bcac --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/WASM/CMakeLists.txt @@ -0,0 +1,9 @@ +add_lldb_library(lldbPluginSymbolVendorWasm PLUGIN + SymbolVendorWasm.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbPluginObjectFileWasm + ) diff --git a/lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWASM.cpp b/lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWASM.cpp new file mode 100644 index 0000000000000..8f00afbf099e0 --- /dev/null +++ b/lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWASM.cpp @@ -0,0 +1,141 @@ +//===-- 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) {} + +// Destructor +SymbolVendorWasm::~SymbolVendorWasm() {} + +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. Sadly, ObjectFileWASM won't + // be able to figure this out consistently as the symbol file may not + // have stripped the code sections, etc. + 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; } diff --git a/lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWASM.h b/lldb/source/Plugins/SymbolVendor/WASM/SymbolVendorWASM.h new file mode 100644 index 0000000000000..ea49428cd27b5 --- /dev/null +++ b/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); + ~SymbolVendorWasm() override; + + 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_ diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index c9849a9e5f09f..3a7cd23d8245c 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -1901,6 +1901,12 @@ size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, trap_opcode_size = sizeof(g_i386_opcode); } break; + case llvm::Triple::wasm32: { + static const uint8_t g_wasm_opcode[] = {0x00}; // unreachable + trap_opcode = g_wasm_opcode; + trap_opcode_size = sizeof(g_wasm_opcode); + } break; + default: llvm_unreachable( "Unhandled architecture in Platform::GetSoftwareBreakpointTrapOpcode"); diff --git a/lldb/source/Target/RegisterContext.cpp b/lldb/source/Target/RegisterContext.cpp index f29cf435d028d..b173e0eef0cda 100644 --- a/lldb/source/Target/RegisterContext.cpp +++ b/lldb/source/Target/RegisterContext.cpp @@ -86,7 +86,7 @@ RegisterContext::UpdateDynamicRegisterSize(const lldb_private::ArchSpec &arch, Value result; Status error; if (dwarf_expr.Evaluate(&exe_ctx, this, opcode_ctx, dwarf_data, nullptr, - eRegisterKindDWARF, nullptr, nullptr, result, + eRegisterKindDWARF, nullptr, nullptr, 0, result, &error)) { expr_result = result.GetScalar().SInt(-1); switch (expr_result) { diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 5e5a596e471d1..e0b2c90c91ea5 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -1092,7 +1092,7 @@ bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) { if (!m_sc.function->GetFrameBaseExpression().Evaluate( &exe_ctx, nullptr, loclist_base_addr, nullptr, nullptr, - expr_value, &m_frame_base_error)) { + 0, expr_value, &m_frame_base_error)) { // We should really have an error if evaluate returns, but in case we // don't, lets set the error to something at least. if (m_frame_base_error.Success()) diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index e12b90501103e..c8c3be3f1db26 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -9,6 +9,7 @@ #include "lldb/Target/Thread.h" #include "Plugins/Process/Utility/UnwindLLDB.h" #include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h" +#include "Plugins/Process/Utility/UnwindWasm.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" @@ -2065,6 +2066,10 @@ Unwind *Thread::GetUnwinder() { m_unwinder_up.reset(new UnwindLLDB(*this)); break; + case llvm::Triple::wasm32: + m_unwinder_up.reset(new UnwindWasm(*this)); + break; + default: if (target_arch.GetTriple().getVendor() == llvm::Triple::Apple) m_unwinder_up.reset(new UnwindMacOSXFrameBackchain(*this)); diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index 40cc4a092b0d8..eca84f18a5d12 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -216,7 +216,10 @@ static const CoreDefinition g_core_definitions[] = { ArchSpec::eCore_uknownMach32, "unknown-mach-32"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::UnknownArch, ArchSpec::eCore_uknownMach64, "unknown-mach-64"}, - {eByteOrderLittle, 4, 2, 4, llvm::Triple::arc, ArchSpec::eCore_arc, "arc"} + {eByteOrderLittle, 4, 2, 4, llvm::Triple::arc, ArchSpec::eCore_arc, "arc"}, + + {eByteOrderLittle, 4, 1, 4, llvm::Triple::wasm32, ArchSpec::eCore_wasm32, + "wasm32"} }; // Ensure that we have an entry in the g_core_definitions for each core. If you @@ -445,6 +448,8 @@ static const ArchDefinitionEntry g_elf_arch_entries[] = { LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON {ArchSpec::eCore_arc, llvm::ELF::EM_ARC_COMPACT2, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARC + {ArchSpec::eCore_wasm32, llvm::ELF::EM_WASM, LLDB_INVALID_CPUTYPE, + 0xFFFFFFFFu, 0xFFFFFFFFu}, // WebAssembly 32bit }; static const ArchDefinition g_elf_arch_def = { diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index 34a7410f74744..0be31bb2cc5ab 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -647,6 +647,8 @@ HANDLE_DW_OP(0xa9, reinterpret, 5, DWARF) // Vendor extensions: // Extensions for GNU-style thread-local storage. HANDLE_DW_OP(0xe0, GNU_push_tls_address, 0, GNU) +// Extensions for WebAssembly. +HANDLE_DW_OP(0xed, WASM_location, 0, WASM) // The GNU entry value extension. HANDLE_DW_OP(0xf3, GNU_entry_value, 0, GNU) // Extensions for Fission proposal. diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h index 93eaf368033a6..2ad201831d2bd 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -63,7 +63,8 @@ enum LLVMConstants : uint32_t { DWARF_VENDOR_GNU = 3, DWARF_VENDOR_GOOGLE = 4, DWARF_VENDOR_LLVM = 5, - DWARF_VENDOR_MIPS = 6 + DWARF_VENDOR_MIPS = 6, + DWARF_VENDOR_WASM = 7 }; /// Constants that define the DWARF format as 32 or 64 bit. diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 46edfb6260be1..10d3d620baad5 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -311,6 +311,7 @@ enum { 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. diff --git a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h index c42ca3ad6eb99..33c55e6d263bd 100644 --- a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h +++ b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h @@ -219,6 +219,18 @@ struct RegClassWeight { unsigned WeightLimit; }; +struct FrameBaseLocation { + enum LocationKind { Register, CFA, TargetIndex } Kind; + struct TargetIndexInfo { + unsigned Index; + signed Offset; + }; + union { + unsigned Reg; + TargetIndexInfo TI; + }; +}; + /// TargetRegisterInfo base class - We assume that the target defines a static /// array of TargetRegisterDesc objects that represent all of the machine /// registers that the target has. As such, we simply have to track a pointer @@ -959,6 +971,14 @@ class TargetRegisterInfo : public MCRegisterInfo { /// for values allocated in the current stack frame. virtual Register getFrameRegister(const MachineFunction &MF) const = 0; + virtual FrameBaseLocation + getFrameBaseLocation(const MachineFunction &MF) const { + FrameBaseLocation Loc; + Loc.Kind = FrameBaseLocation::Register; + Loc.Reg = getFrameRegister(MF); + return Loc; + } + /// Mark a register and all its aliases as reserved in the given set. void markSuperRegs(BitVector &RegisterSet, unsigned Reg) const; diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 8a4b4599f92e4..1c3bd0544f22e 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -874,6 +874,10 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) { OS << MI->getOperand(0).getImm(); } else if (MI->getOperand(0).isCImm()) { MI->getOperand(0).getCImm()->getValue().print(OS, false /*isSigned*/); + } else if (MI->getOperand(0).isTargetIndex()) { + auto Op = MI->getOperand(0); + OS << "!target-index(" << Op.getIndex() << "," << Op.getOffset() << ")"; + return true; } else { unsigned Reg; if (MI->getOperand(0).isReg()) { diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h index 17e39b3d3268f..060239ef3f171 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h +++ b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h @@ -20,13 +20,32 @@ namespace llvm { class AsmPrinter; +struct TargetIndexLocation { + int Index; + int Offset; + + TargetIndexLocation() = default; + TargetIndexLocation(unsigned Idx, int64_t Offset) + : Index(Idx), Offset(Offset) {} + + bool operator==(const TargetIndexLocation &Other) const { + return Index == Other.Index && Offset == Other.Offset; + } +}; + /// A single location or constant. class DbgValueLoc { /// Any complex address location expression for this DbgValueLoc. const DIExpression *Expression; /// Type of entry that this represents. - enum EntryType { E_Location, E_Integer, E_ConstantFP, E_ConstantInt }; + enum EntryType { + E_Location, + E_Integer, + E_ConstantFP, + E_ConstantInt, + E_TargetIndexLocation + }; enum EntryType EntryKind; /// Either a constant, @@ -36,8 +55,12 @@ class DbgValueLoc { const ConstantInt *CIP; } Constant; - /// Or a location in the machine frame. - MachineLocation Loc; + union { + // Or a location in the machine frame. + MachineLocation Loc; + // Or a location from target specific location. + TargetIndexLocation TIL; + }; public: DbgValueLoc(const DIExpression *Expr, int64_t i) @@ -56,8 +79,13 @@ class DbgValueLoc { : Expression(Expr), EntryKind(E_Location), Loc(Loc) { assert(cast(Expr)->isValid()); } + DbgValueLoc(const DIExpression *Expr, TargetIndexLocation Loc) + : Expression(Expr), EntryKind(E_TargetIndexLocation), TIL(Loc) {} bool isLocation() const { return EntryKind == E_Location; } + bool isTargetIndexLocation() const { + return EntryKind == E_TargetIndexLocation; + } bool isInt() const { return EntryKind == E_Integer; } bool isConstantFP() const { return EntryKind == E_ConstantFP; } bool isConstantInt() const { return EntryKind == E_ConstantInt; } @@ -65,6 +93,7 @@ class DbgValueLoc { const ConstantFP *getConstantFP() const { return Constant.CFP; } const ConstantInt *getConstantInt() const { return Constant.CIP; } MachineLocation getLoc() const { return Loc; } + TargetIndexLocation getTargetIndexLocation() const { return TIL; } bool isFragment() const { return getExpression()->isFragment(); } bool isEntryVal() const { return getExpression()->isEntryValue(); } const DIExpression *getExpression() const { return Expression; } @@ -162,6 +191,8 @@ inline bool operator==(const DbgValueLoc &A, switch (A.EntryKind) { case DbgValueLoc::E_Location: return A.Loc == B.Loc; + case DbgValueLoc::E_TargetIndexLocation: + return A.TIL == B.TIL; case DbgValueLoc::E_Integer: return A.Constant.Int == B.Constant.Int; case DbgValueLoc::E_ConstantFP: diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index a61c98ec1c189..827b7180c8791 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -391,13 +391,24 @@ DIE &DwarfCompileUnit::updateSubprogramScopeDIE(const DISubprogram *SP) { // Only include DW_AT_frame_base in full debug info if (!includeMinimalInlineScopes()) { - if (Asm->MF->getTarget().getTargetTriple().isNVPTX()) { + const TargetRegisterInfo *RI = Asm->MF->getSubtarget().getRegisterInfo(); + auto FBL = RI->getFrameBaseLocation(*Asm->MF); + if (FBL.Kind == FrameBaseLocation::CFA) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_call_frame_cfa); addBlock(*SPDie, dwarf::DW_AT_frame_base, Loc); + } else if (FBL.Kind == FrameBaseLocation::TargetIndex) { + if (FBL.TI.Offset >= 0) { + DIELoc *Loc = new (DIEValueAllocator) DIELoc; + DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); + DIExpressionCursor Cursor({}); + DwarfExpr.addTargetIndexLocation(FBL.TI.Index, FBL.TI.Offset); + DwarfExpr.addExpression(std::move(Cursor)); + addBlock(*SPDie, dwarf::DW_AT_frame_base, DwarfExpr.finalize()); + } } else { - const TargetRegisterInfo *RI = Asm->MF->getSubtarget().getRegisterInfo(); - MachineLocation Location(RI->getFrameRegister(*Asm->MF)); + assert(FBL.Kind == FrameBaseLocation::Register); + MachineLocation Location(FBL.Reg); if (Register::isPhysicalRegister(Location.getReg())) addAddress(*SPDie, dwarf::DW_AT_frame_base, Location); } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 4b8a44da3e16a..d6cc79e4e7247 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -241,6 +241,11 @@ static DbgValueLoc getDebugLocValue(const MachineInstr *MI) { MachineLocation MLoc(RegOp.getReg(), Op1.isImm()); return DbgValueLoc(Expr, MLoc); } + if (MI->getOperand(0).isTargetIndex()) { + auto Op = MI->getOperand(0); + return DbgValueLoc( + Expr, TargetIndexLocation(Op.getIndex(), Op.getOffset())); + } if (MI->getOperand(0).isImm()) return DbgValueLoc(Expr, MI->getOperand(0).getImm()); if (MI->getOperand(0).isFPImm()) @@ -2215,6 +2220,9 @@ void DwarfDebug::emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT, if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg())) return; return DwarfExpr.addExpression(std::move(Cursor)); + } else if (Value.isTargetIndexLocation()) { + TargetIndexLocation Loc = Value.getTargetIndexLocation(); + DwarfExpr.addTargetIndexLocation(Loc.Index, Loc.Offset); } else if (Value.isConstantFP()) { APInt RawBytes = Value.getConstantFP()->getValueAPF().bitcastToAPInt(); DwarfExpr.addUnsignedConstant(RawBytes); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp index 1c5a244d7c5d3..9fd56bba619b4 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -555,6 +555,14 @@ void DwarfExpression::addFragmentOffset(const DIExpression *Expr) { OffsetInBits = FragmentOffset; } +void DwarfExpression::addTargetIndexLocation(unsigned Index, int64_t Offset) { + assert(LocationKind == Implicit || LocationKind == Unknown); + LocationKind = Implicit; + emitOp(dwarf::DW_OP_WASM_location); + emitUnsigned(Index); + emitSigned(Offset); +} + void DwarfExpression::emitLegacySExt(unsigned FromBits) { // (((X >> (FromBits - 1)) * (~0)) << FromBits) | X emitOp(dwarf::DW_OP_dup); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h index 1ad46669f9b25..8f003369c49f5 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -334,9 +334,13 @@ class DwarfExpression { /// If applicable, emit an empty DW_OP_piece / DW_OP_bit_piece to advance to /// the fragment described by \c Expr. void addFragmentOffset(const DIExpression *Expr); - + void emitLegacySExt(unsigned FromBits); void emitLegacyZExt(unsigned FromBits); + + /// Emit location information expressed via target's index + offset + /// It is an extension for WebAssembly locals, globals and operand stack. + void addTargetIndexLocation(unsigned Index, int64_t Offset); }; /// DwarfExpression implementation for .debug_loc entries. diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp index 5009b1b7b4127..7d817d8a99257 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -93,6 +93,8 @@ static DescVector getDescriptions() { Descriptions[DW_OP_implicit_value] = Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeBlock); Descriptions[DW_OP_stack_value] = Desc(Op::Dwarf3); + Descriptions[DW_OP_WASM_location] = + Desc(Op::Dwarf4, Op::SizeLEB, Op::SignedSizeLEB); Descriptions[DW_OP_GNU_push_tls_address] = Desc(Op::Dwarf3); Descriptions[DW_OP_addrx] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_GNU_addr_index] = Desc(Op::Dwarf4, Op::SizeLEB); diff --git a/llvm/lib/Target/NVPTX/NVPTXRegisterInfo.h b/llvm/lib/Target/NVPTX/NVPTXRegisterInfo.h index 9ef6940daf864..8c43669415c19 100644 --- a/llvm/lib/Target/NVPTX/NVPTXRegisterInfo.h +++ b/llvm/lib/Target/NVPTX/NVPTXRegisterInfo.h @@ -44,6 +44,13 @@ class NVPTXRegisterInfo : public NVPTXGenRegisterInfo { Register getFrameRegister(const MachineFunction &MF) const override; + FrameBaseLocation + getFrameBaseLocation(const MachineFunction &MF) const override { + FrameBaseLocation Loc; + Loc.Kind = FrameBaseLocation::CFA; + return Loc; + } + ManagedStringPool *getStrPool() const { return const_cast(&ManagedStrPool); } diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index fcbd0a5082ffe..fcd48e0096b61 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -79,6 +79,10 @@ void initializeWebAssemblyRegNumberingPass(PassRegistry &); void initializeWebAssemblyPeepholePass(PassRegistry &); void initializeWebAssemblyCallIndirectFixupPass(PassRegistry &); +namespace WebAssembly { +enum TargetIndex { TI_LOCAL_START, TI_GLOBAL_START, TI_OPERAND_STACK_START }; +} // end namespace WebAssembly + } // end namespace llvm #endif diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp index 579377c9a5d75..c8190273b4d3b 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "WebAssembly.h" #include "WebAssemblyDebugValueManager.h" #include "WebAssemblyMachineFunctionInfo.h" #include "llvm/CodeGen/MachineInstr.h" @@ -43,3 +44,10 @@ void WebAssemblyDebugValueManager::clone(MachineInstr *Insert, MBB->insert(Insert, Clone); } } + +void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) { + for (auto *DBI : DbgValues) { + MachineOperand &Op = DBI->getOperand(0); + Op.ChangeToTargetIndex(llvm::WebAssembly::TI_LOCAL_START, LocalId); + } +} \ No newline at end of file diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h index 06e8805b5ad08..7eae3cb5febd8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h @@ -30,6 +30,7 @@ class WebAssemblyDebugValueManager { void move(MachineInstr *Insert); void updateReg(unsigned Reg); void clone(MachineInstr *Insert, unsigned NewReg); + void replaceWithLocal(unsigned LocalId); }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp index ef75bb215317c..bf681a8fef8f0 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp @@ -17,6 +17,7 @@ #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssembly.h" +#include "WebAssemblyDebugValueManager.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyUtilities.h" @@ -261,6 +262,8 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { .addImm(LocalId) .addReg(MI.getOperand(2).getReg()); + WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId); + MI.eraseFromParent(); Changed = true; continue; @@ -290,6 +293,9 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { } else { unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); unsigned Opc = getLocalSetOpcode(RC); + + WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId); + BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) .addImm(LocalId) .addReg(NewReg); @@ -379,6 +385,17 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { Changed = true; } + // Recording in which local we store SP. Usually global.set precede with TEE + // or GET. + if (MachineInstr *GI = MFI.SPInstr) { + auto TI = std::prev(GI->getIterator()); + if (TI->getOpcode() == WebAssembly::LOCAL_TEE_I32 || + TI->getOpcode() == WebAssembly::LOCAL_TEE_I64 || + TI->getOpcode() == WebAssembly::LOCAL_GET_I32 || + TI->getOpcode() == WebAssembly::LOCAL_GET_I64) + MFI.SPLocal = TI->getOperand(1).getImm(); + } + #ifndef NDEBUG // Assert that all registers have been stackified at this point. for (const MachineBasicBlock &MBB : MF) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp index 71eeebfada4bc..d5a45fbcd62e6 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp @@ -26,6 +26,7 @@ #include "WebAssemblyUtilities.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -122,14 +123,14 @@ bool WebAssemblyFrameLowering::needsSPWriteback( return needsSPForLocalFrame(MF) && !CanUseRedZone; } -void WebAssemblyFrameLowering::writeSPToGlobal( +MachineInstr *WebAssemblyFrameLowering::writeSPToGlobal( unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const { const auto *TII = MF.getSubtarget().getInstrInfo(); const char *ES = "__stack_pointer"; auto *SPSymbol = MF.createExternalSymbolName(ES); - BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::GLOBAL_SET_I32)) + return BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::GLOBAL_SET_I32)) .addExternalSymbol(SPSymbol) .addReg(SrcReg); } @@ -218,7 +219,8 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, .addReg(WebAssembly::SP32); } if (StackSize && needsSPWriteback(MF)) { - writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPt, DL); + auto FI = MF.getInfo(); + FI->SPInstr = writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPt, DL); } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h index fdc0f561dcd96..76a085f668ee9 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h @@ -19,6 +19,7 @@ namespace llvm { class MachineFrameInfo; +class MachineInstr; class WebAssemblyFrameLowering final : public TargetFrameLowering { public: @@ -48,7 +49,7 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering { bool needsPrologForEH(const MachineFunction &MF) const; /// Write SP back to __stack_pointer global. - void writeSPToGlobal(unsigned SrcReg, MachineFunction &MF, + MachineInstr* writeSPToGlobal(unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp index b60fd6f687fb4..8b4de517e5e6c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -14,6 +14,7 @@ #include "WebAssemblyInstrInfo.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssembly.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" #include "llvm/CodeGen/MachineFrameInfo.h" @@ -230,3 +231,12 @@ bool WebAssemblyInstrInfo::reverseBranchCondition( Cond.front() = MachineOperand::CreateImm(!Cond.front().getImm()); return false; } + +ArrayRef> +WebAssemblyInstrInfo::getSerializableTargetIndices() const { + static const std::pair TargetIndices[] = { + {WebAssembly::TI_LOCAL_START, "wasm-local-start"}, + {WebAssembly::TI_GLOBAL_START, "wasm-global-start"}, + {WebAssembly::TI_OPERAND_STACK_START, "wasm-operator-stack-start"}}; + return makeArrayRef(TargetIndices); +} \ No newline at end of file diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h index cca8b395b6f79..4965e9236e216 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.h @@ -16,6 +16,7 @@ #define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYINSTRINFO_H #include "WebAssemblyRegisterInfo.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/CodeGen/TargetInstrInfo.h" #define GET_INSTRINFO_HEADER @@ -64,6 +65,9 @@ class WebAssemblyInstrInfo final : public WebAssemblyGenInstrInfo { int *BytesAdded = nullptr) const override; bool reverseBranchCondition(SmallVectorImpl &Cond) const override; + + ArrayRef> + getSerializableTargetIndices() const override; }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h index 16e2f4392984c..cca5a8e337bc4 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h @@ -27,6 +27,8 @@ namespace yaml { struct WebAssemblyFunctionInfo; } +class MachineInstr; + /// This class is derived from MachineFunctionInfo and contains private /// WebAssembly-specific information for each MachineFunction. class WebAssemblyFunctionInfo final : public MachineFunctionInfo { @@ -60,7 +62,9 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo { bool CFGStackified = false; public: - explicit WebAssemblyFunctionInfo(MachineFunction &MF) : MF(MF) {} + explicit WebAssemblyFunctionInfo(MachineFunction &MF) + : MF(MF), SPVReg(WebAssembly::NoRegister), SPInstr(nullptr), SPLocal(-1) { + } ~WebAssemblyFunctionInfo() override; void initializeBaseYamlFields(const yaml::WebAssemblyFunctionInfo &YamlMFI); @@ -134,6 +138,10 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo { bool isCFGStackified() const { return CFGStackified; } void setCFGStackified(bool Value = true) { CFGStackified = Value; } + + unsigned SPVReg; + MachineInstr *SPInstr; + unsigned SPLocal; }; void computeLegalValueVTs(const Function &F, const TargetMachine &TM, Type *Ty, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp index 789a025794ea0..153d8d2bd5f0c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp @@ -140,6 +140,16 @@ WebAssemblyRegisterInfo::getFrameRegister(const MachineFunction &MF) const { return Regs[TFI->hasFP(MF)][TT.isArch64Bit()]; } +FrameBaseLocation +WebAssemblyRegisterInfo::getFrameBaseLocation(const MachineFunction &MF) const { + const WebAssemblyFunctionInfo &MFI = *MF.getInfo(); + FrameBaseLocation Loc; + Loc.Kind = FrameBaseLocation::TargetIndex; + signed Local = MFI.SPInstr != nullptr ? MFI.SPLocal : -1; + Loc.TI = {0, Local}; + return Loc; +} + const TargetRegisterClass * WebAssemblyRegisterInfo::getPointerRegClass(const MachineFunction &MF, unsigned Kind) const { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.h index 7880eb217dbf3..1112a157a53c5 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.h @@ -41,6 +41,9 @@ class WebAssemblyRegisterInfo final : public WebAssemblyGenRegisterInfo { // Debug information queries. Register getFrameRegister(const MachineFunction &MF) const override; + FrameBaseLocation + getFrameBaseLocation(const MachineFunction &MF) const override; + const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, unsigned Kind = 0) const override; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp index 5eafd6c54e782..d217c512ad231 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyReplacePhysRegs.cpp @@ -88,8 +88,14 @@ bool WebAssemblyReplacePhysRegs::runOnMachineFunction(MachineFunction &MF) { for (auto I = MRI.reg_begin(PReg), E = MRI.reg_end(); I != E;) { MachineOperand &MO = *I++; if (!MO.isImplicit()) { - if (VReg == WebAssembly::NoRegister) + if (VReg == WebAssembly::NoRegister) { VReg = MRI.createVirtualRegister(RC); + if (PReg == WebAssembly::SP32) { + WebAssemblyFunctionInfo &MFI = + *MF.getInfo(); + MFI.SPVReg = VReg; + } + } MO.setReg(VReg); if (MO.getParent()->isDebugValue()) MO.setIsDebug(); diff --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-ti.ll b/llvm/test/DebugInfo/WebAssembly/dbg-value-ti.ll new file mode 100644 index 0000000000000..253cd45ffacdb --- /dev/null +++ b/llvm/test/DebugInfo/WebAssembly/dbg-value-ti.ll @@ -0,0 +1,74 @@ +; RUN: llc < %s -stop-after=wasm-explicit-locals | FileCheck %s + +; Checks if DBG_VALUEs that correspond to new `local.{tee,set}` are +; using `target-index(wasm-local-start)` operands. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown-wasm" + +define hidden i32 @fib(i32 %n) local_unnamed_addr #0 !dbg !7 { +; CHECK: body: +entry: + +; CHECK: %[[REG1:.*]]:i32 = CONST_I32 1, +; CHECK: LOCAL_SET_I32 [[LOOP_LOCAL:.*]], %[[REG1]], +; CHECK: DBG_VALUE 1, $noreg, + call void @llvm.dbg.value(metadata i32 1, metadata !16, metadata !DIExpression()), !dbg !19 + %cmp8 = icmp sgt i32 %n, 0, !dbg !21 + br i1 %cmp8, label %for.body, label %for.end, !dbg !24 + +for.body: ; preds = %entry, %for.body + %b.011 = phi i32 [ %add, %for.body ], [ 1, %entry ] + %a.010 = phi i32 [ %b.011, %for.body ], [ 0, %entry ] + %i.09 = phi i32 [ %inc, %for.body ], [ 0, %entry ] + +; CHECK: %[[REG2:.*]]:i32 = LOCAL_GET_I32 [[LOOP_LOCAL]], +; CHECK: %[[REG3:.*]]:i32 = LOCAL_TEE_I32 [[TMP_LOCAL:.*]], %[[REG2]], +; CHECK: DBG_VALUE target-index(wasm-local-start) + [[TMP_LOCAL]], $noreg, + call void @llvm.dbg.value(metadata i32 %b.011, metadata !16, metadata !DIExpression()), !dbg !19 + +; CHECK: %[[REG4:.*]]:i32 = nsw ADD_I32 +; CHECK: LOCAL_SET_I32 [[LOOP_LOCAL]], %[[REG4]], +; CHECK: DBG_VALUE target-index(wasm-local-start) + [[LOOP_LOCAL]], $noreg, + %add = add nsw i32 %b.011, %a.010, !dbg !26 + %inc = add nuw nsw i32 %i.09, 1, !dbg !28 + call void @llvm.dbg.value(metadata i32 %add, metadata !16, metadata !DIExpression()), !dbg !19 + %exitcond = icmp eq i32 %inc, %n, !dbg !21 + br i1 %exitcond, label %for.end, label %for.body, !dbg !24, !llvm.loop !29 + +for.end: ; preds = %for.body, %entry + %b.0.lcssa = phi i32 [ 1, %entry ], [ %add, %for.body ], !dbg !31 + call void @llvm.dbg.value(metadata i32 %b.0.lcssa, metadata !16, metadata !DIExpression()), !dbg !19 + ret i32 %b.0.lcssa, !dbg !32 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!4} +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 8.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "", directory: "") +!2 = !{} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!7 = distinct !DISubprogram(name: "fib", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!16} +!16 = !DILocalVariable(name: "b", scope: !7, file: !1, line: 2, type: !10) +!17 = !DILocation(line: 1, column: 13, scope: !7) +!18 = !DILocation(line: 2, column: 13, scope: !7) +!19 = !DILocation(line: 2, column: 20, scope: !7) +!20 = !DILocation(line: 2, column: 7, scope: !7) +!21 = !DILocation(line: 3, column: 17, scope: !22) +!22 = distinct !DILexicalBlock(scope: !23, file: !1, line: 3, column: 3) +!23 = distinct !DILexicalBlock(scope: !7, file: !1, line: 3, column: 3) +!24 = !DILocation(line: 3, column: 3, scope: !23) +!25 = !DILocation(line: 2, column: 10, scope: !7) +!26 = !DILocation(line: 6, column: 7, scope: !27) +!27 = distinct !DILexicalBlock(scope: !22, file: !1, line: 3, column: 27) +!28 = !DILocation(line: 3, column: 23, scope: !22) +!29 = distinct !{!29, !24, !30} +!30 = !DILocation(line: 7, column: 3, scope: !23) +!31 = !DILocation(line: 0, scope: !7) +!32 = !DILocation(line: 8, column: 3, scope: !7) \ No newline at end of file