UnpackTagsData(const std::vector<uint8_t> &tags,
size_t granules = 0) const = 0;
+ // Unpack tags from a corefile segment containing compressed tags
+ // (compression that may be different from the one used for GDB transport).
+ //
+ // This method asumes that:
+ // * addr and len have been granule aligned by a tag manager
+ // * addr >= tag_segment_virtual_address
+ //
+ // 'reader' will always be a wrapper around a CoreFile in real use
+ // but allows testing without having to mock a CoreFile.
+ typedef std::function<size_t(lldb::offset_t, size_t, void *)> CoreReaderFn;
+ std::vector<lldb::addr_t> virtual UnpackTagsFromCoreFileSegment(
+ CoreReaderFn reader, lldb::addr_t tag_segment_virtual_address,
+ lldb::addr_t tag_segment_data_address, lldb::addr_t addr,
+ size_t len) const = 0;
+
// Pack uncompressed tags into their storage format (e.g. for gdb QMemTags).
// Checks that each tag is within the expected value range.
// We do not check the number of tags or range they apply to because
return unpacked;
}
+std::vector<lldb::addr_t>
+MemoryTagManagerAArch64MTE::UnpackTagsFromCoreFileSegment(
+ CoreReaderFn reader, lldb::addr_t tag_segment_virtual_address,
+ lldb::addr_t tag_segment_data_address, lldb::addr_t addr,
+ size_t len) const {
+ // We can assume by now that addr and len have been granule aligned by a tag
+ // manager. However because we have 2 tags per byte we need to round the range
+ // up again to align to 2 granule boundaries.
+ const size_t granule = GetGranuleSize();
+ const size_t two_granules = granule * 2;
+ lldb::addr_t aligned_addr = addr;
+ size_t aligned_len = len;
+
+ // First align the start address down.
+ if (aligned_addr % two_granules) {
+ assert(aligned_addr % two_granules == granule);
+ aligned_addr -= granule;
+ aligned_len += granule;
+ }
+
+ // Then align the length up.
+ bool aligned_length_up = false;
+ if (aligned_len % two_granules) {
+ assert(aligned_len % two_granules == granule);
+ aligned_len += granule;
+ aligned_length_up = true;
+ }
+
+ // ProcessElfCore should have validated this when it found the segment.
+ assert(aligned_addr >= tag_segment_virtual_address);
+
+ // By now we know that aligned_addr is aligned to a 2 granule boundary.
+ const size_t offset_granules =
+ (aligned_addr - tag_segment_virtual_address) / granule;
+ // 2 tags per byte.
+ const size_t file_offset_in_bytes = offset_granules / 2;
+
+ // By now we know that aligned_len is at least 2 granules.
+ const size_t tag_bytes_to_read = aligned_len / granule / 2;
+ std::vector<uint8_t> tag_data(tag_bytes_to_read);
+ const size_t bytes_copied =
+ reader(tag_segment_data_address + file_offset_in_bytes, tag_bytes_to_read,
+ tag_data.data());
+ assert(bytes_copied == tag_bytes_to_read);
+
+ std::vector<lldb::addr_t> tags;
+ tags.reserve(2 * tag_data.size());
+ // No need to check the range of the tag value here as each occupies only 4
+ // bits.
+ for (auto tag_byte : tag_data) {
+ tags.push_back(tag_byte & 0xf);
+ tags.push_back(tag_byte >> 4);
+ }
+
+ // If we aligned the address down, don't return the extra first tag.
+ if (addr != aligned_addr)
+ tags.erase(tags.begin());
+ // If we aligned the length up, don't return the extra last tag.
+ if (aligned_length_up)
+ tags.pop_back();
+
+ return tags;
+}
+
llvm::Expected<std::vector<uint8_t>> MemoryTagManagerAArch64MTE::PackTags(
const std::vector<lldb::addr_t> &tags) const {
std::vector<uint8_t> packed;
ASSERT_THAT(expected, testing::ContainerEq(*packed));
}
+TEST(MemoryTagManagerAArch64MTETest, UnpackTagsFromCoreFileSegment) {
+ MemoryTagManagerAArch64MTE manager;
+ // This is our fake segment data where tags are compressed as 2 4 bit tags
+ // per byte.
+ std::vector<uint8_t> tags_data;
+ MemoryTagManager::CoreReaderFn reader =
+ [&tags_data](lldb::offset_t offset, size_t length, void *dst) {
+ std::memcpy(dst, tags_data.data() + offset, length);
+ return length;
+ };
+
+ // Zero length is ok.
+ std::vector<lldb::addr_t> tags =
+ manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 0);
+ ASSERT_EQ(tags.size(), (size_t)0);
+
+ // In the simplest case we read 2 tags which are in the same byte.
+ tags_data.push_back(0x21);
+ // The least significant bits are the first tag in memory.
+ std::vector<lldb::addr_t> expected{1, 2};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 32);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+ // If we read just one then it will have to trim off the second one.
+ expected = std::vector<lldb::addr_t>{1};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 16);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+ // If we read the second tag only then the first one must be trimmed.
+ expected = std::vector<lldb::addr_t>{2};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 16, 16);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+ // This trimming logic applies if you read a larger set of tags.
+ tags_data = std::vector<uint8_t>{0x21, 0x43, 0x65, 0x87};
+
+ // Trailing tag should be trimmed.
+ expected = std::vector<lldb::addr_t>{1, 2, 3};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 0, 48);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+ // Leading tag should be trimmed.
+ expected = std::vector<lldb::addr_t>{2, 3, 4};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 16, 48);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+ // Leading and trailing trimmmed.
+ expected = std::vector<lldb::addr_t>{2, 3, 4, 5};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 0, 16, 64);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+ // The address given is an offset into the whole file so the address requested
+ // from the reader should be beyond that.
+ tags_data = std::vector<uint8_t>{0xFF, 0xFF, 0x21, 0x43, 0x65, 0x87};
+ expected = std::vector<lldb::addr_t>{1, 2};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 0, 2, 0, 32);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+
+ // addr is a virtual address that we expect to be >= the tag segment's
+ // starting virtual address. So again an offset must be made from the
+ // difference.
+ expected = std::vector<lldb::addr_t>{3, 4};
+ tags = manager.UnpackTagsFromCoreFileSegment(reader, 32, 2, 64, 32);
+ ASSERT_THAT(expected, testing::ContainerEq(tags));
+}
+
TEST(MemoryTagManagerAArch64MTETest, GetLogicalTag) {
MemoryTagManagerAArch64MTE manager;