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
19using namespace lldb;
20using namespace lldb_private;
21
22#define LLDB_OPTIONS_memory_tag_read
23#include "CommandOptions.inc"
24
25class CommandObjectMemoryTagRead : public CommandObjectParsed {
26public:
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
44protected:
45  void 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;
51    }
52
53    Status error;
54    addr_t start_addr = OptionArgParser::ToRawAddress(
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;
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::ToRawAddress(&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;
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;
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;
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;
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  }
132};
133
134#define LLDB_OPTIONS_memory_tag_write
135#include "CommandOptions.inc"
136
137class CommandObjectMemoryTagWrite : public CommandObjectParsed {
138public:
139  class OptionGroupTagWrite : public OptionGroup {
140  public:
141    OptionGroupTagWrite() = default;
142
143    ~OptionGroupTagWrite() override = default;
144
145    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
146      return llvm::ArrayRef(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::ToRawAddress(
158            execution_context, option_value, 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 = LLDB_INVALID_ADDRESS;
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
196protected:
197  void 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;
202    }
203
204    Status error;
205    addr_t start_addr = OptionArgParser::ToRawAddress(
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;
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;
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;
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;
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;
295    }
296
297    result.SetStatus(eReturnStatusSuccessFinishResult);
298  }
299
300  OptionGroupOptions m_option_group;
301  OptionGroupTagWrite m_tag_write_options;
302};
303
304CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter)
305    : CommandObjectMultiword(
306          interpreter, "tag", "Commands for manipulating memory tags",
307          "memory tag <sub-command> [<sub-command-options>]") {
308  CommandObjectSP read_command_object(
309      new CommandObjectMemoryTagRead(interpreter));
310  read_command_object->SetCommandName("memory tag read");
311  LoadSubCommand("read", read_command_object);
312
313  CommandObjectSP write_command_object(
314      new CommandObjectMemoryTagWrite(interpreter));
315  write_command_object->SetCommandName("memory tag write");
316  LoadSubCommand("write", write_command_object);
317}
318
319CommandObjectMemoryTag::~CommandObjectMemoryTag() = default;
320