DavidSpickett created this revision.
Herald added subscribers: mgrang, kristof.beyls.
DavidSpickett requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.
This is to be used when you want to know what subranges
of a larger range have memory tagging.
Like MakeTaggedRange but memory without tags is skipped
and you get a list of ranges back.
Will be used later by DumpDataExtractor to show memory tags.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D112824
Files:
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
Index: lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
===================================================================
--- lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
+++ lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
@@ -247,6 +247,101 @@
ASSERT_EQ(*got, expected_range);
}
+TEST(MemoryTagManagerAArch64MTETest, MakeTaggedRanges) {
+ MemoryTagManagerAArch64MTE manager;
+ MemoryRegionInfos memory_regions;
+
+ // Note that MakeTaggedRanges takes start/end address.
+ // Whereas TagRanges and regions take start address and size.
+
+ // Range must not be inverted
+ // (this is the only error situation)
+ ASSERT_THAT_EXPECTED(
+ manager.MakeTaggedRange(1, 0, memory_regions),
+ llvm::FailedWithMessage(
+ "End address (0x0) must be greater than the start address (0x1)"));
+
+ // No regions means no tagged regions, no ranges returned.
+ llvm::Expected<std::vector<MemoryTagManager::TagRange>> got =
+ manager.MakeTaggedRanges(0, 0x10, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
+
+ // Cover whole range, untagged. No ranges returned.
+ memory_regions.push_back(MakeRegionInfo(0, 0x20, false));
+ got = manager.MakeTaggedRanges(0, 0x20, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{});
+
+ // Make the region tagged and it'll be the one range returned.
+ memory_regions.back().SetMemoryTagged(MemoryRegionInfo::eYes);
+ got = manager.MakeTaggedRanges(0, 0x20, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+ MemoryTagManager::TagRange(0, 0x20)});
+
+ // This region will be trimmed if it's larger than the whole range.
+ memory_regions.clear();
+ memory_regions.push_back(MakeRegionInfo(0, 0x40, true));
+ got = manager.MakeTaggedRanges(0x10, 0x30, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+ MemoryTagManager::TagRange(0x10, 0x20)});
+
+ memory_regions.clear();
+
+ // Only start of range is tagged, only that is returned.
+ // Start the region just before the requested range to check
+ // we limit the result to the requested range.
+ memory_regions.push_back(MakeRegionInfo(0, 0x20, true));
+ got = manager.MakeTaggedRanges(0x10, 0x100, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, std::vector<MemoryTagManager::TagRange>{
+ MemoryTagManager::TagRange(0x10, 0x10)});
+
+ // Add a tagged region at the end, now we get both
+ // and the middle is untagged.
+ // The range added here is deliberately over the end of the
+ // requested range to show that we trim the end.
+ memory_regions.push_back(MakeRegionInfo(0xE0, 0x40, true));
+ got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+
+ std::vector<MemoryTagManager::TagRange> expected{
+ MemoryTagManager::TagRange(0x10, 0x10),
+ MemoryTagManager::TagRange(0xE0, 0x30)};
+ ASSERT_EQ(*got, expected);
+
+ // Now add a middle tagged region.
+ memory_regions.push_back(MakeRegionInfo(0x90, 0x20, true));
+ // MakeTaggedRanges will sort the regions it is given, so the output
+ // is always in ascending address order. So this goes in the middle
+ // of expected.
+ expected.insert(std::next(expected.begin()),
+ MemoryTagManager::TagRange(0x90, 0x20));
+ got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, expected);
+
+ // Then if we add untagged regions in between the tagged,
+ // the output should stay the same.
+ memory_regions.push_back(MakeRegionInfo(0x20, 0x30, false));
+ memory_regions.push_back(MakeRegionInfo(0xC0, 0x10, false));
+ got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, expected);
+
+ // Finally check that we handle only having the end of the range.
+ memory_regions.clear();
+ expected.clear();
+
+ memory_regions.push_back(MakeRegionInfo(0x100, 0x10, true));
+ expected.push_back(MemoryTagManager::TagRange(0x100, 0x10));
+ got = manager.MakeTaggedRanges(0x10, 0x110, memory_regions);
+ ASSERT_THAT_EXPECTED(got, llvm::Succeeded());
+ ASSERT_EQ(*got, expected);
+}
+
TEST(MemoryTagManagerAArch64MTETest, RemoveNonAddressBits) {
MemoryTagManagerAArch64MTE manager;
Index: lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
===================================================================
--- lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
+++ lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
@@ -36,6 +36,10 @@
lldb::addr_t addr, lldb::addr_t end_addr,
const lldb_private::MemoryRegionInfos &memory_regions) const override;
+ llvm::Expected<std::vector<TagRange>> MakeTaggedRanges(
+ lldb::addr_t addr, lldb::addr_t end_addr,
+ lldb_private::MemoryRegionInfos memory_regions) const override;
+
llvm::Expected<std::vector<lldb::addr_t>>
UnpackTagsData(const std::vector<uint8_t> &tags,
size_t granules = 0) const override;
Index: lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
===================================================================
--- lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
+++ lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "MemoryTagManagerAArch64MTE.h"
+#include "llvm/Support/Error.h"
using namespace lldb_private;
@@ -66,6 +67,15 @@
return TagRange(new_start, new_len);
}
+static llvm::Error make_invalid_range_err(lldb::addr_t addr,
+ lldb::addr_t end_addr) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "End address (0x%" PRIx64
+ ") must be greater than the start address (0x%" PRIx64 ")",
+ end_addr, addr);
+}
+
llvm::Expected<MemoryTagManager::TagRange>
MemoryTagManagerAArch64MTE::MakeTaggedRange(
lldb::addr_t addr, lldb::addr_t end_addr,
@@ -74,13 +84,8 @@
// We must remove tags here otherwise an address with a higher
// tag value will always be > the other.
ptrdiff_t len = AddressDiff(end_addr, addr);
- if (len <= 0) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- "End address (0x%" PRIx64
- ") must be greater than the start address (0x%" PRIx64 ")",
- end_addr, addr);
- }
+ if (len <= 0)
+ return make_invalid_range_err(addr, end_addr);
// Region addresses will not have memory tags. So when searching
// we must use an untagged address.
@@ -123,6 +128,73 @@
return tag_range;
}
+llvm::Expected<std::vector<MemoryTagManager::TagRange>>
+MemoryTagManagerAArch64MTE::MakeTaggedRanges(
+ lldb::addr_t addr, lldb::addr_t end_addr,
+ lldb_private::MemoryRegionInfos memory_regions) const {
+ // First check that the range is not inverted.
+ // We must remove tags here otherwise an address with a higher
+ // tag value will always be > the other.
+ ptrdiff_t len = AddressDiff(end_addr, addr);
+ if (len <= 0)
+ return make_invalid_range_err(addr, end_addr);
+
+ std::vector<MemoryTagManager::TagRange> tagged_ranges;
+ // No memory regions means no tagged memory at all
+ if (memory_regions.empty())
+ return tagged_ranges;
+
+ // For the logic to work regions must be in ascending order.
+ // We're going to assume that there are no overlapping regions
+ // and that each region is granule aligned already.
+ // Regions are most likely multiples of page size and granules
+ // are some smaller division of that.
+ std::sort(memory_regions.begin(), memory_regions.end(),
+ [](const MemoryRegionInfo &lhs, const MemoryRegionInfo &rhs) {
+ return lhs.GetRange().GetRangeBase() <
+ rhs.GetRange().GetRangeBase();
+ });
+
+ // Region addresses will not have memory tags so when searching
+ // we must use an untagged address.
+ MemoryRegionInfo::RangeType range(RemoveNonAddressBits(addr), len);
+ range = ExpandToGranule(range);
+
+ MemoryRegionInfos::const_iterator region = memory_regions.begin();
+ MemoryRegionInfos::const_iterator end_region = memory_regions.end();
+
+ // While there are regions to check and the range has non zero length
+ for (; region != end_region && range.IsValid(); ++region) {
+ // If the region doesn't overlap the range at all, ignore it.
+ if (!region->GetRange().DoesIntersect(range))
+ continue;
+
+ // If it's tagged record this sub-range.
+ // (assuming that it's already granule aligned)
+ if (region->GetMemoryTagged()) {
+ // The region found may extend outside the requested range.
+ // For example the first region might start before the range.
+ // We must only add what covers the requested range.
+ lldb::addr_t start =
+ std::max(range.GetRangeBase(), region->GetRange().GetRangeBase());
+ lldb::addr_t end =
+ std::min(range.GetRangeEnd(), region->GetRange().GetRangeEnd());
+ tagged_ranges.push_back(MemoryTagManager::TagRange(start, end - start));
+ }
+
+ // Move the range up to start at the end of the region.
+ lldb::addr_t old_end = range.GetRangeEnd();
+ // This "slides" the range so it moves the end as well.
+ range.SetRangeBase(region->GetRange().GetRangeEnd());
+ // So we set the end back to the original end address after sliding it up.
+ range.SetRangeEnd(old_end);
+ // (if the above were to try to set end < begin the range will just be set
+ // to 0 size)
+ }
+
+ return tagged_ranges;
+}
+
llvm::Expected<std::vector<lldb::addr_t>>
MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
size_t granules /*=0*/) const {
Index: lldb/include/lldb/Target/MemoryTagManager.h
===================================================================
--- lldb/include/lldb/Target/MemoryTagManager.h
+++ lldb/include/lldb/Target/MemoryTagManager.h
@@ -72,6 +72,20 @@
lldb::addr_t addr, lldb::addr_t end_addr,
const lldb_private::MemoryRegionInfos &memory_regions) const = 0;
+ // Given a range addr to end_addr, check that end_addr >= addr.
+ // If it is not, return an error saying so.
+ // Otherwise, granule align it and return a set of ranges representing
+ // subsections of the aligned range that have memory tagging enabled.
+ //
+ // Basically a sparse version of MakeTaggedRange. Use this when you
+ // want to know which parts of a larger range have memory tagging.
+ //
+ // Tags in the input addresses are ignored and not present
+ // in the returned range.
+ virtual llvm::Expected<std::vector<TagRange>>
+ MakeTaggedRanges(lldb::addr_t addr, lldb::addr_t end_addr,
+ lldb_private::MemoryRegionInfos memory_regions) 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.
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits