[Lldb-commits] [lldb] e0f2744 - [lldb][AArch64] Add class for managing memory tags

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Thu Jun 24 07:10:10 PDT 2021


Author: David Spickett
Date: 2021-06-24T15:10:01+01:00
New Revision: e0f2744a115c41e8d295b93dd74b39535c852390

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

LOG: [lldb][AArch64] Add class for managing memory tags

This adds the MemoryTagManager class and a specialisation
of that class for AArch64 MTE tags. It provides a generic
interface for various tagging operations.
Adding/removing tags, diffing tagged pointers, etc.

Later patches will use this manager to handle memory tags
in generic code in both lldb and lldb-server.
Since it will be used in both, the base class header is in
lldb/Target.
(MemoryRegionInfo is another example of this pattern)

Reviewed By: omjavaid

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

Added: 
    lldb/include/lldb/Target/MemoryTagManager.h
    lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
    lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
    lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp

Modified: 
    lldb/source/Plugins/Process/Utility/CMakeLists.txt
    lldb/unittests/Process/Utility/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/MemoryTagManager.h b/lldb/include/lldb/Target/MemoryTagManager.h
new file mode 100644
index 0000000000000..a0b94b19cebb2
--- /dev/null
+++ b/lldb/include/lldb/Target/MemoryTagManager.h
@@ -0,0 +1,86 @@
+//===-- MemoryTagManager.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_TARGET_MEMORYTAGMANAGER_H
+#define LLDB_TARGET_MEMORYTAGMANAGER_H
+
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/lldb-private.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+
+// This interface allows high level commands to handle memory tags
+// in a generic way.
+//
+// Definitions:
+//   logical tag    - the tag stored in a pointer
+//   allocation tag - the tag stored in hardware
+//                    (e.g. special memory, cache line bits)
+//   granule        - number of bytes of memory a single tag applies to
+
+class MemoryTagManager {
+public:
+  typedef Range<lldb::addr_t, size_t> TagRange;
+
+  // Extract the logical tag from a pointer
+  // The tag is returned as a plain value, with any shifts removed.
+  // For example if your tags are stored in bits 56-60 then the logical tag
+  // you get will have been shifted down 56 before being returned.
+  virtual lldb::addr_t GetLogicalTag(lldb::addr_t addr) const = 0;
+
+  // Remove non address bits from a pointer
+  virtual lldb::addr_t RemoveNonAddressBits(lldb::addr_t addr) const = 0;
+
+  // Return the 
diff erence between two addresses, ignoring any logical tags they
+  // have. If your tags are just part of a larger set of ignored bits, this
+  // should ignore all those bits.
+  virtual ptr
diff _t AddressDiff(lldb::addr_t addr1,
+                                lldb::addr_t addr2) const = 0;
+
+  // Return the number of bytes a single tag covers
+  virtual lldb::addr_t GetGranuleSize() const = 0;
+
+  // Align an address range to granule boundaries.
+  // So that reading memory tags for the new range returns
+  // tags that will cover the original range.
+  //
+  // Say your granules are 16 bytes and you want
+  // tags for 16 bytes of memory starting from address 8.
+  // 1 granule isn't enough because it only covers addresses
+  // 0-16, we want addresses 8-24. So the range must be
+  // expanded to 2 granules.
+  virtual TagRange ExpandToGranule(TagRange range) const = 0;
+
+  // Return the type value to use in GDB protocol qMemTags packets to read
+  // allocation tags. This is named "Allocation" specifically because the spec
+  // allows for logical tags to be read the same way, though we do not use that.
+  //
+  // This value is unique within a given architecture. Meaning that 
diff erent
+  // tagging schemes within the same architecture should use unique values,
+  // but other architectures can overlap those values.
+  virtual int32_t GetAllocationTagType() const = 0;
+
+  // Return the number of bytes a single tag will be packed into during
+  // transport. For example an MTE tag is 4 bits but occupies 1 byte during
+  // transport.
+  virtual size_t GetTagSizeInBytes() const = 0;
+
+  // Unpack tags from their stored format (e.g. gdb qMemTags data) into seperate
+  // tags. Checks that each tag is within the expected value range and that the
+  // number of tags found matches the number of granules we originally asked
+  // for.
+  virtual llvm::Expected<std::vector<lldb::addr_t>>
+  UnpackTagsData(const std::vector<uint8_t> &tags, size_t granules) const = 0;
+
+  virtual ~MemoryTagManager() {}
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_MEMORYTAGMANAGER_H

diff  --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
index c780b27149efc..14318763f6a06 100644
--- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
@@ -8,6 +8,7 @@ add_lldb_library(lldbPluginProcessUtility
   InferiorCallPOSIX.cpp
   LinuxProcMaps.cpp
   LinuxSignals.cpp
+  MemoryTagManagerAArch64MTE.cpp
   MipsLinuxSignals.cpp
   NativeProcessSoftwareSingleStep.cpp
   NativeRegisterContextDBReg_arm64.cpp

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
new file mode 100644
index 0000000000000..964f0364d65e9
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
@@ -0,0 +1,98 @@
+//===-- MemoryTagManagerAArch64MTE.cpp --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "MemoryTagManagerAArch64MTE.h"
+
+using namespace lldb_private;
+
+static const unsigned MTE_START_BIT = 56;
+static const unsigned MTE_TAG_MAX = 0xf;
+static const unsigned MTE_GRANULE_SIZE = 16;
+
+lldb::addr_t
+MemoryTagManagerAArch64MTE::GetLogicalTag(lldb::addr_t addr) const {
+  return (addr >> MTE_START_BIT) & MTE_TAG_MAX;
+}
+
+lldb::addr_t
+MemoryTagManagerAArch64MTE::RemoveNonAddressBits(lldb::addr_t addr) const {
+  // Here we're ignoring the whole top byte. If you've got MTE
+  // you must also have TBI (top byte ignore).
+  // The other 4 bits could contain other extension bits or
+  // user metadata.
+  return addr & ~((lldb::addr_t)0xFF << MTE_START_BIT);
+}
+
+ptr
diff _t MemoryTagManagerAArch64MTE::AddressDiff(lldb::addr_t addr1,
+                                                  lldb::addr_t addr2) const {
+  return RemoveNonAddressBits(addr1) - RemoveNonAddressBits(addr2);
+}
+
+lldb::addr_t MemoryTagManagerAArch64MTE::GetGranuleSize() const {
+  return MTE_GRANULE_SIZE;
+}
+
+int32_t MemoryTagManagerAArch64MTE::GetAllocationTagType() const {
+  return eMTE_allocation;
+}
+
+size_t MemoryTagManagerAArch64MTE::GetTagSizeInBytes() const { return 1; }
+
+MemoryTagManagerAArch64MTE::TagRange
+MemoryTagManagerAArch64MTE::ExpandToGranule(TagRange range) const {
+  // Ignore reading a length of 0
+  if (!range.IsValid())
+    return range;
+
+  const size_t granule = GetGranuleSize();
+
+  // Align start down to granule start
+  lldb::addr_t new_start = range.GetRangeBase();
+  lldb::addr_t align_down_amount = new_start % granule;
+  new_start -= align_down_amount;
+
+  // Account for the distance we moved the start above
+  size_t new_len = range.GetByteSize() + align_down_amount;
+  // Then align up to the end of the granule
+  size_t align_up_amount = granule - (new_len % granule);
+  if (align_up_amount != granule)
+    new_len += align_up_amount;
+
+  return TagRange(new_start, new_len);
+}
+
+llvm::Expected<std::vector<lldb::addr_t>>
+MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
+                                           size_t granules) const {
+  size_t num_tags = tags.size() / GetTagSizeInBytes();
+  if (num_tags != granules) {
+    return llvm::createStringError(
+        llvm::inconvertibleErrorCode(),
+        "Packed tag data size does not match expected number of tags. "
+        "Expected %" PRIu64 " tag(s) for %" PRIu64 " granules, got %" PRIu64
+        " tag(s).",
+        granules, granules, num_tags);
+  }
+
+  // (if bytes per tag was not 1, we would reconstruct them here)
+
+  std::vector<lldb::addr_t> unpacked;
+  unpacked.reserve(tags.size());
+  for (auto it = tags.begin(); it != tags.end(); ++it) {
+    // Check all tags are in range
+    if (*it > MTE_TAG_MAX) {
+      return llvm::createStringError(
+          llvm::inconvertibleErrorCode(),
+          "Found tag 0x%x which is > max MTE tag value of 0x%x.", *it,
+          MTE_TAG_MAX);
+    }
+    unpacked.push_back(*it);
+  }
+
+  return unpacked;
+}

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
new file mode 100644
index 0000000000000..ef1d0791bcd09
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
@@ -0,0 +1,42 @@
+//===-- MemoryTagManagerAArch64MTE.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_PLUGINS_PROCESS_UTILITY_MEMORYTAGMANAGERAARCH64MTE_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_MEMORYTAGMANAGERAARCH64MTE_H
+
+#include "lldb/Target/MemoryTagManager.h"
+
+namespace lldb_private {
+
+class MemoryTagManagerAArch64MTE : public MemoryTagManager {
+public:
+  // This enum is supposed to be shared for all of AArch64 but until
+  // there are more tag types than MTE, it will live here.
+  enum MTETagTypes {
+    eMTE_logical = 0,
+    eMTE_allocation = 1,
+  };
+
+  lldb::addr_t GetGranuleSize() const override;
+  int32_t GetAllocationTagType() const override;
+  size_t GetTagSizeInBytes() const override;
+
+  lldb::addr_t GetLogicalTag(lldb::addr_t addr) const override;
+  lldb::addr_t RemoveNonAddressBits(lldb::addr_t addr) const override;
+  ptr
diff _t AddressDiff(lldb::addr_t addr1, lldb::addr_t addr2) const override;
+
+  TagRange ExpandToGranule(TagRange range) const override;
+
+  llvm::Expected<std::vector<lldb::addr_t>>
+  UnpackTagsData(const std::vector<uint8_t> &tags,
+                 size_t granules) const override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_MEMORYTAGMANAGERAARCH64MTE_H

diff  --git a/lldb/unittests/Process/Utility/CMakeLists.txt b/lldb/unittests/Process/Utility/CMakeLists.txt
index 772e781b5cc55..95b65cef6a42c 100644
--- a/lldb/unittests/Process/Utility/CMakeLists.txt
+++ b/lldb/unittests/Process/Utility/CMakeLists.txt
@@ -17,7 +17,9 @@ set(LLVM_OPTIONAL_SOURCES
 add_lldb_unittest(ProcessUtilityTests
   RegisterContextTest.cpp
   LinuxProcMapsTest.cpp
+  MemoryTagManagerAArch64MTETest.cpp
   ${PLATFORM_SOURCES}
 
   LINK_LIBS
-    lldbPluginProcessUtility)
+    lldbPluginProcessUtility
+    LLVMTestingSupport)

diff  --git a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
new file mode 100644
index 0000000000000..efd6514dc6c05
--- /dev/null
+++ b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
@@ -0,0 +1,120 @@
+//===-- MemoryTagManagerAArch64MTETest.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 "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+
+TEST(MemoryTagManagerAArch64MTETest, UnpackTagsData) {
+  MemoryTagManagerAArch64MTE manager;
+
+  // Error for insufficient tag data
+  std::vector<uint8_t> input;
+  ASSERT_THAT_EXPECTED(
+      manager.UnpackTagsData(input, 2),
+      llvm::FailedWithMessage(
+          "Packed tag data size does not match expected number of tags. "
+          "Expected 2 tag(s) for 2 granules, got 0 tag(s)."));
+
+  // This is out of the valid tag range
+  input.push_back(0x1f);
+  ASSERT_THAT_EXPECTED(
+      manager.UnpackTagsData(input, 1),
+      llvm::FailedWithMessage(
+          "Found tag 0x1f which is > max MTE tag value of 0xf."));
+
+  // MTE tags are 1 per byte
+  input.pop_back();
+  input.push_back(0xe);
+  input.push_back(0xf);
+
+  std::vector<lldb::addr_t> expected{0xe, 0xf};
+
+  llvm::Expected<std::vector<lldb::addr_t>> got =
+      manager.UnpackTagsData(input, 2);
+  ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+  ASSERT_THAT(expected, testing::ContainerEq(*got));
+}
+
+TEST(MemoryTagManagerAArch64MTETest, GetLogicalTag) {
+  MemoryTagManagerAArch64MTE manager;
+
+  // Set surrounding bits to check shift is correct
+  ASSERT_EQ((lldb::addr_t)0, manager.GetLogicalTag(0xe0e00000ffffffff));
+  // Max tag value
+  ASSERT_EQ((lldb::addr_t)0xf, manager.GetLogicalTag(0x0f000000ffffffff));
+  ASSERT_EQ((lldb::addr_t)2, manager.GetLogicalTag(0x02000000ffffffff));
+}
+
+TEST(MemoryTagManagerAArch64MTETest, ExpandToGranule) {
+  MemoryTagManagerAArch64MTE manager;
+  // Reading nothing, no alignment needed
+  ASSERT_EQ(
+      MemoryTagManagerAArch64MTE::TagRange(0, 0),
+      manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0, 0)));
+
+  // Ranges with 0 size are unchanged even if address is non 0
+  // (normally 0x1234 would be aligned to 0x1230)
+  ASSERT_EQ(
+      MemoryTagManagerAArch64MTE::TagRange(0x1234, 0),
+      manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0x1234, 0)));
+
+  // Ranges already aligned don't change
+  ASSERT_EQ(
+      MemoryTagManagerAArch64MTE::TagRange(0x100, 64),
+      manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0x100, 64)));
+
+  // Any read of less than 1 granule is rounded up to reading 1 granule
+  ASSERT_EQ(
+      MemoryTagManagerAArch64MTE::TagRange(0, 16),
+      manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(0, 1)));
+
+  // Start address is aligned down, and length modified accordingly
+  // Here bytes 8 through 24 straddle 2 granules. So the resulting range starts
+  // at 0 and covers 32 bytes.
+  ASSERT_EQ(
+      MemoryTagManagerAArch64MTE::TagRange(0, 32),
+      manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(8, 16)));
+
+  // Here only the size of the range needs aligning
+  ASSERT_EQ(
+      MemoryTagManagerAArch64MTE::TagRange(16, 32),
+      manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(16, 24)));
+
+  // Start and size need aligning here but we only need 1 granule to cover it
+  ASSERT_EQ(
+      MemoryTagManagerAArch64MTE::TagRange(16, 16),
+      manager.ExpandToGranule(MemoryTagManagerAArch64MTE::TagRange(18, 4)));
+}
+
+TEST(MemoryTagManagerAArch64MTETest, RemoveNonAddressBits) {
+  MemoryTagManagerAArch64MTE manager;
+
+  ASSERT_EQ(0, 0);
+  ASSERT_EQ((lldb::addr_t)0x00ffeedd11223344,
+            manager.RemoveNonAddressBits(0x00ffeedd11223344));
+  ASSERT_EQ((lldb::addr_t)0x0000000000000000,
+            manager.RemoveNonAddressBits(0xFF00000000000000));
+  ASSERT_EQ((lldb::addr_t)0x0055555566666666,
+            manager.RemoveNonAddressBits(0xee55555566666666));
+}
+
+TEST(MemoryTagManagerAArch64MTETest, AddressDiff) {
+  MemoryTagManagerAArch64MTE manager;
+
+  ASSERT_EQ(0, manager.AddressDiff(0, 0));
+  // Result is signed
+  ASSERT_EQ(10, manager.AddressDiff(10, 0));
+  ASSERT_EQ(-10, manager.AddressDiff(0, 10));
+  // Anything in the top byte is ignored
+  ASSERT_EQ(0, manager.AddressDiff(0x2211222233334444, 0x3311222233334444));
+  ASSERT_EQ(-32, manager.AddressDiff(0x5511222233334400, 0x4411222233334420));
+  ASSERT_EQ(65, manager.AddressDiff(0x9911222233334441, 0x6611222233334400));
+}


        


More information about the lldb-commits mailing list