[Lldb-commits] [lldb] 31f9960 - [lldb][AArch64] Add "memory tag read" command

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Thu Jun 24 09:35:53 PDT 2021


Author: David Spickett
Date: 2021-06-24T17:35:45+01:00
New Revision: 31f9960c38529ce805edf9764535eb0ce188cadf

URL: https://github.com/llvm/llvm-project/commit/31f9960c38529ce805edf9764535eb0ce188cadf
DIFF: https://github.com/llvm/llvm-project/commit/31f9960c38529ce805edf9764535eb0ce188cadf.diff

LOG: [lldb][AArch64] Add "memory tag read" command

This new command looks much like "memory read"
and mirrors its basic behaviour.

(lldb) memory tag read new_buf_ptr new_buf_ptr+32
Logical tag: 0x9
Allocation tags:
[0x900fffff7ffa000, 0x900fffff7ffa010): 0x9
[0x900fffff7ffa010, 0x900fffff7ffa020): 0x0

Important proprties:
* The end address is optional and defaults to reading
  1 tag if ommitted
* It is an error to try to read tags if the architecture
  or process doesn't support it, or if the range asked
  for is not tagged.
* It is an error to read an inverted range (end < begin)
  (logical tags are removed for this check so you can
  pass tagged addresses here)
* The range will be expanded to fit the tagging granule,
  so you can get more tags than simply (end-begin)/granule size.
  Whatever you get back will always cover the original range.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D97285

Added: 
    lldb/source/Commands/CommandObjectMemoryTag.cpp
    lldb/source/Commands/CommandObjectMemoryTag.h
    lldb/test/API/functionalities/memory/tag/Makefile
    lldb/test/API/functionalities/memory/tag/TestMemoryTag.py
    lldb/test/API/functionalities/memory/tag/main.cpp
    lldb/test/API/linux/aarch64/mte_tag_read/Makefile
    lldb/test/API/linux/aarch64/mte_tag_read/TestAArch64LinuxMTEMemoryTagRead.py
    lldb/test/API/linux/aarch64/mte_tag_read/main.c

Modified: 
    lldb/source/Commands/CMakeLists.txt
    lldb/source/Commands/CommandObjectMemory.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Commands/CMakeLists.txt b/lldb/source/Commands/CMakeLists.txt
index 988ff894ea679..4a6d0c5fa86d2 100644
--- a/lldb/source/Commands/CMakeLists.txt
+++ b/lldb/source/Commands/CMakeLists.txt
@@ -16,6 +16,7 @@ add_lldb_library(lldbCommands
   CommandObjectLanguage.cpp
   CommandObjectLog.cpp
   CommandObjectMemory.cpp
+  CommandObjectMemoryTag.cpp
   CommandObjectMultiword.cpp
   CommandObjectPlatform.cpp
   CommandObjectPlugin.cpp

diff  --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp
index addbf5878a56b..5487d94c90199 100644
--- a/lldb/source/Commands/CommandObjectMemory.cpp
+++ b/lldb/source/Commands/CommandObjectMemory.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "CommandObjectMemory.h"
+#include "CommandObjectMemoryTag.h"
 #include "lldb/Core/DumpDataExtractor.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Core/ValueObjectMemory.h"
@@ -1736,6 +1737,8 @@ CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter)
                  CommandObjectSP(new CommandObjectMemoryHistory(interpreter)));
   LoadSubCommand("region",
                  CommandObjectSP(new CommandObjectMemoryRegion(interpreter)));
+  LoadSubCommand("tag",
+                 CommandObjectSP(new CommandObjectMemoryTag(interpreter)));
 }
 
 CommandObjectMemory::~CommandObjectMemory() = default;

