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