| //===-- WriteMemoryRequestHandler.cpp -------------------------------------===// |
| // |
| // 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 "DAP.h" |
| #include "JSONUtils.h" |
| #include "RequestHandler.h" |
| #include "lldb/API/SBMemoryRegionInfo.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/Base64.h" |
| |
| namespace lldb_dap { |
| |
| // Writes bytes to memory at the provided location. |
| // |
| // Clients should only call this request if the corresponding capability |
| // supportsWriteMemoryRequest is true. |
| llvm::Expected<protocol::WriteMemoryResponseBody> |
| WriteMemoryRequestHandler::Run( |
| const protocol::WriteMemoryArguments &args) const { |
| const lldb::addr_t address = args.memoryReference + args.offset.value_or(0); |
| ; |
| |
| lldb::SBProcess process = dap.target.GetProcess(); |
| if (!lldb::SBDebugger::StateIsStoppedState(process.GetState())) |
| return llvm::make_error<NotStoppedError>(); |
| |
| if (args.data.empty()) { |
| return llvm::make_error<DAPError>( |
| "Data cannot be empty value. Provide valid data"); |
| } |
| |
| // The VSCode IDE or other DAP clients send memory data as a Base64 string. |
| // This function decodes it into raw binary before writing it to the target |
| // process memory. |
| std::vector<char> output; |
| auto decode_error = llvm::decodeBase64(args.data, output); |
| |
| if (decode_error) { |
| return llvm::make_error<DAPError>( |
| llvm::toString(std::move(decode_error)).c_str()); |
| } |
| |
| lldb::SBError write_error; |
| uint64_t bytes_written = 0; |
| |
| // Write the memory. |
| if (!output.empty()) { |
| lldb::SBProcess process = dap.target.GetProcess(); |
| // If 'allowPartial' is false or missing, a debug adapter should attempt to |
| // verify the region is writable before writing, and fail the response if it |
| // is not. |
| if (!args.allowPartial.value_or(false)) { |
| // Start checking from the initial write address. |
| lldb::addr_t start_address = address; |
| // Compute the end of the write range. |
| lldb::addr_t end_address = start_address + output.size() - 1; |
| |
| while (start_address <= end_address) { |
| // Get memory region info for the given address. |
| // This provides the region's base, end, and permissions |
| // (read/write/executable). |
| lldb::SBMemoryRegionInfo region_info; |
| lldb::SBError error = |
| process.GetMemoryRegionInfo(start_address, region_info); |
| // Fail if the region info retrieval fails, is not writable, or the |
| // range exceeds the region. |
| if (!error.Success() || !region_info.IsWritable()) { |
| return llvm::make_error<DAPError>( |
| "Memory 0x" + llvm::utohexstr(args.memoryReference) + |
| " region is not writable"); |
| } |
| // If the current region covers the full requested range, stop futher |
| // iterations. |
| if (end_address <= region_info.GetRegionEnd()) { |
| break; |
| } |
| // Move to the start of the next memory region. |
| start_address = region_info.GetRegionEnd() + 1; |
| } |
| } |
| |
| bytes_written = |
| process.WriteMemory(address, static_cast<void *>(output.data()), |
| output.size(), write_error); |
| } |
| |
| if (bytes_written == 0) { |
| return llvm::make_error<DAPError>(write_error.GetCString()); |
| } |
| protocol::WriteMemoryResponseBody response; |
| response.bytesWritten = bytes_written; |
| return response; |
| } |
| |
| } // namespace lldb_dap |