diff  --git a/lldb/source/Commands/CommandObjectMemoryTag.cpp b/lldb/source/Commands/CommandObjectMemoryTag.cpp
new file mode 100644
index 0000000000000..07dccf5c16fb0
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectMemoryTag.cpp
@@ -0,0 +1,117 @@
+//===-- CommandObjectMemoryTag.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectMemoryTag.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_memory_tag_read
+#include "CommandOptions.inc"
+
+class CommandObjectMemoryTagRead : public CommandObjectParsed {
+public:
+  CommandObjectMemoryTagRead(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "tag",
+                            "Read memory tags for the given range of memory.",
+                            nullptr,
+                            eCommandRequiresTarget | eCommandRequiresProcess |
+                                eCommandProcessMustBePaused) {
+    // Address
+    m_arguments.push_back(
+        CommandArgumentEntry{CommandArgumentData(eArgTypeAddressOrExpression)});
+    // Optional end address
+    m_arguments.push_back(CommandArgumentEntry{
+        CommandArgumentData(eArgTypeAddressOrExpression, eArgRepeatOptional)});
+  }
+
+  ~CommandObjectMemoryTagRead() override = default;
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if ((command.GetArgumentCount() < 1) || (command.GetArgumentCount() > 2)) {
+      result.AppendError(
+          "wrong number of arguments; expected at least <address-expression>, "
+          "at most <address-expression> <end-address-expression>");
+      return false;
+    }
+
+    Status error;
+    addr_t start_addr = OptionArgParser::ToAddress(
+        &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error);
+    if (start_addr == LLDB_INVALID_ADDRESS) {
+      result.AppendErrorWithFormatv("Invalid address expression, {0}",
+                                    error.AsCString());
+      return false;
+    }
+
+    // Default 1 byte beyond start, rounds up to at most 1 granule later
+    addr_t end_addr = start_addr + 1;
+
+    if (command.GetArgumentCount() > 1) {
+      end_addr = OptionArgParser::ToAddress(&m_exe_ctx, command[1].ref(),
+                                            LLDB_INVALID_ADDRESS, &error);
+      if (end_addr == LLDB_INVALID_ADDRESS) {
+        result.AppendErrorWithFormatv("Invalid end address expression, {0}",
+                                      error.AsCString());
+        return false;
+      }
+    }
+
+    Process *process = m_exe_ctx.GetProcessPtr();
+    llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
+        process->GetMemoryTagManager(start_addr, end_addr);
+
+    if (!tag_manager_or_err) {
+      result.SetError(Status(tag_manager_or_err.takeError()));
+      return false;
+    }
+
+    const MemoryTagManager *tag_manager = *tag_manager_or_err;
+    ptr
diff _t len = tag_manager->AddressDiff(end_addr, start_addr);
+    llvm::Expected<std::vector<lldb::addr_t>> tags =
+        process->ReadMemoryTags(tag_manager, start_addr, len);
+
+    if (!tags) {
+      result.SetError(Status(tags.takeError()));
+      return false;
+    }
+
+    result.AppendMessageWithFormatv("Logical tag: {0:x}",
+                                    tag_manager->GetLogicalTag(start_addr));
+    result.AppendMessage("Allocation tags:");
+
+    MemoryTagManager::TagRange initial_range(start_addr, len);
+    addr_t addr = tag_manager->ExpandToGranule(initial_range).GetRangeBase();
+    for (auto tag : *tags) {
+      addr_t next_addr = addr + tag_manager->GetGranuleSize();
+      // Showing tagged adresses here until we have non address bit handling
+      result.AppendMessageWithFormatv("[{0:x}, {1:x}): {2:x}", addr, next_addr,
+                                      tag);
+      addr = next_addr;
+    }
+
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    return true;
+  }
+};
+
+CommandObjectMemoryTag::CommandObjectMemoryTag(CommandInterpreter &interpreter)
+    : CommandObjectMultiword(
+          interpreter, "tag", "Commands for manipulating memory tags",
+          "memory tag <sub-command> [<sub-command-options>]") {
+  CommandObjectSP read_command_object(
+      new CommandObjectMemoryTagRead(interpreter));
+  read_command_object->SetCommandName("memory tag read");
+  LoadSubCommand("read", read_command_object);
+}
+
+CommandObjectMemoryTag::~CommandObjectMemoryTag() = default;

