| //===----------------------------------------------------------------------===// | |
| // | |
| // 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()); | |
| // FIXME: LLVM's RuntimeDyld doesn't support the Wasm object format, so we | |
| // can't JIT expressions for this target. | |
| SetCanJIT(false); | |
| } | |
| void ProcessWasm::Initialize() { | |
| 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: | |
| break; | |
| } | |
| error.FromErrorStringWithFormatv( | |
| "Wasm read failed for invalid address {0:x} (type = {1:x}, module = " | |
| "{2:x}, offset = {3:x})", | |
| vm_addr, wasm_addr.GetType(), wasm_addr.GetModuleID(), | |
| wasm_addr.GetOffset()); | |
| 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; | |
| } |