-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[lldb][lldb-dap] Added support for "WriteMemory" request. #131820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 144 commits
960351c
94c7680
3235090
ff9077f
fa762d2
09ff158
ab44a69
ef4f016
35ba7f7
bd38f88
f65a580
5763225
bb18d6d
1b1ae2b
a2e4580
3795dcd
628fa40
4e2e524
4774f58
175f1d3
a6e9f66
e5c0aed
c502203
89800a8
c1bf2bd
5590afc
afcce33
281c281
624b789
023b51e
d19eb88
16c37cd
599a5ca
fe9a4b6
c91b02d
35ad541
8e3f3a5
de29728
ac84b53
7d1319c
2a0eb9d
08f40aa
2a0bc63
656c34a
673ae34
c6e552a
043895d
fe36e25
2011cb5
03e4c20
10d16b8
a6c4895
58affe7
033c6a1
7c96402
17a99a7
1e7d5f0
e6fa6a8
5069297
7e22182
ffe8dbc
4d87a93
18bcb03
9893985
e0101c1
17f8c6d
8c19498
5031df9
27d668f
dbad4c5
bac086e
5401fbe
3523cef
ab68b22
fc30dd1
724e679
76168c5
e17a0fa
466c75a
a34fe4b
a9dad2e
d096a4e
dbd26f5
be23e48
c22c9e0
4d3c68d
626ecee
90bc8c0
fa0a7c6
684b1e7
8959322
698a4b1
5400b68
e5e2908
cd186a8
98db394
8764180
55a67cd
ef23ddc
c55eece
0a8140e
9a6d643
b428148
dcdaa07
6d0d08f
895b78e
d14ac94
9a88226
6ca125a
a1d75c6
27ecce0
7311866
2122c2e
f7e9af8
683d775
32f4823
f5b7cf3
f65902a
1f77293
6f676bb
b342a42
50a2bc2
898327f
9149f89
1b84b53
cd70ba3
4f2163e
001dcd7
6417816
359a365
285f07d
cd7145c
b9bc76b
dccb38f
e8feef2
cb8e204
8b358b8
b8a5631
fb9f136
d0f95bd
15fed53
2eebc3d
a5e0830
20fc36c
75c2fe5
fc8fe46
440987f
652405a
150222e
7d25017
cdbecca
d9fccd7
2930a70
77ad557
582ab01
492f5c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| int main() { | ||
| int not_a_ptr = 666; | ||
| const char *rawptr = "dead"; | ||
| // Immutable variable, .rodata region. | ||
| static const int nonWritable = 100; | ||
| // Breakpoint | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -529,6 +529,16 @@ class TestGetTargetBreakpointsRequestHandler : public LegacyRequestHandler { | |
| void operator()(const llvm::json::Object &request) const override; | ||
| }; | ||
|
|
||
| class WriteMemoryRequestHandler : public LegacyRequestHandler { | ||
|
||
| public: | ||
| using LegacyRequestHandler::LegacyRequestHandler; | ||
| static llvm::StringLiteral GetCommand() { return "writeMemory"; } | ||
| FeatureSet GetSupportedFeatures() const override { | ||
| return {protocol::eAdapterFeatureWriteMemoryRequest}; | ||
| } | ||
| void operator()(const llvm::json::Object &request) const override; | ||
| }; | ||
|
|
||
| } // namespace lldb_dap | ||
|
|
||
| #endif | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably worth mentioning in the release notes, in |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| //===-- 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 memory_reference = | ||
| GetString(arguments, "memoryReference").value_or(""); | ||
|
|
||
| auto addr_opt = DecodeMemoryReference(memory_reference); | ||
| if (!addr_opt.has_value()) { | ||
| dap.SendErrorResponse(response, "Malformed memory reference: " + | ||
| memory_reference.str()); | ||
| return; | ||
| } | ||
| lldb::addr_t address = | ||
| *addr_opt + GetInteger<uint64_t>(arguments, "offset").value_or(0); | ||
|
|
||
| llvm::StringRef data64 = GetString(arguments, "data").value_or(""); | ||
santhoshe447 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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 allow_partial = GetBoolean(arguments, "allowPartial").value_or(true); | ||
| lldb::SBError write_error; | ||
| uint64_t bytes_written = 0; | ||
|
|
||
| // Write the memory. | ||
| if (!output.empty()) { | ||
santhoshe447 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 (!allow_partial) { | ||
| // 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()) { | ||
| dap.SendErrorResponse(response, "Memory 0x" + | ||
| llvm::utohexstr(address) + | ||
| " region is not writable"); | ||
| return; | ||
| } | ||
| // 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) { | ||
| dap.SendErrorResponse(response, write_error.GetCString()); | ||
| return; | ||
| } | ||
|
|
||
| llvm::json::Object body; | ||
| body.try_emplace("bytesWritten", std::move(bytes_written)); | ||
|
|
||
| response.try_emplace("body", std::move(body)); | ||
| dap.SendJSON(llvm::json::Value(std::move(response))); | ||
| } | ||
|
|
||
| } // namespace lldb_dap | ||
Uh oh!
There was an error while loading. Please reload this page.