diff options
Diffstat (limited to 'lldb/source')
43 files changed, 1083 insertions, 86 deletions
diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index 0687c84..fdd6b3b 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -327,6 +327,7 @@ foreach(header endif() add_custom_target(liblldb-stage-header-${basename} DEPENDS ${staged_header}) + add_dependencies(liblldb-stage-header-${basename} lldb-sbapi-dwarf-enums) add_dependencies(liblldb-header-staging liblldb-stage-header-${basename}) add_custom_command( DEPENDS ${header} OUTPUT ${staged_header} @@ -339,6 +340,7 @@ foreach(header set(output_header $<TARGET_FILE_DIR:liblldb>/Headers/${basename}) add_custom_target(lldb-framework-fixup-header-${basename} DEPENDS ${staged_header}) + add_dependencies(lldb-framework-fixup-header-${basename} liblldb-stage-header-${basename}) add_dependencies(lldb-framework-fixup-all-headers lldb-framework-fixup-header-${basename}) add_custom_command(TARGET lldb-framework-fixup-header-${basename} POST_BUILD diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp index a2c004d..d7589cc 100644 --- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp +++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp @@ -18,11 +18,14 @@ #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Target/StackFrame.h" #include "lldb/Utility/ConstString.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" #include "lldb/ValueObject/ValueObject.h" #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" #include <regex> @@ -132,27 +135,22 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, }; // Dump `valobj` according to whether `po` was requested or not. - auto dump_val_object = [&](ValueObject &valobj) { + auto dump_val_object = [&](ValueObject &valobj) -> Error { if (is_po) { StreamString temp_result_stream; - if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) { - result.AppendError(toString(std::move(error))); - return; - } + if (Error err = valobj.Dump(temp_result_stream, dump_options)) + return err; llvm::StringRef output = temp_result_stream.GetString(); maybe_add_hint(output); result.GetOutputStream() << output; } else { - llvm::Error error = - valobj.Dump(result.GetOutputStream(), dump_options); - if (error) { - result.AppendError(toString(std::move(error))); - return; - } + if (Error err = valobj.Dump(result.GetOutputStream(), dump_options)) + return err; } m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(), m_cmd_name); result.SetStatus(eReturnStatusSuccessFinishResult); + return Error::success(); }; // First, try `expr` as a _limited_ frame variable expression path: only the @@ -186,8 +184,13 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, expr); } - dump_val_object(*valobj_sp); - return; + Error err = dump_val_object(*valobj_sp); + if (!err) + return; + + // Dump failed, continue on to expression evaluation. + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err), + "could not print frame variable '{1}': {0}", expr); } } @@ -196,8 +199,14 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, if (auto *state = target.GetPersistentExpressionStateForLanguage(language)) if (auto var_sp = state->GetVariable(expr)) if (auto valobj_sp = var_sp->GetValueObject()) { - dump_val_object(*valobj_sp); - return; + Error err = dump_val_object(*valobj_sp); + if (!err) + return; + + // Dump failed, continue on to expression evaluation. + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err), + "could not print persistent variable '{1}': {0}", + expr); } // Third, and lastly, try `expr` as a source expression to evaluate. @@ -248,10 +257,12 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, result.AppendNoteWithFormatv("ran `expression {0}{1}`", flags, expr); } - if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) - dump_val_object(*valobj_sp); - else + if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) { + if (Error err = dump_val_object(*valobj_sp)) + result.SetError(std::move(err)); + } else { result.SetStatus(eReturnStatusSuccessFinishNoResult); + } if (suppress_result) if (auto result_var_sp = diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 79bc6c8..391e277 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -91,9 +91,10 @@ void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) { m_reg_kind = reg_kind; } -static llvm::Error ReadRegisterValueAsScalar(RegisterContext *reg_ctx, - lldb::RegisterKind reg_kind, - uint32_t reg_num, Value &value) { +llvm::Error +DWARFExpression::ReadRegisterValueAsScalar(RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, + uint32_t reg_num, Value &value) { if (reg_ctx == nullptr) return llvm::createStringError("no register context in frame"); @@ -2302,7 +2303,8 @@ llvm::Expected<Value> DWARFExpression::Evaluate( default: if (dwarf_cu) { - if (dwarf_cu->ParseVendorDWARFOpcode(op, opcodes, offset, stack)) { + if (dwarf_cu->ParseVendorDWARFOpcode(op, opcodes, offset, reg_ctx, + reg_kind, stack)) { break; } } diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp index 17ea159..329768d 100644 --- a/lldb/source/Expression/Materializer.cpp +++ b/lldb/source/Expression/Materializer.cpp @@ -102,22 +102,23 @@ public: m_persistent_variable_sp->GetName(), mem, eAddressTypeLoad, map.GetAddressByteSize()); - if (m_persistent_variable_sp->m_flags & - ExpressionVariable::EVKeepInTarget) { - if (used_policy == IRMemoryMap::eAllocationPolicyMirror) { + if (used_policy == IRMemoryMap::eAllocationPolicyMirror) { + if (m_persistent_variable_sp->m_flags & + ExpressionVariable::EVKeepInTarget) { // Clear the flag if the variable will never be deallocated. Status leak_error; map.Leak(mem, leak_error); m_persistent_variable_sp->m_flags &= ~ExpressionVariable::EVNeedsAllocation; - } else { - // If the variable cannot be kept in target, clear this flag... - m_persistent_variable_sp->m_flags &= - ~ExpressionVariable::EVKeepInTarget; - // ...and set the flag to copy the value during dematerialization. - m_persistent_variable_sp->m_flags |= - ExpressionVariable::EVNeedsFreezeDry; } + } else { + // If we cannot allocate memory in the process, + // - clear the 'EVKeepInTarget' flag to ensure that 'm_live_sp' is reset + // during dematerialization, + m_persistent_variable_sp->m_flags &= ~ExpressionVariable::EVKeepInTarget; + // - set the 'EVNeedsFreezeDry' flag so that the value is copied to + // 'm_frozen_sp' during dematerialization. + m_persistent_variable_sp->m_flags |= ExpressionVariable::EVNeedsFreezeDry; } // Write the contents of the variable to the area. diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 5992b54..510f9c7 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -82,10 +82,11 @@ int __pthread_fchdir(int fildes); using namespace lldb; using namespace lldb_private; -#if !defined(__APPLE__) -// The system log is currently only meaningful on Darwin, where this means -// os_log. The meaning of a "system log" isn't as clear on other platforms, and -// therefore we don't providate a default implementation. Vendors are free to +#if !defined(__APPLE__) && !defined(_WIN32) +// The system log is currently only meaningful on Darwin and Windows. +// On Darwin, this means os_log. On Windows this means Events Viewer. +// The meaning of a "system log" isn't as clear on other platforms, and +// therefore we don't providate a default implementation. Vendors are free // to implement this function if they have a use for it. void Host::SystemLog(Severity severity, llvm::StringRef message) {} #endif diff --git a/lldb/source/Host/windows/Host.cpp b/lldb/source/Host/windows/Host.cpp index a7369e7..4e747f7 100644 --- a/lldb/source/Host/windows/Host.cpp +++ b/lldb/source/Host/windows/Host.cpp @@ -22,7 +22,9 @@ #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StructuredData.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ManagedStatic.h" // Windows includes #include <tlhelp32.h> @@ -302,3 +304,64 @@ Environment Host::GetEnvironment() { } return env; } + +/// Manages the lifecycle of a Windows Event's Source. +/// The destructor will call DeregisterEventSource. +/// This class is meant to be used with \ref llvm::ManagedStatic. +class WindowsEventLog { +public: + WindowsEventLog() : handle(RegisterEventSource(nullptr, L"lldb")) {} + + ~WindowsEventLog() { + if (handle) + DeregisterEventSource(handle); + } + + HANDLE GetHandle() const { return handle; } + +private: + HANDLE handle; +}; + +static llvm::ManagedStatic<WindowsEventLog> event_log; + +static std::wstring AnsiToUtf16(const std::string &ansi) { + if (ansi.empty()) + return {}; + + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, nullptr, 0); + if (unicode_length == 0) + return {}; + + std::wstring unicode(unicode_length, L'\0'); + MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, &unicode[0], unicode_length); + return unicode; +} + +void Host::SystemLog(Severity severity, llvm::StringRef message) { + HANDLE h = event_log->GetHandle(); + if (!h) + return; + + std::wstring wide_message = AnsiToUtf16(message.str()); + if (wide_message.empty()) + return; + + LPCWSTR msg_ptr = wide_message.c_str(); + + WORD event_type; + switch (severity) { + case lldb::eSeverityWarning: + event_type = EVENTLOG_WARNING_TYPE; + break; + case lldb::eSeverityError: + event_type = EVENTLOG_ERROR_TYPE; + break; + case lldb::eSeverityInfo: + default: + event_type = EVENTLOG_INFORMATION_TYPE; + } + + ReportEventW(h, event_type, 0, 0, nullptr, 1, 0, &msg_ptr, nullptr); +} diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 5ee19ff..6a654a0 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -119,7 +119,7 @@ lldb::StreamSP ScriptInterpreter::GetOpaqueTypeFromSBStream( SymbolContext ScriptInterpreter::GetOpaqueTypeFromSBSymbolContext( const lldb::SBSymbolContext &sb_sym_ctx) const { if (sb_sym_ctx.m_opaque_up) - return *sb_sym_ctx.m_opaque_up.get(); + return *sb_sym_ctx.m_opaque_up; return {}; } diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index 9f77fbc..214e260 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -1991,7 +1991,7 @@ void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context, const bool is_artificial = false; CXXMethodDecl *method_decl = m_clang_ast_context->AddMethodToCXXRecordType( - copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", nullptr, + copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", /*asm_label=*/{}, method_type, lldb::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 3995bc0..e5a1d2d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -894,7 +894,7 @@ ClangExpressionParser::ClangExpressionParser( m_llvm_context = std::make_unique<LLVMContext>(); m_code_generator.reset(CreateLLVMCodeGen( m_compiler->getDiagnostics(), module_name, - &m_compiler->getVirtualFileSystem(), m_compiler->getHeaderSearchOpts(), + m_compiler->getVirtualFileSystemPtr(), m_compiler->getHeaderSearchOpts(), m_compiler->getPreprocessorOpts(), m_compiler->getCodeGenOpts(), *m_llvm_context)); } diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp index 2adde02..5e429a9 100644 --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -1807,7 +1807,7 @@ RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations( Log *log = GetLog(LLDBLog::Unwind); LLDB_LOGF(log, "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find " - "corresponding load reserve insturuction", + "corresponding load reserve instruction", __FUNCTION__); return {*pc + (inst->is_rvc ? 2u : 4u)}; } @@ -1839,7 +1839,7 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence( EmulateInstructionRISCV *riscv_emulator = static_cast<EmulateInstructionRISCV *>(m_emulator_up.get()); - // Handle instructions between LR and SC. According to unprivilleged + // Handle instructions between LR and SC. According to unprivileged // RISC-V ISA there can be at most 16 instructions in the sequence. lldb::addr_t entry_pc = pc; // LR instruction address @@ -1872,7 +1872,7 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence( Log *log = GetLog(LLDBLog::Unwind); LLDB_LOGF(log, "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find " - "corresponding store conditional insturuction", + "corresponding store conditional instruction", __FUNCTION__); return {entry_pc + (lr_inst->is_rvc ? 2u : 4u)}; } diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp index 67963a7..b1efd25 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -376,9 +376,13 @@ DataExtractor ObjectFileWasm::ReadImageData(offset_t offset, uint32_t size) { DataBufferSP buffer_sp(data_up.release()); data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); } + } else if (offset < m_data.GetByteSize()) { + size = + std::min(static_cast<uint64_t>(size), m_data.GetByteSize() - offset); + return DataExtractor(m_data.GetDataStart() + offset, size, GetByteOrder(), + GetAddressByteSize()); } } - data.SetByteOrder(GetByteOrder()); return data; } diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index bd9b1b8..3413360 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -29,3 +29,4 @@ add_subdirectory(elf-core) add_subdirectory(mach-core) add_subdirectory(minidump) add_subdirectory(FreeBSDKernel) +add_subdirectory(wasm) diff --git a/lldb/source/Plugins/Process/Utility/AuxVector.cpp b/lldb/source/Plugins/Process/Utility/AuxVector.cpp index 50500a85..d660bfe 100644 --- a/lldb/source/Plugins/Process/Utility/AuxVector.cpp +++ b/lldb/source/Plugins/Process/Utility/AuxVector.cpp @@ -92,6 +92,8 @@ const char *AuxVector::GetEntryName(EntryType type) const { case ENTRY_NAME(AUXV_AT_L1D_CACHESHAPE); break; case ENTRY_NAME(AUXV_AT_L2_CACHESHAPE); break; case ENTRY_NAME(AUXV_AT_L3_CACHESHAPE); break; + case ENTRY_NAME(AUXV_FREEBSD_AT_HWCAP3); break; + case ENTRY_NAME(AUXV_FREEBSD_AT_HWCAP4); break; } #undef ENTRY_NAME diff --git a/lldb/source/Plugins/Process/Utility/AuxVector.h b/lldb/source/Plugins/Process/Utility/AuxVector.h index 7733e0f..ad6b70e 100644 --- a/lldb/source/Plugins/Process/Utility/AuxVector.h +++ b/lldb/source/Plugins/Process/Utility/AuxVector.h @@ -70,6 +70,10 @@ public: // Platform specific values which may overlap the Linux values. AUXV_FREEBSD_AT_HWCAP = 25, ///< FreeBSD specific AT_HWCAP value. + // FreeBSD and Linux use the same AT_HWCAP2 value. + AUXV_FREEBSD_AT_HWCAP3 = 38, ///< FreeBSD specific AT_HWCAP3 value. + AUXV_FREEBSD_AT_HWCAP4 = 39, ///< FreeBSD specific AT_HWCAP4 value. + }; std::optional<uint64_t> GetAuxValue(enum EntryType entry_type) const; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 36e7409..f610422 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -323,6 +323,11 @@ ProcessGDBRemote::~ProcessGDBRemote() { KillDebugserverProcess(); } +std::shared_ptr<ThreadGDBRemote> +ProcessGDBRemote::CreateThread(lldb::tid_t tid) { + return std::make_shared<ThreadGDBRemote>(*this, tid); +} + bool ProcessGDBRemote::ParsePythonTargetDefinition( const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = @@ -1594,7 +1599,7 @@ bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list, ThreadSP thread_sp( old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); if (!thread_sp) { - thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid); + thread_sp = CreateThread(tid); LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", thread_sp.get(), thread_sp->GetID()); } else { @@ -1726,7 +1731,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( if (!thread_sp) { // Create the thread if we need to - thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid); + thread_sp = CreateThread(tid); m_thread_list_real.AddThread(thread_sp); } } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 7ae3383..7c3dfb1 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -246,6 +246,8 @@ protected: ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + virtual std::shared_ptr<ThreadGDBRemote> CreateThread(lldb::tid_t tid); + bool SupportsMemoryTagging() override; /// Broadcaster event bits definitions. diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt new file mode 100644 index 0000000..779b97e --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginProcessWasm PLUGIN + ProcessWasm.cpp + RegisterContextWasm.cpp + ThreadWasm.cpp + UnwindWasm.cpp + + LINK_LIBS + lldbCore + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp new file mode 100644 index 0000000..580e8c1 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp @@ -0,0 +1,166 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ProcessWasm.h" +#include "ThreadWasm.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/Utility/DataBufferHeap.h" + +#include "lldb/Target/UnixSignals.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private::wasm; + +LLDB_PLUGIN_DEFINE(ProcessWasm) + +ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp) + : ProcessGDBRemote(target_sp, listener_sp) { + assert(target_sp); + // Wasm doesn't have any Unix-like signals as a platform concept, but pretend + // like it does to appease LLDB. + m_unix_signals_sp = UnixSignals::Create(target_sp->GetArchitecture()); +} + +void ProcessWasm::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); + }); +} + +void ProcessWasm::DebuggerInitialize(Debugger &debugger) { + ProcessGDBRemote::DebuggerInitialize(debugger); +} + +llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); } + +llvm::StringRef ProcessWasm::GetPluginNameStatic() { return "wasm"; } + +llvm::StringRef ProcessWasm::GetPluginDescriptionStatic() { + return "GDB Remote protocol based WebAssembly debugging plug-in."; +} + +void ProcessWasm::Terminate() { + PluginManager::UnregisterPlugin(ProcessWasm::CreateInstance); +} + +lldb::ProcessSP ProcessWasm::CreateInstance(lldb::TargetSP target_sp, + ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect) { + if (crash_file_path == nullptr) + return std::make_shared<ProcessWasm>(target_sp, listener_sp); + return {}; +} + +bool ProcessWasm::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + if (plugin_specified_by_name) + return true; + + if (Module *exe_module = target_sp->GetExecutableModulePointer()) { + if (ObjectFile *exe_objfile = exe_module->GetObjectFile()) + return exe_objfile->GetArchitecture().GetMachine() == + llvm::Triple::wasm32; + } + + // However, if there is no wasm module, we return false, otherwise, + // we might use ProcessWasm to attach gdb remote. + return false; +} + +std::shared_ptr<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) { + return std::make_shared<ThreadWasm>(*this, tid); +} + +size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) { + wasm_addr_t wasm_addr(vm_addr); + + switch (wasm_addr.GetType()) { + case WasmAddressType::Memory: + case WasmAddressType::Object: + return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error); + case WasmAddressType::Invalid: + error.FromErrorStringWithFormat( + "Wasm read failed for invalid address 0x%" PRIx64, vm_addr); + return 0; + } +} + +llvm::Expected<std::vector<lldb::addr_t>> +ProcessWasm::GetWasmCallStack(lldb::tid_t tid) { + StreamString packet; + packet.Printf("qWasmCallStack:"); + packet.Printf("%" PRIx64, tid); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != + GDBRemoteCommunication::PacketResult::Success) + return llvm::createStringError("failed to send qWasmCallStack"); + + if (!response.IsNormalResponse()) + return llvm::createStringError("failed to get response for qWasmCallStack"); + + WritableDataBufferSP data_buffer_sp = + std::make_shared<DataBufferHeap>(response.GetStringRef().size() / 2, 0); + const size_t bytes = response.GetHexBytes(data_buffer_sp->GetData(), '\xcc'); + if (bytes == 0 || bytes % sizeof(uint64_t) != 0) + return llvm::createStringError("invalid response for qWasmCallStack"); + + // To match the Wasm specification, the addresses are encoded in little endian + // byte order. + DataExtractor data(data_buffer_sp, lldb::eByteOrderLittle, + GetAddressByteSize()); + lldb::offset_t offset = 0; + std::vector<lldb::addr_t> call_stack_pcs; + while (offset < bytes) + call_stack_pcs.push_back(data.GetU64(&offset)); + + return call_stack_pcs; +} + +llvm::Expected<lldb::DataBufferSP> +ProcessWasm::GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index, + int index) { + StreamString packet; + switch (kind) { + case eWasmTagLocal: + packet.Printf("qWasmLocal:"); + break; + case eWasmTagGlobal: + packet.Printf("qWasmGlobal:"); + break; + case eWasmTagOperandStack: + packet.PutCString("qWasmStackValue:"); + break; + case eWasmTagNotAWasmLocation: + return llvm::createStringError("not a Wasm location"); + } + packet.Printf("%d;%d", frame_index, index); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != + GDBRemoteCommunication::PacketResult::Success) + return llvm::createStringError("failed to send Wasm variable"); + + if (!response.IsNormalResponse()) + return llvm::createStringError("failed to get response for Wasm variable"); + + WritableDataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + return buffer_sp; +} diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h new file mode 100644 index 0000000..22effe7 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H + +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "Utility/WasmVirtualRegisters.h" + +namespace lldb_private { +namespace wasm { + +/// Each WebAssembly module has separated address spaces for Code and Memory. +/// A WebAssembly module also has a Data section which, when the module is +/// loaded, gets mapped into a region in the module Memory. +enum WasmAddressType : uint8_t { Memory = 0x00, Object = 0x01, Invalid = 0xff }; + +/// For the purpose of debugging, we can represent all these separated 32-bit +/// address spaces with a single virtual 64-bit address space. The +/// wasm_addr_t provides this encoding using bitfields. +struct wasm_addr_t { + uint64_t offset : 32; + uint64_t module_id : 30; + uint64_t type : 2; + + wasm_addr_t(lldb::addr_t addr) + : offset(addr & 0x00000000ffffffff), + module_id((addr & 0x00ffffff00000000) >> 32), type(addr >> 62) {} + + wasm_addr_t(WasmAddressType type, uint32_t module_id, uint32_t offset) + : offset(offset), module_id(module_id), type(type) {} + + WasmAddressType GetType() { return static_cast<WasmAddressType>(type); } + + operator lldb::addr_t() { return *(uint64_t *)this; } +}; + +static_assert(sizeof(wasm_addr_t) == 8, ""); + +/// ProcessWasm provides the access to the Wasm program state +/// retrieved from the Wasm engine. +class ProcessWasm : public process_gdb_remote::ProcessGDBRemote { +public: + ProcessWasm(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + ~ProcessWasm() override = default; + + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + static void DebuggerInitialize(Debugger &debugger); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic(); + static llvm::StringRef GetPluginDescriptionStatic(); + + llvm::StringRef GetPluginName() override; + + size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + /// Retrieve the current call stack from the WebAssembly remote process. + llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack(lldb::tid_t tid); + + /// Query the value of a WebAssembly variable from the WebAssembly + /// remote process. + llvm::Expected<lldb::DataBufferSP> + GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index, int index); + +protected: + std::shared_ptr<process_gdb_remote::ThreadGDBRemote> + CreateThread(lldb::tid_t tid) override; + +private: + friend class UnwindWasm; + friend class ThreadWasm; + + process_gdb_remote::GDBRemoteDynamicRegisterInfoSP &GetRegisterInfo() { + return m_register_info_sp; + } + + ProcessWasm(const ProcessWasm &); + const ProcessWasm &operator=(const ProcessWasm &) = delete; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif diff --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp new file mode 100644 index 0000000..b468171 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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 "RegisterContextWasm.h" +#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "ProcessWasm.h" +#include "ThreadWasm.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/Support/Error.h" +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private::wasm; + +RegisterContextWasm::RegisterContextWasm( + wasm::ThreadWasm &thread, uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfoSP reg_info_sp) + : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false, + false) {} + +RegisterContextWasm::~RegisterContextWasm() = default; + +uint32_t RegisterContextWasm::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + return num; +} + +size_t RegisterContextWasm::GetRegisterCount() { + // Wasm has no registers. + return 0; +} + +const RegisterInfo *RegisterContextWasm::GetRegisterInfoAtIndex(size_t reg) { + uint32_t tag = GetWasmVirtualRegisterTag(reg); + if (tag == eWasmTagNotAWasmLocation) + return m_reg_info_sp->GetRegisterInfoAtIndex( + GetWasmVirtualRegisterIndex(reg)); + + auto it = m_register_map.find(reg); + if (it == m_register_map.end()) { + WasmVirtualRegisterKinds kind = static_cast<WasmVirtualRegisterKinds>(tag); + std::tie(it, std::ignore) = m_register_map.insert( + {reg, std::make_unique<WasmVirtualRegisterInfo>( + kind, GetWasmVirtualRegisterIndex(reg))}); + } + return it->second.get(); +} + +size_t RegisterContextWasm::GetRegisterSetCount() { return 0; } + +const RegisterSet *RegisterContextWasm::GetRegisterSet(size_t reg_set) { + // Wasm has no registers. + return nullptr; +} + +bool RegisterContextWasm::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + // The only real registers is the PC. + if (reg_info->name) + return GDBRemoteRegisterContext::ReadRegister(reg_info, value); + + // Read the virtual registers. + ThreadWasm *thread = static_cast<ThreadWasm *>(&GetThread()); + ProcessWasm *process = static_cast<ProcessWasm *>(thread->GetProcess().get()); + if (!thread) + return false; + + uint32_t frame_index = m_concrete_frame_idx; + WasmVirtualRegisterInfo *wasm_reg_info = + static_cast<WasmVirtualRegisterInfo *>( + const_cast<RegisterInfo *>(reg_info)); + + llvm::Expected<DataBufferSP> maybe_buffer = process->GetWasmVariable( + wasm_reg_info->kind, frame_index, wasm_reg_info->index); + if (!maybe_buffer) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), maybe_buffer.takeError(), + "Failed to read Wasm local: {0}"); + return false; + } + + DataBufferSP buffer_sp = *maybe_buffer; + DataExtractor reg_data(buffer_sp, process->GetByteOrder(), + process->GetAddressByteSize()); + wasm_reg_info->byte_size = buffer_sp->GetByteSize(); + wasm_reg_info->encoding = lldb::eEncodingUint; + + Status error = value.SetValueFromData( + *reg_info, reg_data, reg_info->byte_offset, /*partial_data_ok=*/false); + return error.Success(); +} + +void RegisterContextWasm::InvalidateAllRegisters() {} + +bool RegisterContextWasm::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + // The only real registers is the PC. + if (reg_info->name) + return GDBRemoteRegisterContext::WriteRegister(reg_info, value); + return false; +} diff --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h new file mode 100644 index 0000000..7e63eb8 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_PROCESS_WASM_REGISTERCONTEXTWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_REGISTERCONTEXTWASM_H + +#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "ThreadWasm.h" +#include "Utility/WasmVirtualRegisters.h" +#include "lldb/lldb-private-types.h" +#include <unordered_map> + +namespace lldb_private { +namespace wasm { + +class RegisterContextWasm; + +typedef std::shared_ptr<RegisterContextWasm> RegisterContextWasmSP; + +struct WasmVirtualRegisterInfo : public RegisterInfo { + WasmVirtualRegisterKinds kind; + uint32_t index; + + WasmVirtualRegisterInfo(WasmVirtualRegisterKinds kind, uint32_t index) + : RegisterInfo(), kind(kind), index(index) {} +}; + +class RegisterContextWasm + : public process_gdb_remote::GDBRemoteRegisterContext { +public: + RegisterContextWasm( + wasm::ThreadWasm &thread, uint32_t concrete_frame_idx, + process_gdb_remote::GDBRemoteDynamicRegisterInfoSP reg_info_sp); + + ~RegisterContextWasm() override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) override; + +private: + std::unordered_map<size_t, std::unique_ptr<WasmVirtualRegisterInfo>> + m_register_map; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp new file mode 100644 index 0000000..0666b75 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ThreadWasm.h" + +#include "ProcessWasm.h" +#include "RegisterContextWasm.h" +#include "UnwindWasm.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::wasm; + +Unwind &ThreadWasm::GetUnwinder() { + if (!m_unwinder_up) { + assert(CalculateTarget()->GetArchitecture().GetMachine() == + llvm::Triple::wasm32); + m_unwinder_up.reset(new wasm::UnwindWasm(*this)); + } + return *m_unwinder_up; +} + +llvm::Expected<std::vector<lldb::addr_t>> ThreadWasm::GetWasmCallStack() { + if (ProcessSP process_sp = GetProcess()) { + ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get()); + return wasm_process->GetWasmCallStack(GetID()); + } + return llvm::createStringError("no process"); +} + +lldb::RegisterContextSP +ThreadWasm::CreateRegisterContextForFrame(StackFrame *frame) { + uint32_t concrete_frame_idx = 0; + ProcessSP process_sp(GetProcess()); + ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get()); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + return std::make_shared<RegisterContextWasm>( + *this, concrete_frame_idx, wasm_process->GetRegisterInfo()); + + return GetUnwinder().CreateRegisterContextForFrame(frame); +} diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h b/lldb/source/Plugins/Process/wasm/ThreadWasm.h new file mode 100644 index 0000000..c2f5762 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H + +#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" + +namespace lldb_private { +namespace wasm { + +/// ProcessWasm provides the access to the Wasm program state +/// retrieved from the Wasm engine. +class ThreadWasm : public process_gdb_remote::ThreadGDBRemote { +public: + ThreadWasm(Process &process, lldb::tid_t tid) + : process_gdb_remote::ThreadGDBRemote(process, tid) {} + ~ThreadWasm() override = default; + + /// Retrieve the current call stack from the WebAssembly remote process. + llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack(); + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + +protected: + Unwind &GetUnwinder() override; + + ThreadWasm(const ThreadWasm &); + const ThreadWasm &operator=(const ThreadWasm &) = delete; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp new file mode 100644 index 0000000..99845dd --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// 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/ThreadGDBRemote.h" +#include "ProcessWasm.h" +#include "ThreadWasm.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; +using namespace process_gdb_remote; +using namespace wasm; + +class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext { +public: + WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread, + uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfoSP ®_info_sp, + uint64_t pc) + : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false, + false) { + // Wasm does not have a fixed set of registers but relies on a mechanism + // named local and global variables to store information such as the stack + // pointer. The only actual register is the PC. + PrivateSetRegisterValue(0, pc); + } +}; + +lldb::RegisterContextSP +UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) { + if (m_frames.size() <= frame->GetFrameIndex()) + return lldb::RegisterContextSP(); + + ThreadSP thread = frame->GetThread(); + ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *>(thread.get()); + ProcessWasm *wasm_process = + static_cast<ProcessWasm *>(thread->GetProcess().get()); + + return std::make_shared<WasmGDBRemoteRegisterContext>( + *gdb_thread, frame->GetConcreteFrameIndex(), + wasm_process->GetRegisterInfo(), m_frames[frame->GetFrameIndex()]); +} + +uint32_t UnwindWasm::DoGetFrameCount() { + if (m_unwind_complete) + return m_frames.size(); + + m_unwind_complete = true; + m_frames.clear(); + + ThreadWasm &wasm_thread = static_cast<ThreadWasm &>(GetThread()); + llvm::Expected<std::vector<lldb::addr_t>> call_stack_pcs = + wasm_thread.GetWasmCallStack(); + if (!call_stack_pcs) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Unwind), call_stack_pcs.takeError(), + "Failed to get Wasm callstack: {0}"); + m_frames.clear(); + return 0; + } + + m_frames = *call_stack_pcs; + return m_frames.size(); +} + +bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) { + if (m_frames.size() == 0) + DoGetFrameCount(); + + if (frame_idx >= m_frames.size()) + return false; + + behaves_like_zeroth_frame = (frame_idx == 0); + cfa = 0; + pc = m_frames[frame_idx]; + return true; +} diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h new file mode 100644 index 0000000..ff5e06d --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_PROCESS_WASM_UNWINDWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_UNWINDWASM_H + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Unwind.h" +#include <vector> + +namespace lldb_private { +namespace wasm { + +/// UnwindWasm manages stack unwinding for a WebAssembly process. +class UnwindWasm : public lldb_private::Unwind { +public: + UnwindWasm(lldb_private::Thread &thread) : Unwind(thread) {} + ~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<lldb::addr_t> m_frames; + bool m_unwind_complete = false; + + UnwindWasm(const UnwindWasm &); + const UnwindWasm &operator=(const UnwindWasm &) = delete; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif diff --git a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt index 212cc36..c3f1bb5 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt @@ -35,6 +35,7 @@ add_lldb_library(lldbPluginSymbolFileDWARF PLUGIN SymbolFileDWARF.cpp SymbolFileDWARFDwo.cpp SymbolFileDWARFDebugMap.cpp + SymbolFileWasm.cpp UniqueDWARFASTType.cpp LINK_COMPONENTS diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index ba65f50..e58e28a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -249,6 +249,14 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, return cv_quals; } +static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { + char const *name = die.GetMangledName(/*substitute_name_allowed*/ false); + if (!name) + return {}; + + return name; +} + TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc, const DWARFDIE &die, Log *log) { @@ -1231,7 +1239,7 @@ std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod( clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(), - attrs.mangled_name, clang_type, accessibility, attrs.is_virtual, + MakeLLDBFuncAsmLabel(die), clang_type, accessibility, attrs.is_virtual, is_static, attrs.is_inline, attrs.is_explicit, is_attr_used, attrs.is_artificial); @@ -1384,7 +1392,7 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, GetOwningClangModule(die), name, clang_type, attrs.storage, - attrs.is_inline); + attrs.is_inline, MakeLLDBFuncAsmLabel(die)); std::free(name_buf); if (has_template_params) { @@ -1394,7 +1402,7 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, GetOwningClangModule(die), attrs.name.GetStringRef(), clang_type, - attrs.storage, attrs.is_inline); + attrs.storage, attrs.is_inline, /*asm_label=*/{}); clang::FunctionTemplateDecl *func_template_decl = m_ast.CreateFunctionTemplateDecl( containing_decl_ctx, GetOwningClangModule(die), @@ -1406,20 +1414,6 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, lldbassert(function_decl); if (function_decl) { - // Attach an asm(<mangled_name>) label to the FunctionDecl. - // This ensures that clang::CodeGen emits function calls - // using symbols that are mangled according to the DW_AT_linkage_name. - // If we didn't do this, the external symbols wouldn't exactly - // match the mangled name LLDB knows about and the IRExecutionUnit - // would have to fall back to searching object files for - // approximately matching function names. The motivating - // example is generating calls to ABI-tagged template functions. - // This is done separately for member functions in - // AddMethodToCXXRecordType. - if (attrs.mangled_name) - function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( - m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false)); - LinkDeclContextToDIE(function_decl, die); const clang::FunctionProtoType *function_prototype( diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index a66af5b..94fc2e83 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -736,9 +736,11 @@ DWARFUnit::GetVendorDWARFOpcodeSize(const DataExtractor &data, bool DWARFUnit::ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, lldb::offset_t &offset, + RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, std::vector<Value> &stack) const { return GetSymbolFileDWARF().ParseVendorDWARFOpcode(op, opcodes, offset, - stack); + reg_ctx, reg_kind, stack); } bool DWARFUnit::ParseDWARFLocationList( diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h index f55400e..91a6938 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -164,9 +164,11 @@ public: const lldb::offset_t data_offset, const uint8_t op) const override; - bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, - lldb::offset_t &offset, - std::vector<Value> &stack) const override; + virtual bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, + lldb::offset_t &offset, + RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, + std::vector<Value> &stack) const override; bool ParseDWARFLocationList(const DataExtractor &data, DWARFExpressionList &loc_list) const; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 4b4a582..41ab8d1 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -41,6 +41,7 @@ #include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileWasm.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/CompileUnit.h" @@ -327,6 +328,9 @@ llvm::StringRef SymbolFileDWARF::GetPluginDescriptionStatic() { } SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFileSP objfile_sp) { + if (objfile_sp->GetArchitecture().GetTriple().isWasm()) + return new SymbolFileWasm(std::move(objfile_sp), + /*dwo_section_list*/ nullptr); return new SymbolFileDWARF(std::move(objfile_sp), /*dwo_section_list*/ nullptr); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 2dc862c..56d8ccb 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -329,6 +329,8 @@ public: virtual bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, lldb::offset_t &offset, + RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, std::vector<Value> &stack) const { return false; } @@ -556,6 +558,7 @@ protected: /// an index that identifies the .DWO or .o file. std::optional<uint64_t> m_file_index; }; + } // namespace dwarf } // namespace lldb_private::plugin diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp index c1829ab..52de3ab 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp @@ -97,9 +97,11 @@ uint64_t SymbolFileDWARFDwo::GetDebugInfoSize(bool load_all_debug_info) { } bool SymbolFileDWARFDwo::ParseVendorDWARFOpcode( - uint8_t op, const lldb_private::DataExtractor &opcodes, - lldb::offset_t &offset, std::vector<lldb_private::Value> &stack) const { - return GetBaseSymbolFile().ParseVendorDWARFOpcode(op, opcodes, offset, stack); + uint8_t op, const DataExtractor &opcodes, lldb::offset_t &offset, + RegisterContext *reg_ctx, lldb::RegisterKind reg_kind, + std::vector<Value> &stack) const { + return GetBaseSymbolFile().ParseVendorDWARFOpcode(op, opcodes, offset, + reg_ctx, reg_kind, stack); } llvm::DenseMap<const DWARFDebugInfoEntry *, Type *> & diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h index 75f5986..1ab6494 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h @@ -50,7 +50,8 @@ public: uint64_t GetDebugInfoSize(bool load_all_debug_info = false) override; bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, - lldb::offset_t &offset, + lldb::offset_t &offset, RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, std::vector<Value> &stack) const override; void FindGlobalVariables(ConstString name, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp new file mode 100644 index 0000000..e25a89c --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// 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 "SymbolFileWasm.h" +#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" +#include "Utility/WasmVirtualRegisters.h" +#include "lldb/Utility/LLDBLog.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +SymbolFileWasm::SymbolFileWasm(ObjectFileSP objfile_sp, + SectionList *dwo_section_list) + : SymbolFileDWARF(objfile_sp, dwo_section_list) {} + +SymbolFileWasm::~SymbolFileWasm() = default; + +lldb::offset_t +SymbolFileWasm::GetVendorDWARFOpcodeSize(const DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) const { + if (op != llvm::dwarf::DW_OP_WASM_location) + return LLDB_INVALID_OFFSET; + + lldb::offset_t offset = data_offset; + const uint8_t wasm_op = data.GetU8(&offset); + switch (wasm_op) { + case 0: // LOCAL + case 1: // GLOBAL_FIXED + case 2: // OPERAND_STACK + data.GetULEB128(&offset); + break; + case 3: // GLOBAL_RELOC + data.GetU32(&offset); + break; + default: + return LLDB_INVALID_OFFSET; + } + + return offset - data_offset; +} + +bool SymbolFileWasm::ParseVendorDWARFOpcode(uint8_t op, + const DataExtractor &opcodes, + lldb::offset_t &offset, + RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, + std::vector<Value> &stack) const { + if (op != llvm::dwarf::DW_OP_WASM_location) + return false; + + uint32_t index = 0; + uint8_t tag = eWasmTagNotAWasmLocation; + + /// |DWARF Location Index | WebAssembly Construct | + /// |---------------------|-----------------------| + /// |0 | Local | + /// |1 or 3 | Global | + /// |2 | Operand Stack | + const uint8_t wasm_op = opcodes.GetU8(&offset); + switch (wasm_op) { + case 0: // LOCAL + index = opcodes.GetULEB128(&offset); + tag = eWasmTagLocal; + break; + case 1: // GLOBAL_FIXED + index = opcodes.GetULEB128(&offset); + tag = eWasmTagGlobal; + break; + case 2: // OPERAND_STACK + index = opcodes.GetULEB128(&offset); + tag = eWasmTagOperandStack; + break; + case 3: // GLOBAL_RELOC + index = opcodes.GetU32(&offset); + tag = eWasmTagGlobal; + break; + default: + return false; + } + + const uint32_t reg_num = GetWasmRegister(tag, index); + + Value tmp; + llvm::Error error = DWARFExpression::ReadRegisterValueAsScalar( + reg_ctx, reg_kind, reg_num, tmp); + if (error) { + LLDB_LOG_ERROR(GetLog(DWARFLog::DebugInfo), std::move(error), "{0}"); + return false; + } + + stack.push_back(tmp); + return true; +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h new file mode 100644 index 0000000..0e0b742 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileWasm.h @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEWASM_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEWASM_H + +#include "SymbolFileDWARF.h" + +namespace lldb_private::plugin { +namespace dwarf { +class SymbolFileWasm : public SymbolFileDWARF { +public: + SymbolFileWasm(lldb::ObjectFileSP objfile_sp, SectionList *dwo_section_list); + + ~SymbolFileWasm() override; + + lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) const override; + + bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, + lldb::offset_t &offset, RegisterContext *reg_ctx, + lldb::RegisterKind reg_kind, + std::vector<Value> &stack) const override; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp index 702ec5e..8137622 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -88,7 +88,7 @@ struct CreateMethodDecl : public TypeVisitorCallbacks { MethodOptions::CompilerGenerated; function_decl = m_clang.AddMethodToCXXRecordType( parent_ty, proc_name, - /*mangled_name=*/nullptr, func_ct, /*access=*/access_type, + /*asm_label=*/{}, func_ct, /*access=*/access_type, /*is_virtual=*/is_virtual, /*is_static=*/is_static, /*is_inline=*/false, /*is_explicit=*/false, /*is_attr_used=*/false, /*is_artificial=*/is_artificial); @@ -903,7 +903,7 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, if (!function_decl) { function_decl = m_clang.AddMethodToCXXRecordType( parent_opaque_ty, func_name, - /*mangled_name=*/nullptr, func_ct, + /*asm_label=*/{}, func_ct, /*access=*/lldb::AccessType::eAccessPublic, /*is_virtual=*/false, /*is_static=*/false, /*is_inline=*/false, /*is_explicit=*/false, @@ -913,7 +913,7 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, } else { function_decl = m_clang.CreateFunctionDeclaration( parent, OptionalClangModuleID(), func_name, func_ct, func_storage, - is_inline); + is_inline, /*asm_label=*/{}); CreateFunctionParameters(func_id, *function_decl, param_count); } return function_decl; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h index 1f888f4..98b965c 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h @@ -65,7 +65,7 @@ struct CVTagRecord { } llvm::StringRef name() const { - if (m_kind == Struct || m_kind == Union) + if (m_kind == Struct || m_kind == Class) return cvclass.Name; if (m_kind == Enum) return cvenum.Name; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp index 807ee5b..1c575e9 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -111,9 +111,8 @@ void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx, bool is_artificial = (options & MethodOptions::CompilerGenerated) == MethodOptions::CompilerGenerated; m_ast_builder.clang().AddMethodToCXXRecordType( - derived_opaque_ty, name.data(), nullptr, method_ct, - access_type, attrs.isVirtual(), attrs.isStatic(), false, false, false, - is_artificial); + derived_opaque_ty, name.data(), /*asm_label=*/{}, method_ct, access_type, + attrs.isVirtual(), attrs.isStatic(), false, false, false, is_artificial); m_cxx_record_map[derived_opaque_ty].insert({name, method_ct}); } diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp index 0090d8f..8b8eac6e 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -954,7 +954,8 @@ PDBASTParser::GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol) { auto decl = m_ast.CreateFunctionDeclaration( decl_context, OptionalClangModuleID(), name, - type->GetForwardCompilerType(), storage, func->hasInlineAttribute()); + type->GetForwardCompilerType(), storage, func->hasInlineAttribute(), + /*asm_label=*/{}); std::vector<clang::ParmVarDecl *> params; if (std::unique_ptr<PDBSymbolTypeFunctionSig> sig = func->getSignature()) { @@ -1446,7 +1447,7 @@ PDBASTParser::AddRecordMethod(lldb_private::SymbolFile &symbol_file, // TODO: get mangled name for the method. return m_ast.AddMethodToCXXRecordType( record_type.GetOpaqueQualType(), name.c_str(), - /*mangled_name*/ nullptr, method_comp_type, access, method.isVirtual(), + /*asm_label=*/{}, method_comp_type, access, method.isVirtual(), method.isStatic(), method.hasInlineAttribute(), /*is_explicit*/ false, // FIXME: Need this field in CodeView. /*is_attr_used*/ false, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 256952dc..836f9fe 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -2137,7 +2137,7 @@ std::string TypeSystemClang::GetTypeNameForDecl(const NamedDecl *named_decl, FunctionDecl *TypeSystemClang::CreateFunctionDeclaration( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, llvm::StringRef name, const CompilerType &function_clang_type, - clang::StorageClass storage, bool is_inline) { + clang::StorageClass storage, bool is_inline, llvm::StringRef asm_label) { FunctionDecl *func_decl = nullptr; ASTContext &ast = getASTContext(); if (!decl_ctx) @@ -2158,6 +2158,21 @@ FunctionDecl *TypeSystemClang::CreateFunctionDeclaration( func_decl->setConstexprKind(isConstexprSpecified ? ConstexprSpecKind::Constexpr : ConstexprSpecKind::Unspecified); + + // Attach an asm(<mangled_name>) label to the FunctionDecl. + // This ensures that clang::CodeGen emits function calls + // using symbols that are mangled according to the DW_AT_linkage_name. + // If we didn't do this, the external symbols wouldn't exactly + // match the mangled name LLDB knows about and the IRExecutionUnit + // would have to fall back to searching object files for + // approximately matching function names. The motivating + // example is generating calls to ABI-tagged template functions. + // This is done separately for member functions in + // AddMethodToCXXRecordType. + if (!asm_label.empty()) + func_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(ast, asm_label, + /*literal=*/true)); + SetOwningModule(func_decl, owning_module); decl_ctx->addDecl(func_decl); @@ -7651,7 +7666,7 @@ TypeSystemClang::CreateParameterDeclarations( clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, - const char *mangled_name, const CompilerType &method_clang_type, + llvm::StringRef asm_label, const CompilerType &method_clang_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial) { if (!type || !method_clang_type.IsValid() || name.empty()) @@ -7784,10 +7799,9 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( if (is_attr_used) cxx_method_decl->addAttr(clang::UsedAttr::CreateImplicit(getASTContext())); - if (mangled_name != nullptr) { + if (!asm_label.empty()) cxx_method_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( - getASTContext(), mangled_name, /*literal=*/false)); - } + getASTContext(), asm_label, /*literal=*/true)); // Parameters on member function declarations in DWARF generally don't // have names, so we omit them when creating the ParmVarDecls. diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 63dee9d..de66bb68 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -477,7 +477,7 @@ public: clang::FunctionDecl *CreateFunctionDeclaration( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, llvm::StringRef name, const CompilerType &function_Type, - clang::StorageClass storage, bool is_inline); + clang::StorageClass storage, bool is_inline, llvm::StringRef asm_label); CompilerType CreateFunctionType(const CompilerType &result_type, @@ -1001,7 +1001,7 @@ public: clang::CXXMethodDecl *AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, - const char *mangled_name, const CompilerType &method_type, + llvm::StringRef asm_label, const CompilerType &method_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial); diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index 8000cd0..e9d9c8f 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -2076,6 +2076,13 @@ size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, trap_opcode_size = sizeof(g_loongarch_opcode); } break; + case llvm::Triple::wasm32: { + // Unreachable (0x00) triggers an unconditional trap. + static const uint8_t g_wasm_opcode[] = {0x00}; + trap_opcode = g_wasm_opcode; + trap_opcode_size = sizeof(g_wasm_opcode); + } break; + default: return 0; } diff --git a/lldb/source/Utility/WasmVirtualRegisters.h b/lldb/source/Utility/WasmVirtualRegisters.h new file mode 100644 index 0000000..404a5ae --- /dev/null +++ b/lldb/source/Utility/WasmVirtualRegisters.h @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_UTILITY_WASM_VIRTUAL_REGISTERS_H +#define LLDB_SOURCE_UTILITY_WASM_VIRTUAL_REGISTERS_H + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// LLDB doesn't have an address space to represents WebAssembly locals, +// globals and operand stacks. We encode these elements into virtual +// registers: +// +// | tag: 2 bits | index: 30 bits | +// +// Where tag is: +// 0: Not a Wasm location +// 1: Local +// 2: Global +// 3: Operand stack value +enum WasmVirtualRegisterKinds { + eWasmTagNotAWasmLocation = 0, + eWasmTagLocal = 1, + eWasmTagGlobal = 2, + eWasmTagOperandStack = 3, +}; + +static const uint32_t kWasmVirtualRegisterTagMask = 0x03; +static const uint32_t kWasmVirtualRegisterIndexMask = 0x3fffffff; +static const uint32_t kWasmVirtualRegisterTagShift = 30; + +inline uint32_t GetWasmVirtualRegisterTag(size_t reg) { + return (reg >> kWasmVirtualRegisterTagShift) & kWasmVirtualRegisterTagMask; +} + +inline uint32_t GetWasmVirtualRegisterIndex(size_t reg) { + return reg & kWasmVirtualRegisterIndexMask; +} + +inline uint32_t GetWasmRegister(uint8_t tag, uint32_t index) { + return ((tag & kWasmVirtualRegisterTagMask) << kWasmVirtualRegisterTagShift) | + (index & kWasmVirtualRegisterIndexMask); +} + +} // namespace lldb_private + +#endif |