| //===----------------------------------------------------------------------===// | |
| // | |
| // 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(ThreadGDBRemote &thread, | |
| uint32_t concrete_frame_idx, | |
| DynamicRegisterInfoSP reg_info_sp) | |
| : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false, | |
| false) {} | |
| RegisterContextWasm::~RegisterContextWasm() = default; | |
| uint32_t RegisterContextWasm::GetModuleID() { | |
| return GetWasmModuleID(GetPC(LLDB_INVALID_ADDRESS)); | |
| } | |
| 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; | |
| } | |
| /// A local and an operand stack value belong to the frame, while a global | |
| /// belongs to the module instance the frame is executing, which is named | |
| /// directly wherever the stub accepts one. | |
| static llvm::Expected<DataBufferSP> | |
| ReadWasmValue(ProcessWasm &process, const WasmVirtualRegisterInfo ®_info, | |
| uint32_t frame_index, uint32_t module_id) { | |
| if (reg_info.kind != eWasmTagGlobal) | |
| return process.GetWasmVariable(reg_info.kind, frame_index, reg_info.index); | |
| if (process.CanNameInstance(module_id)) | |
| return process.GetWasmGlobalForModule(module_id, reg_info.index); | |
| return process.GetWasmGlobalForFrame(frame_index, reg_info.index); | |
| } | |
| bool RegisterContextWasm::ReadRegister(const RegisterInfo *reg_info, | |
| RegisterValue &value) { | |
| ThreadWasm &wasm_thread = static_cast<ThreadWasm &>(GetThread()); | |
| // The only real register is the PC. | |
| if (reg_info->name) { | |
| // A caller frame's PC is the unwound return address, which the base | |
| // register context cannot provide because it only sees the innermost | |
| // frame's live PC. Use the PC the unwinder recorded for this frame. | |
| if (m_concrete_frame_idx > 0) { | |
| lldb::addr_t pc = wasm_thread.GetConcreteFramePC(m_concrete_frame_idx); | |
| if (pc != LLDB_INVALID_ADDRESS) { | |
| value.SetUInt(pc, reg_info->byte_size); | |
| return true; | |
| } | |
| } | |
| return GDBRemoteRegisterContext::ReadRegister(reg_info, value); | |
| } | |
| // Read the virtual registers. | |
| ProcessWasm *process = | |
| static_cast<ProcessWasm *>(wasm_thread.GetProcess().get()); | |
| if (!process) | |
| 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 = | |
| ReadWasmValue(*process, *wasm_reg_info, frame_index, GetModuleID()); | |
| if (!maybe_buffer) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Process), maybe_buffer.takeError(), | |
| "Failed to read Wasm value: {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; | |
| } |