diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp new file mode 100644 index 000000000000..d74b66b58afc --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp @@ -0,0 +1,200 @@ +//===-- 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); +} + +ptrdiff_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<MemoryTagManager::TagRange> +MemoryTagManagerAArch64MTE::MakeTaggedRange( + lldb::addr_t addr, lldb::addr_t end_addr, + const 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 llvm::createStringError( + llvm::inconvertibleErrorCode(), + "End address (0x%" PRIx64 + ") must be greater than the start address (0x%" PRIx64 ")", + end_addr, addr); + } + + // Region addresses will not have memory tags. So when searching + // we must use an untagged address. + MemoryRegionInfo::RangeType tag_range(RemoveNonAddressBits(addr), len); + tag_range = ExpandToGranule(tag_range); + + // Make a copy so we can use the original for errors and the final return. + MemoryRegionInfo::RangeType remaining_range(tag_range); + + // While there are parts of the range that don't have a matching tagged memory + // region + while (remaining_range.IsValid()) { + // Search for a region that contains the start of the range + MemoryRegionInfos::const_iterator region = std::find_if( + memory_regions.cbegin(), memory_regions.cend(), + [&remaining_range](const MemoryRegionInfo ®ion) { + return region.GetRange().Contains(remaining_range.GetRangeBase()); + }); + + if (region == memory_regions.cend() || + region->GetMemoryTagged() != MemoryRegionInfo::eYes) { + // Some part of this range is untagged (or unmapped) so error + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Address range 0x%" PRIx64 ":0x%" PRIx64 + " is not in a memory tagged region", + tag_range.GetRangeBase(), + tag_range.GetRangeEnd()); + } + + // We've found some part of the range so remove that part and continue + // searching for the rest. Moving the base "slides" the range so we need to + // save/restore the original end. If old_end is less than the new base, the + // range will be set to have 0 size and we'll exit the while. + lldb::addr_t old_end = remaining_range.GetRangeEnd(); + remaining_range.SetRangeBase(region->GetRange().GetRangeEnd()); + remaining_range.SetRangeEnd(old_end); + } + + // Every part of the range is contained within a tagged memory region. + return tag_range; +} + +llvm::Expected<std::vector<lldb::addr_t>> +MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags, + size_t granules /*=0*/) const { + // 0 means don't check the number of tags before unpacking + if (granules) { + 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 %zu tag(s) for %zu granule(s), got %zu 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; +} + +llvm::Expected<std::vector<uint8_t>> MemoryTagManagerAArch64MTE::PackTags( + const std::vector<lldb::addr_t> &tags) const { + std::vector<uint8_t> packed; + packed.reserve(tags.size() * GetTagSizeInBytes()); + + for (auto tag : tags) { + if (tag > MTE_TAG_MAX) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Found tag 0x%" PRIx64 + " which is > max MTE tag value of 0x%x.", + tag, MTE_TAG_MAX); + } + packed.push_back(static_cast<uint8_t>(tag)); + } + + return packed; +} + +llvm::Expected<std::vector<lldb::addr_t>> +MemoryTagManagerAArch64MTE::RepeatTagsForRange( + const std::vector<lldb::addr_t> &tags, TagRange range) const { + std::vector<lldb::addr_t> new_tags; + + // If the range is not empty + if (range.IsValid()) { + if (tags.empty()) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Expected some tags to cover given range, got zero."); + } + + // We assume that this range has already been expanded/aligned to granules + size_t granules = range.GetByteSize() / GetGranuleSize(); + new_tags.reserve(granules); + for (size_t to_copy = 0; granules > 0; granules -= to_copy) { + to_copy = granules > tags.size() ? tags.size() : granules; + new_tags.insert(new_tags.end(), tags.begin(), tags.begin() + to_copy); + } + } + + return new_tags; +} |