diff  --git a/lldb/source/Commands/CommandObjectMemoryTag.h b/lldb/source/Commands/CommandObjectMemoryTag.h
new file mode 100644
index 0000000000000..54909ac908119
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectMemoryTag.h
@@ -0,0 +1,25 @@
+//===-- CommandObjectMemoryTag.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORYTAG_H
+#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORYTAG_H
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+
+namespace lldb_private {
+
+class CommandObjectMemoryTag : public CommandObjectMultiword {
+public:
+  CommandObjectMemoryTag(CommandInterpreter &interpreter);
+
+  ~CommandObjectMemoryTag() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTMEMORYTAG_H

diff  --git a/lldb/test/API/functionalities/memory/tag/Makefile b/lldb/test/API/functionalities/memory/tag/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/functionalities/memory/tag/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

diff  --git a/lldb/test/API/functionalities/memory/tag/TestMemoryTag.py b/lldb/test/API/functionalities/memory/tag/TestMemoryTag.py
new file mode 100644
index 0000000000000..608845600c125
--- /dev/null
+++ b/lldb/test/API/functionalities/memory/tag/TestMemoryTag.py
@@ -0,0 +1,35 @@
+"""
+Test errors from 'memory tag' commands on unsupported platforms.
+Tests for the only supported platform, AArch64 Linux, are in
+API/linux/aarch64/.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import lldbsuite.test.lldbutil as lldbutil
+
+
+class MemoryTagTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_memory_tag_read_unsupported(self):
+        """Test that "memory tag read" errors on unsupported platforms"""
+        if self.isAArch64MTE():
+            self.skipTest("Requires a target without AArch64 MTE.")
+
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line(self, "main.cpp",
+                            line_number('main.cpp', '// Breakpoint here'),
+                                        num_expected_locations=1)
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        self.expect("memory tag read 0 1",
+                    substrs=["error: This architecture does not support memory tagging"],
+                    error=True)

diff  --git a/lldb/test/API/functionalities/memory/tag/main.cpp b/lldb/test/API/functionalities/memory/tag/main.cpp
new file mode 100644
index 0000000000000..7d61d95d35f00
--- /dev/null
+++ b/lldb/test/API/functionalities/memory/tag/main.cpp
@@ -0,0 +1,4 @@
+int main(int argc, char const *argv[]) {
+  // Breakpoint here
+  return 0;
+}

diff  --git a/lldb/test/API/linux/aarch64/mte_tag_read/Makefile b/lldb/test/API/linux/aarch64/mte_tag_read/Makefile
new file mode 100644
index 0000000000000..01745f1889eac
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_tag_read/Makefile
@@ -0,0 +1,4 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -march=armv8.5-a+memtag
+
+include Makefile.rules

diff  --git a/lldb/test/API/linux/aarch64/mte_tag_read/TestAArch64LinuxMTEMemoryTagRead.py b/lldb/test/API/linux/aarch64/mte_tag_read/TestAArch64LinuxMTEMemoryTagRead.py
new file mode 100644
index 0000000000000..aa79397c1c27a
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_tag_read/TestAArch64LinuxMTEMemoryTagRead.py
@@ -0,0 +1,126 @@
+"""
+Test "memory tag read" command on AArch64 Linux with MTE.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AArch64LinuxMTEMemoryTagReadTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    @skipUnlessAArch64MTELinuxCompiler
+    def test_mte_tag_read(self):
+        if not self.isAArch64MTE():
+            self.skipTest('Target must support MTE.')
+
+        self.build()
+        self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line(self, "main.c",
+            line_number('main.c', '// Breakpoint here'),
+            num_expected_locations=1)
+
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        if self.process().GetState() == lldb.eStateExited:
+            self.fail("Test program failed to run.")
+
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs=['stopped',
+                     'stop reason = breakpoint'])
+
+        # Argument validation
+        self.expect("memory tag read",
+                substrs=["error: wrong number of arguments; expected at least <address-expression>, "
+                         "at most <address-expression> <end-address-expression>"],
+                error=True)
+        self.expect("memory tag read buf buf+16 32",
+                substrs=["error: wrong number of arguments; expected at least <address-expression>, "
+                         "at most <address-expression> <end-address-expression>"],
+                error=True)
+        self.expect("memory tag read not_a_symbol",
+                substrs=["error: Invalid address expression, address expression \"not_a_symbol\" "
+                         "evaluation failed"],
+                error=True)
+        self.expect("memory tag read buf not_a_symbol",
+                substrs=["error: Invalid end address expression, address expression \"not_a_symbol\" "
+                         "evaluation failed"],
+                error=True)
+        # Inverted range
+        self.expect("memory tag read buf buf-16",
+                patterns=["error: End address \(0x[A-Fa-f0-9]+\) must be "
+                          "greater than the start address \(0x[A-Fa-f0-9]+\)"],
+                error=True)
+        # Range of length 0
+        self.expect("memory tag read buf buf",
+                patterns=["error: End address \(0x[A-Fa-f0-9]+\) must be "
+                          "greater than the start address \(0x[A-Fa-f0-9]+\)"],
+                error=True)
+
+
+        # Can't read from a region without tagging
+        self.expect("memory tag read non_mte_buf",
+                patterns=["error: Address range 0x[0-9A-Fa-f]+00:0x[0-9A-Fa-f]+10 is not "
+                         "in a memory tagged region"],
+                error=True)
+
+        # If there's no end address we assume 1 granule
+        self.expect("memory tag read buf",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
+
+        # Range of <1 granule is rounded up to 1 granule
+        self.expect("memory tag read buf buf+8",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
+
+        # Start address is aligned down, end aligned up
+        self.expect("memory tag read buf+8 buf+24",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0\n"
+                          "\[0x[0-9A-Fa-f]+10, 0x[0-9A-Fa-f]+20\): 0x1$"])
+
+        # You may read up to the end of the tagged region
+        # Layout is buf (MTE), buf2 (MTE), <unmapped/non MTE>
+        # so we read from the end of buf2 here.
+        self.expect("memory tag read buf2+page_size-16 buf2+page_size",
+                patterns=["Logical tag: 0x0\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+, 0x[0-9A-Fa-f]+\): 0x0$"])
+
+        # Ranges with any part outside the region will error
+        self.expect("memory tag read buf2+page_size-16 buf2+page_size+32",
+                patterns=["error: Address range 0x[0-9A-fa-f]+f0:0x[0-9A-Fa-f]+20 "
+                          "is not in a memory tagged region"],
+                error=True)
+        self.expect("memory tag read buf2+page_size",
+                patterns=["error: Address range 0x[0-9A-fa-f]+00:0x[0-9A-Fa-f]+10 "
+                          "is not in a memory tagged region"],
+                error=True)
+
+        # You can read a range that spans more than one mapping
+        # This spills into buf2 which is also MTE
+        self.expect("memory tag read buf+page_size-16 buf+page_size+16",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+f0, 0x[0-9A-Fa-f]+00\): 0xf\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])
+
+        # Tags in start/end are ignored when creating the range.
+        # So this is not an error despite start/end having 
diff erent tags
+        self.expect("memory tag read buf buf_alt_tag+16 ",
+                patterns=["Logical tag: 0x9\n"
+                          "Allocation tags:\n"
+                          "\[0x[0-9A-Fa-f]+00, 0x[0-9A-Fa-f]+10\): 0x0$"])

