| //===-- 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; |
| } |