[Lldb-commits] [lldb] 883b0d5 - [lldb][AArch64] Add UnpackTagsFromCoreFileSegment to MemoryTagManager

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Mon Jul 25 07:51:42 PDT 2022


Author: David Spickett
Date: 2022-07-25T15:51:36+01:00
New Revision: 883b0d5b7f873a7d6f8c8ee13c7f6174a2a79a50

URL: https://github.com/llvm/llvm-project/commit/883b0d5b7f873a7d6f8c8ee13c7f6174a2a79a50
DIFF: https://github.com/llvm/llvm-project/commit/883b0d5b7f873a7d6f8c8ee13c7f6174a2a79a50.diff

LOG: [lldb][AArch64] Add UnpackTagsFromCoreFileSegment to MemoryTagManager

This is the first part of support for reading MTE tags from Linux
core files. The format is documented here:
https://www.kernel.org/doc/html/latest/arm64/memory-tagging-extension.html#core-dump-support

This patch adds a method to unpack from the format the core
file uses, which is different to the one chosen for GDB packets.

MemoryTagManagerAArch64MTE is not tied one OS so another OS
might choose a different format in future. However, infrastructure
to handle that would go untested until then so I've chosen not to
attempt to handle that.

Reviewed By: omjavaid

Differential Revision: https://reviews.llvm.org/D129487

Added: 
    

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/MemoryTagManager.h b/lldb/include/lldb/Target/MemoryTagManager.h
index 28a8acc34632c..b082224c38edb 100644
--- a/lldb/include/lldb/Target/MemoryTagManager.h
+++ b/lldb/include/lldb/Target/MemoryTagManager.h
@@ -113,6 +113,21 @@ class MemoryTagManager {
   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 
diff erent 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

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
index b71de4cadb185..e0126d840971e 100644
--- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp
@@ -247,6 +247,70 @@ MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags,
   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;

diff  --git a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
index 7cda728b140f0..365e176e5b1da 100644
--- a/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
+++ b/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h
@@ -44,6 +44,12 @@ class MemoryTagManagerAArch64MTE : public MemoryTagManager {
   UnpackTagsData(const std::vector<uint8_t> &tags,
                  size_t granules = 0) const override;
 
+  std::vector<lldb::addr_t>
+  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 override;
+
   llvm::Expected<std::vector<uint8_t>>
   PackTags(const std::vector<lldb::addr_t> &tags) const override;
 

diff  --git a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
index 9c209ec2363ad..6d8b699bbadae 100644
--- a/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
+++ b/lldb/unittests/Process/Utility/MemoryTagManagerAArch64MTETest.cpp
@@ -80,6 +80,72 @@ TEST(MemoryTagManagerAArch64MTETest, PackTags) {
   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
+  // 
diff erence.
+  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;
 


        


More information about the lldb-commits mailing list