diff  --git a/lldb/test/API/linux/aarch64/mte_tag_read/main.c b/lldb/test/API/linux/aarch64/mte_tag_read/main.c
new file mode 100644
index 0000000000000..f35f1a794ddbc
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_tag_read/main.c
@@ -0,0 +1,77 @@
+#include <arm_acle.h>
+#include <asm/hwcap.h>
+#include <asm/mman.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+int main(int argc, char const *argv[]) {
+  // We assume that the test runner has checked we're on an MTE system
+
+  if (prctl(PR_SET_TAGGED_ADDR_CTRL,
+            PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+                // Allow all tags to be generated by the addg
+                // instruction __arm_mte_increment_tag produces.
+                (0xffff << PR_MTE_TAG_SHIFT),
+            0, 0, 0)) {
+    return 1;
+  }
+
+  size_t page_size = sysconf(_SC_PAGESIZE);
+
+  // Allocate memory with MTE
+  // We ask for two pages. One is read only so that we get
+  // 2 mappings in /proc/.../smaps so we can check reading
+  // a range across mappings.
+  // The first allocation will start at the highest address,
+  // so we allocate buf2 first to get:
+  // <low address> | buf | buf2 | <high address>
+  int prot = PROT_READ | PROT_MTE;
+  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+
+  char *buf2 = mmap(0, page_size, prot, flags, -1, 0);
+  if (buf2 == MAP_FAILED)
+    return 1;
+
+  // Writeable so we can set tags on it later
+  char *buf = mmap(0, page_size, prot | PROT_WRITE, flags, -1, 0);
+  if (buf == MAP_FAILED)
+    return 1;
+
+  // We expect the mappings to be next to each other
+  if (buf2 - buf != page_size)
+    return 1;
+
+  // And without MTE
+  char *non_mte_buf = mmap(0, page_size, PROT_READ | PROT_WRITE, flags, -1, 0);
+  if (non_mte_buf == MAP_FAILED)
+    return 1;
+
+  // Set incrementing tags until end of the first page
+  char *tagged_ptr = buf;
+  // This ignores tag bits when subtracting the addresses
+  while (__arm_mte_ptr
diff (tagged_ptr, buf) < page_size) {
+    // Set the allocation tag for this location
+    __arm_mte_set_tag(tagged_ptr);
+    // + 16 for 16 byte granules
+    // Earlier we allowed all tag values, so this will give us an
+    // incrementing pattern 0-0xF wrapping back to 0.
+    tagged_ptr = __arm_mte_increment_tag(tagged_ptr + 16, 1);
+  }
+
+  // Tag the original pointer with 9
+  buf = __arm_mte_create_random_tag(buf, ~(1 << 9));
+  // A 
diff erent tag so that buf_alt_tag > buf if you don't handle the tag
+  char *buf_alt_tag = __arm_mte_create_random_tag(buf, ~(1 << 10));
+
+  // lldb should be removing the whole top byte, not just the tags.
+  // So fill 63-60 with something non zero so we'll fail if we only remove tags.
+#define SET_TOP_NIBBLE(ptr) (char *)((size_t)(ptr) | (0xA << 60))
+  buf = SET_TOP_NIBBLE(buf);
+  buf_alt_tag = SET_TOP_NIBBLE(buf_alt_tag);
+  buf2 = SET_TOP_NIBBLE(buf2);
+
+  // Breakpoint here
+  return 0;
+}


        


More information about the lldb-commits mailing list