================ @@ -0,0 +1,175 @@ +//===-- 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 { + +// "WriteMemoryRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Writes bytes to memory at the provided location.\n +// Clients should only call this request if the corresponding +// capability `supportsWriteMemoryRequest` is true.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "writeMemory" ] +// }, +// "arguments": { +// "$ref": "#/definitions/WriteMemoryArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "WriteMemoryArguments": { +// "type": "object", +// "description": "Arguments for `writeMemory` request.", +// "properties": { +// "memoryReference": { +// "type": "string", +// "description": "Memory reference to the base location to which +// data should be written." +// }, +// "offset": { +// "type": "integer", +// "description": "Offset (in bytes) to be applied to the reference +// location before writing data. Can be negative." +// }, +// "allowPartial": { +// "type": "boolean", +// "description": "Property to control partial writes. If true, the +// debug adapter should attempt to write memory even if the entire +// memory region is not writable. In such a case the debug adapter +// should stop after hitting the first byte of memory that cannot be +// written and return the number of bytes written in the response +// via the `offset` and `bytesWritten` properties.\nIf false or +// missing, a debug adapter should attempt to verify the region is +// writable before writing, and fail the response if it is not." +// }, +// "data": { +// "type": "string", +// "description": "Bytes to write, encoded using base64." +// } +// }, +// "required": [ "memoryReference", "data" ] +// }, +// "WriteMemoryResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to `writeMemory` request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "offset": { +// "type": "integer", +// "description": "Property that should be returned when +// `allowPartial` is true to indicate the offset of the first +// byte of data successfully written. Can be negative." +// }, +// "bytesWritten": { +// "type": "integer", +// "description": "Property that should be returned when +// `allowPartial` is true to indicate the number of bytes +// starting from address that were successfully written." +// } +// } +// } +// } +// }] +// }, +void WriteMemoryRequestHandler::operator()( + const llvm::json::Object &request) const { + llvm::json::Object response; + FillResponse(request, response); + + auto arguments = request.getObject("arguments"); + llvm::StringRef memoryReference = + GetString(arguments, "memoryReference").value_or(""); + + auto addr_opt = DecodeMemoryReference(memoryReference); + if (!addr_opt.has_value()) { + dap.SendErrorResponse(response, "Malformed memory reference: " + + memoryReference.str()); + return; + } + lldb::addr_t address = + *addr_opt + GetInteger<uint64_t>(arguments, "offset").value_or(0); + + llvm::StringRef data64 = GetString(arguments, "data").value_or(""); + if (data64.empty()) { + dap.SendErrorResponse(response, + "Data cannot be empty value. Provide valid data"); + return; + } + + // 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(data64, output); + + if (decode_error) { + dap.SendErrorResponse(response, + llvm::toString(std::move(decode_error)).c_str()); + return; + } + + bool allowPartial = GetBoolean(arguments, "allowPartial").value_or(true); + 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 (!allowPartial) { + // Compute the end of the write range. + lldb::addr_t end_address = address + output.size(); + // 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(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() || ---------------- tedwoodward wrote:
The write might spill into the next region (or more). Say we write 0x40 bytes starting at 0xff0, and we have regions from 0x0-0xfff and 0x1000-0x1fff. If both are writeable, we want to be able to write. So if the end address is > than the region end address, get the next region (region end address + 1), and test for writeable, and the end address against the 2nd region's end address. repeat until success (writeable and end < region end), or failure (not writeable). What to do if there is no region is debatable. https://github.com/llvm/llvm-project/pull/131820 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits