[Lldb-commits] [lldb] [lldb][lldb-dap] Added support for "WriteMemory" request. (PR #131820)
Adrian Vogelsgesang via lldb-commits
lldb-commits at lists.llvm.org
Fri Mar 28 02:53:38 PDT 2025
================
@@ -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()) {
+ response["success"] = false;
+ response["message"] =
+ "Malformed memory reference: " + memoryReference.str();
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+
+ lldb::addr_t address = *addr_opt;
+ lldb::addr_t address_offset =
+ address + GetInteger<uint64_t>(arguments, "offset").value_or(0);
+
+ llvm::StringRef data64 = GetString(arguments, "data").value_or("");
+
+ // 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 decodeError = llvm::decodeBase64(data64, output);
+
+ if (decodeError) {
+ response["success"] = false;
+ EmplaceSafeString(response, "message",
+ llvm::toString(std::move(decodeError)).c_str());
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ return;
+ }
+
+ bool allowPartial = GetBoolean(arguments, "allowPartial").value_or(true);
+ lldb::SBError writeError;
+ 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 == false) {
+
+ lldb::SBMemoryRegionInfo region_info;
+ lldb::SBError error =
+ process.GetMemoryRegionInfo(address_offset, region_info);
+ if (!error.Success() || !region_info.IsWritable()) {
+ response["success"] = false;
+ EmplaceSafeString(response, "message",
+ "Memory 0x" + llvm::utohexstr(address_offset) +
+ " region is not writable");
----------------
vogelsgesang wrote:
I think relying on start and end address would be fine to make sure we cover the complete address range.
However, afaik, `MemoryRegionInfo` describes the memory permissions from the point of view of the debuggee (i.e. the program being debugged). The debugger might have more permissions, i.e. maybe (?) the debugger is able to write to memory regions which for the debuggee are marked as read-only? Not sure if this case can happen, and how to properly check which regions the debugger is allowed to write to...
@labath do you know how to properly implement this check?
https://github.com/llvm/llvm-project/pull/131820
More information about the lldb-commits
mailing list