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:
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:
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 
146     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
147       return llvm::ArrayRef(g_memory_tag_write_options);
148     }
149 
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 
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 
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 
195   Options *GetOptions() override { return &m_option_group; }
196 
197 protected:
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 
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