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