[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