| 1 | //===-- WriteMemoryRequestHandler.cpp -------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "DAP.h" |
| 10 | #include "JSONUtils.h" |
| 11 | #include "RequestHandler.h" |
| 12 | #include "lldb/API/SBMemoryRegionInfo.h" |
| 13 | #include "llvm/ADT/StringExtras.h" |
| 14 | #include "llvm/Support/Base64.h" |
| 15 | |
| 16 | namespace lldb_dap { |
| 17 | |
| 18 | // Writes bytes to memory at the provided location. |
| 19 | // |
| 20 | // Clients should only call this request if the corresponding capability |
| 21 | // supportsWriteMemoryRequest is true. |
| 22 | llvm::Expected<protocol::WriteMemoryResponseBody> |
| 23 | WriteMemoryRequestHandler::Run( |
| 24 | const protocol::WriteMemoryArguments &args) const { |
| 25 | const lldb::addr_t address = args.memoryReference + args.offset; |
| 26 | |
| 27 | lldb::SBProcess process = dap.target.GetProcess(); |
| 28 | if (!lldb::SBDebugger::StateIsStoppedState(state: process.GetState())) |
| 29 | return llvm::make_error<NotStoppedError>(); |
| 30 | |
| 31 | if (args.data.empty()) { |
| 32 | return llvm::make_error<DAPError>( |
| 33 | Args: "Data cannot be empty value. Provide valid data" ); |
| 34 | } |
| 35 | |
| 36 | // The VSCode IDE or other DAP clients send memory data as a Base64 string. |
| 37 | // This function decodes it into raw binary before writing it to the target |
| 38 | // process memory. |
| 39 | std::vector<char> output; |
| 40 | auto decode_error = llvm::decodeBase64(Input: args.data, Output&: output); |
| 41 | |
| 42 | if (decode_error) { |
| 43 | return llvm::make_error<DAPError>( |
| 44 | Args: llvm::toString(E: std::move(decode_error)).c_str()); |
| 45 | } |
| 46 | |
| 47 | lldb::SBError write_error; |
| 48 | uint64_t bytes_written = 0; |
| 49 | |
| 50 | // Write the memory. |
| 51 | if (!output.empty()) { |
| 52 | lldb::SBProcess process = dap.target.GetProcess(); |
| 53 | // If 'allowPartial' is false or missing, a debug adapter should attempt to |
| 54 | // verify the region is writable before writing, and fail the response if it |
| 55 | // is not. |
| 56 | if (!args.allowPartial) { |
| 57 | // Start checking from the initial write address. |
| 58 | lldb::addr_t start_address = address; |
| 59 | // Compute the end of the write range. |
| 60 | lldb::addr_t end_address = start_address + output.size() - 1; |
| 61 | |
| 62 | while (start_address <= end_address) { |
| 63 | // Get memory region info for the given address. |
| 64 | // This provides the region's base, end, and permissions |
| 65 | // (read/write/executable). |
| 66 | lldb::SBMemoryRegionInfo region_info; |
| 67 | lldb::SBError error = |
| 68 | process.GetMemoryRegionInfo(load_addr: start_address, region_info); |
| 69 | // Fail if the region info retrieval fails, is not writable, or the |
| 70 | // range exceeds the region. |
| 71 | if (!error.Success() || !region_info.IsWritable()) { |
| 72 | return llvm::make_error<DAPError>( |
| 73 | Args: "Memory 0x" + llvm::utohexstr(X: args.memoryReference) + |
| 74 | " region is not writable" ); |
| 75 | } |
| 76 | // If the current region covers the full requested range, stop further |
| 77 | // iterations. |
| 78 | if (end_address <= region_info.GetRegionEnd()) { |
| 79 | break; |
| 80 | } |
| 81 | // Move to the start of the next memory region. |
| 82 | start_address = region_info.GetRegionEnd() + 1; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | bytes_written = |
| 87 | process.WriteMemory(addr: address, buf: static_cast<void *>(output.data()), |
| 88 | size: output.size(), error&: write_error); |
| 89 | } |
| 90 | |
| 91 | if (bytes_written == 0) { |
| 92 | return llvm::make_error<DAPError>(Args: write_error.GetCString()); |
| 93 | } |
| 94 | protocol::WriteMemoryResponseBody response; |
| 95 | response.bytesWritten = bytes_written; |
| 96 | return response; |
| 97 | } |
| 98 | |
| 99 | } // namespace lldb_dap |
| 100 | |