1 //===-- CommandObjectMemoryTag.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 "CommandObjectMemoryTag.h"
10 #include "lldb/Host/OptionParser.h"
11 #include "lldb/Interpreter/CommandReturnObject.h"
12 #include "lldb/Interpreter/OptionArgParser.h"
13 #include "lldb/Interpreter/OptionGroupFormat.h"
14 #include "lldb/Interpreter/OptionValueString.h"
15 #include "lldb/Target/Process.h"
16
17 using namespace lldb;
18 using namespace lldb_private;
19
20 #define LLDB_OPTIONS_memory_tag_read
21 #include "CommandOptions.inc"
22
23 class CommandObjectMemoryTagRead : public CommandObjectParsed {
24 public:
CommandObjectMemoryTagRead(CommandInterpreter & interpreter)25 CommandObjectMemoryTagRead(CommandInterpreter &interpreter)
26 : CommandObjectParsed(interpreter, "tag",
27 "Read memory tags for the given range of memory."
28 " Mismatched tags will be marked.",
29 nullptr,
30 eCommandRequiresTarget | eCommandRequiresProcess |
31 eCommandProcessMustBePaused) {
32 // Address
33 m_arguments.push_back(
34 CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
35 // Optional end address
36 m_arguments.push_back(CommandArgumentEntry{
37 CommandArgumentData(eArgTypeAddressOrExpression, eArgRepeatOptional)});
38 }
39
40 ~CommandObjectMemoryTagRead() override = default;
41
42 protected:
DoExecute(Args & command,CommandReturnObject & result)43 bool DoExecute(Args &command, CommandReturnObject &result) override {
44 if ((command.GetArgumentCount() < 1) || (command.GetArgumentCount() > 2)) {
45 result.AppendError(
46 "wrong number of arguments; expected at least <address-expression>, "
47 "at most <address-expression> <end-address-expression>");
48 return false;
49 }
50
51 Status error;
52 addr_t start_addr = OptionArgParser::ToAddress(
53 &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
54 if (start_addr == LLDB_INVALID_ADDRESS) {
55 result.AppendErrorWithFormatv("Invalid address expression, {0}",
56 error.AsCString());
57 return false;
58 }
59
60 // Default 1 byte beyond start, rounds up to at most 1 granule later
61 addr_t end_addr = start_addr + 1;
62
63 if (command.GetArgumentCount() > 1) {
64 end_addr = OptionArgParser::ToAddress(&m_exe_ctx, command[1].ref(),
65 LLDB_INVALID_ADDRESS, &error);
66 if (end_addr == LLDB_INVALID_ADDRESS) {
67 result.AppendErrorWithFormatv("Invalid end address expression, {0}",
68 error.AsCString());
69 return false;
70 }
71 }
72
73 Process *process = m_exe_ctx.GetProcessPtr();
74 llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
75 process->GetMemoryTagManager();
76
77 if (!tag_manager_or_err) {
78 result.SetError(Status(tag_manager_or_err.takeError()));
79 return false;
80 }
81
82 const MemoryTagManager *tag_manager = *tag_manager_or_err;
83
84 MemoryRegionInfos memory_regions;
85 // If this fails the list of regions is cleared, so we don't need to read
86 // the return status here.
87 process->GetMemoryRegions(memory_regions);
88 llvm::Expected<MemoryTagManager::TagRange> tagged_range =
89 tag_manager->MakeTaggedRange(start_addr, end_addr, memory_regions);
90
91 if (!tagged_range) {
92 result.SetError(Status(tagged_range.takeError()));
93 return false;
94 }
95
96 llvm::Expected<std::vector<lldb::addr_t>> tags = process->ReadMemoryTags(
97 tagged_range->GetRangeBase(), tagged_range->GetByteSize());
98
99 if (!tags) {
100 result.SetError(Status(tags.takeError()));
101 return false;
102 }
103
104 lldb::addr_t logical_tag = tag_manager->GetLogicalTag(start_addr);
105 result.AppendMessageWithFormatv("Logical tag: {0:x}", logical_tag);
106 result.AppendMessage("Allocation tags:");
107
108 addr_t addr = tagged_range->GetRangeBase();
109 for (auto tag : *tags) {
110 addr_t next_addr = addr + tag_manager->GetGranuleSize();
111 // Showing tagged adresses here until we have non address bit handling
112 result.AppendMessageWithFormatv("[{0:x}, {1:x}): {2:x}{3}", addr,
113 next_addr, tag,
114 logical_tag == tag ? "" : " (mismatch)");
115 addr = next_addr;
116 }
117
118 result.SetStatus(eReturnStatusSuccessFinishResult);
119 return true;
120 }
121 };
122
123 #define LLDB_OPTIONS_memory_tag_write
124 #include "CommandOptions.inc"
125
126 class CommandObjectMemoryTagWrite : public CommandObjectParsed {
127 public:
128 class OptionGroupTagWrite : public OptionGroup {
129 public:
OptionGroupTagWrite()130 OptionGroupTagWrite() : OptionGroup(), m_end_addr(LLDB_INVALID_ADDRESS) {}
131
132 ~OptionGroupTagWrite() override = default;
133
GetDefinitions()134 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
135 return llvm::makeArrayRef(g_memory_tag_write_options);
136 }
137
SetOptionValue(uint32_t option_idx,llvm::StringRef option_value,ExecutionContext * execution_context)138 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
139 ExecutionContext *execution_context) override {
140 Status status;
141 const int short_option =
142 g_memory_tag_write_options[option_idx].short_option;
143
144 switch (short_option) {
145 case 'e':
146 m_end_addr = OptionArgParser::ToAddress(execution_context, option_value,
147 LLDB_INVALID_ADDRESS, &status);
148 break;
149 default:
150 llvm_unreachable("Unimplemented option");
151 }
152
153 return status;
154 }
155
OptionParsingStarting(ExecutionContext * execution_context)156 void OptionParsingStarting(ExecutionContext *execution_context) override {
157 m_end_addr = LLDB_INVALID_ADDRESS;
158 }
159
160 lldb::addr_t m_end_addr;
161 };
162
CommandObjectMemoryTagWrite(CommandInterpreter & interpreter)163 CommandObjectMemoryTagWrite(CommandInterpreter &interpreter)
164 : CommandObjectParsed(interpreter, "tag",
165 "Write memory tags starting from the granule that "
166 "contains the given address.",
167 nullptr,
168 eCommandRequiresTarget | eCommandRequiresProcess |
169 eCommandProcessMustBePaused),
170 m_option_group(), m_tag_write_options() {
171 // Address
172 m_arguments.push_back(
173 CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
174 // One or more tag values
175 m_arguments.push_back(CommandArgumentEntry{
176 CommandArgumentData(eArgTypeValue, eArgRepeatPlus)});
177
178 m_option_group.Append(&m_tag_write_options);
179 m_option_group.Finalize();
180 }
181
182 ~CommandObjectMemoryTagWrite() override = default;
183
GetOptions()184 Options *GetOptions() override { return &m_option_group; }
185
186 protected:
DoExecute(Args & command,CommandReturnObject & result)187 bool DoExecute(Args &command, CommandReturnObject &result) override {
188 if (command.GetArgumentCount() < 2) {
189 result.AppendError("wrong number of arguments; expected "
190 "<address-expression> <tag> [<tag> [...]]");
191 return false;
192 }
193
194 Status error;
195 addr_t start_addr = OptionArgParser::ToAddress(
196 &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
197 if (start_addr == LLDB_INVALID_ADDRESS) {
198 result.AppendErrorWithFormatv("Invalid address expression, {0}",
199 error.AsCString());
200 return false;
201 }
202
203 command.Shift(); // shift off start address
204
205 std::vector<lldb::addr_t> tags;
206 for (auto &entry : command) {
207 lldb::addr_t tag_value;
208 // getAsInteger returns true on failure
209 if (entry.ref().getAsInteger(0, tag_value)) {
210 result.AppendErrorWithFormat(
211 "'%s' is not a valid unsigned decimal string value.\n",
212 entry.c_str());
213 return false;
214 }
215 tags.push_back(tag_value);
216 }
217
218 Process *process = m_exe_ctx.GetProcessPtr();
219 llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
220 process->GetMemoryTagManager();
221
222 if (!tag_manager_or_err) {
223 result.SetError(Status(tag_manager_or_err.takeError()));
224 return false;
225 }
226
227 const MemoryTagManager *tag_manager = *tag_manager_or_err;
228
229 MemoryRegionInfos memory_regions;
230 // If this fails the list of regions is cleared, so we don't need to read
231 // the return status here.
232 process->GetMemoryRegions(memory_regions);
233
234 // We have to assume start_addr is not granule aligned.
235 // So if we simply made a range:
236 // (start_addr, start_addr + (N * granule_size))
237 // We would end up with a range that isn't N granules but N+1
238 // granules. To avoid this we'll align the start first using the method that
239 // doesn't check memory attributes. (if the final range is untagged we'll
240 // handle that error later)
241 lldb::addr_t aligned_start_addr =
242 tag_manager->ExpandToGranule(MemoryTagManager::TagRange(start_addr, 1))
243 .GetRangeBase();
244
245 lldb::addr_t end_addr = 0;
246 // When you have an end address you want to align the range like tag read
247 // does. Meaning, align the start down (which we've done) and align the end
248 // up.
249 if (m_tag_write_options.m_end_addr != LLDB_INVALID_ADDRESS)
250 end_addr = m_tag_write_options.m_end_addr;
251 else
252 // Without an end address assume number of tags matches number of granules
253 // to write to
254 end_addr =
255 aligned_start_addr + (tags.size() * tag_manager->GetGranuleSize());
256
257 // Now we've aligned the start address so if we ask for another range
258 // using the number of tags N, we'll get back a range that is also N
259 // granules in size.
260 llvm::Expected<MemoryTagManager::TagRange> tagged_range =
261 tag_manager->MakeTaggedRange(aligned_start_addr, end_addr,
262 memory_regions);
263
264 if (!tagged_range) {
265 result.SetError(Status(tagged_range.takeError()));
266 return false;
267 }
268
269 Status status = process->WriteMemoryTags(tagged_range->GetRangeBase(),
270 tagged_range->GetByteSize(), tags);
271
272 if (status.Fail()) {
273 result.SetError(status);
274 return false;
275 }
276
277 result.SetStatus(eReturnStatusSuccessFinishResult);
278 return true;
279 }
280
281 OptionGroupOptions m_option_group;
282 OptionGroupTagWrite m_tag_write_options;
283 };
284
CommandObjectMemoryTag(CommandInterpreter & interpreter)285 CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter)
286 : CommandObjectMultiword(
287 interpreter, "tag", "Commands for manipulating memory tags",
288 "memory tag <sub-command> [<sub-command-options>]") {
289 CommandObjectSP read_command_object(
290 new CommandObjectMemoryTagRead(interpreter));
291 read_command_object->SetCommandName("memory tag read");
292 LoadSubCommand("read", read_command_object);
293
294 CommandObjectSP write_command_object(
295 new CommandObjectMemoryTagWrite(interpreter));
296 write_command_object->SetCommandName("memory tag write");
297 LoadSubCommand("write", write_command_object);
298 }
299
300 CommandObjectMemoryTag::~CommandObjectMemoryTag() = default;
301