[llvm] 2f9fa9e - [lldb][AArch64] Add support for memory tags in core files

David Spickett via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 26 00:46:40 PDT 2022


Author: David Spickett
Date: 2022-07-26T08:46:36+01:00
New Revision: 2f9fa9ef5387de3d87b0c866c678d93695c1c1f3

URL: https://github.com/llvm/llvm-project/commit/2f9fa9ef5387de3d87b0c866c678d93695c1c1f3
DIFF: https://github.com/llvm/llvm-project/commit/2f9fa9ef5387de3d87b0c866c678d93695c1c1f3.diff

LOG: [lldb][AArch64] Add support for memory tags in core files

This teaches ProcessElfCore to recognise the MTE tag segments.

https://www.kernel.org/doc/html/latest/arm64/memory-tagging-extension.html#core-dump-support

These segments contain all the tags for a matching memory segment
which will have the same size in virtual address terms. In real terms
it's 2 tags per byte so the data in the segment is much smaller.

Since MTE is the only tag type supported I have hardcoded some
things to those values. We could and should support more formats
as they appear but doing so now would leave code untested until that
happens.

A few things to note:
* /proc/pid/smaps is not in the core file, only the details you have
  in "maps". Meaning we mark a region tagged only if it has a tag segment.
* A core file supports memory tagging if it has at least 1 memory
  tag segment, there is no other flag we can check to tell if memory
  tagging was enabled. (unlike a live process that can support memory
  tagging even if there are currently no tagged memory regions)

Tests have been added at the commands level for a core file with
mte and without.

There is a lot of overlap between the "memory tag read" tests here and the unit tests for
MemoryTagManagerAArch64MTE::UnpackTagsFromCoreFileSegment, but I think it's
worth keeping to check ProcessElfCore doesn't cause an assert.

Depends on D129487

Reviewed By: omjavaid

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

Added: 
    lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
    lldb/test/API/linux/aarch64/mte_core_file/core.mte
    lldb/test/API/linux/aarch64/mte_core_file/core.nomte
    lldb/test/API/linux/aarch64/mte_core_file/main.c

Modified: 
    lldb/include/lldb/Target/Process.h
    lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
    lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
    llvm/docs/ReleaseNotes.rst
    llvm/include/llvm/BinaryFormat/ELF.h

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index a55659225ef19..505e211e09b63 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -1715,8 +1715,8 @@ class Process : public std::enable_shared_from_this<Process>,
   ///     an error saying so.
   ///     If it does, either the memory tags or an error describing a
   ///     failure to read or unpack them.
-  llvm::Expected<std::vector<lldb::addr_t>> ReadMemoryTags(lldb::addr_t addr,
-                                                           size_t len);
+  virtual llvm::Expected<std::vector<lldb::addr_t>>
+  ReadMemoryTags(lldb::addr_t addr, size_t len);
 
   /// Write memory tags for a range of memory.
   /// (calls DoWriteMemoryTags to do the target specific work)

diff  --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
index 24c942f1d290a..24d3c4bd0ba24 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -144,6 +144,18 @@ lldb::addr_t ProcessElfCore::AddAddressRangeFromLoadSegment(
   return addr;
 }
 
+lldb::addr_t ProcessElfCore::AddAddressRangeFromMemoryTagSegment(
+    const elf::ELFProgramHeader &header) {
+  // If lldb understood multiple kinds of tag segments we would record the type
+  // of the segment here also. As long as there is only 1 type lldb looks for,
+  // there is no need.
+  FileRange file_range(header.p_offset, header.p_filesz);
+  m_core_tag_ranges.Append(
+      VMRangeToFileOffset::Entry(header.p_vaddr, header.p_memsz, file_range));
+
+  return header.p_vaddr;
+}
+
 // Process Control
 Status ProcessElfCore::DoLoadCore() {
   Status error;
@@ -170,9 +182,12 @@ Status ProcessElfCore::DoLoadCore() {
 
   bool ranges_are_sorted = true;
   lldb::addr_t vm_addr = 0;
+  lldb::addr_t tag_addr = 0;
   /// Walk through segments and Thread and Address Map information.
   /// PT_NOTE - Contains Thread and Register information
   /// PT_LOAD - Contains a contiguous range of Process Address Space
+  /// PT_AARCH64_MEMTAG_MTE - Contains AArch64 MTE memory tags for a range of
+  ///                         Process Address Space.
   for (const elf::ELFProgramHeader &H : segments) {
     DataExtractor data = core->GetSegmentData(H);
 
@@ -187,12 +202,18 @@ Status ProcessElfCore::DoLoadCore() {
       if (vm_addr > last_addr)
         ranges_are_sorted = false;
       vm_addr = last_addr;
+    } else if (H.p_type == llvm::ELF::PT_AARCH64_MEMTAG_MTE) {
+      lldb::addr_t last_addr = AddAddressRangeFromMemoryTagSegment(H);
+      if (tag_addr > last_addr)
+        ranges_are_sorted = false;
+      tag_addr = last_addr;
     }
   }
 
   if (!ranges_are_sorted) {
     m_core_aranges.Sort();
     m_core_range_infos.Sort();
+    m_core_tag_ranges.Sort();
   }
 
   // Even if the architecture is set in the target, we need to override it to
@@ -310,6 +331,15 @@ Status ProcessElfCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr,
                                     ? MemoryRegionInfo::eYes
                                     : MemoryRegionInfo::eNo);
       region_info.SetMapped(MemoryRegionInfo::eYes);
+
+      // A region is memory tagged if there is a memory tag segment that covers
+      // the exact same range.
+      region_info.SetMemoryTagged(MemoryRegionInfo::eNo);
+      const VMRangeToFileOffset::Entry *tag_entry =
+          m_core_tag_ranges.FindEntryStartsAt(permission_entry->GetRangeBase());
+      if (tag_entry &&
+          tag_entry->GetRangeEnd() == permission_entry->GetRangeEnd())
+        region_info.SetMemoryTagged(MemoryRegionInfo::eYes);
     } else if (load_addr < permission_entry->GetRangeBase()) {
       region_info.GetRange().SetRangeBase(load_addr);
       region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase());
@@ -317,6 +347,7 @@ Status ProcessElfCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr,
       region_info.SetWritable(MemoryRegionInfo::eNo);
       region_info.SetExecutable(MemoryRegionInfo::eNo);
       region_info.SetMapped(MemoryRegionInfo::eNo);
+      region_info.SetMemoryTagged(MemoryRegionInfo::eNo);
     }
     return Status();
   }
@@ -327,6 +358,7 @@ Status ProcessElfCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr,
   region_info.SetWritable(MemoryRegionInfo::eNo);
   region_info.SetExecutable(MemoryRegionInfo::eNo);
   region_info.SetMapped(MemoryRegionInfo::eNo);
+  region_info.SetMemoryTagged(MemoryRegionInfo::eNo);
   return Status();
 }
 
@@ -376,6 +408,38 @@ size_t ProcessElfCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
   return bytes_copied;
 }
 
+llvm::Expected<std::vector<lldb::addr_t>>
+ProcessElfCore::ReadMemoryTags(lldb::addr_t addr, size_t len) {
+  ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
+  if (core_objfile == nullptr)
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "No core object file.");
+
+  llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
+      GetMemoryTagManager();
+  if (!tag_manager_or_err)
+    return tag_manager_or_err.takeError();
+
+  // LLDB only supports AArch64 MTE tag segments so we do not need to worry
+  // about the segment type here. If you got here then you must have a tag
+  // manager (meaning you are debugging AArch64) and all the segments in this
+  // list will have had type PT_AARCH64_MEMTAG_MTE.
+  const VMRangeToFileOffset::Entry *tag_entry =
+      m_core_tag_ranges.FindEntryThatContains(addr);
+  // If we don't have a tag segment or the range asked for extends outside the
+  // segment.
+  if (!tag_entry || (addr + len) >= tag_entry->GetRangeEnd())
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "No tag segment that covers this range.");
+
+  const MemoryTagManager *tag_manager = *tag_manager_or_err;
+  return tag_manager->UnpackTagsFromCoreFileSegment(
+      [core_objfile](lldb::offset_t offset, size_t length, void *dst) {
+        return core_objfile->CopyData(offset, length, dst);
+      },
+      tag_entry->GetRangeBase(), tag_entry->data.GetRangeBase(), addr, len);
+}
+
 void ProcessElfCore::Clear() {
   m_thread_list.Clear();
 

diff  --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
index fd36e50278168..03c23378e3c16 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -86,6 +86,11 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
                       lldb_private::Status &error) override;
 
+  // We do not implement DoReadMemoryTags. Instead all the work is done in
+  // ReadMemoryTags which avoids having to unpack and repack tags.
+  llvm::Expected<std::vector<lldb::addr_t>> ReadMemoryTags(lldb::addr_t addr,
+                                                           size_t len) override;
+
   lldb::addr_t GetImageInfoAddress() override;
 
   lldb_private::ArchSpec GetArchitecture();
@@ -105,6 +110,8 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
   DoGetMemoryRegionInfo(lldb::addr_t load_addr,
                         lldb_private::MemoryRegionInfo &region_info) override;
 
+  bool SupportsMemoryTagging() override { return !m_core_tag_ranges.IsEmpty(); }
+
 private:
   struct NT_FILE_Entry {
     lldb::addr_t start;
@@ -139,6 +146,9 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
   // Permissions for all ranges
   VMRangeToPermissions m_core_range_infos;
 
+  // Memory tag ranges found in the core
+  VMRangeToFileOffset m_core_tag_ranges;
+
   // NT_FILE entries found from the NOTE segment
   std::vector<NT_FILE_Entry> m_nt_file_entries;
 
@@ -154,6 +164,10 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
   lldb::addr_t
   AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader &header);
 
+  // Parse a contiguous address range from a memory tag segment
+  lldb::addr_t
+  AddAddressRangeFromMemoryTagSegment(const elf::ELFProgramHeader &header);
+
   llvm::Expected<std::vector<lldb_private::CoreNote>>
   parseSegment(const lldb_private::DataExtractor &segment);
   llvm::Error parseFreeBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes);

diff  --git a/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
new file mode 100644
index 0000000000000..d16916cb1b23a
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
@@ -0,0 +1,170 @@
+"""
+Test that memory tagging features work with Linux core files.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class AArch64LinuxMTEMemoryTagCoreFileTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    MTE_BUF_ADDR = hex(0xffff82c74000)
+    BUF_ADDR = hex(0xffff82c73000)
+
+    @skipIfLLVMTargetMissing("AArch64")
+    def test_mte_tag_core_file_memory_region(self):
+        """ Test that memory regions are marked as tagged when there is a tag
+            segment in the core file. """
+        self.runCmd("target create --core core.mte")
+
+        # There should only be one tagged region.
+        self.runCmd("memory region --all")
+        got = self.res.GetOutput()
+        found_tagged_region = False
+
+        for line in got.splitlines():
+            if "memory tagging: enabled" in line:
+                if found_tagged_region:
+                    self.fail("Expected only one tagged region.")
+                found_tagged_region = True
+
+        self.assertTrue(found_tagged_region, "Did not find a tagged memory region.")
+
+        # mte_buf is tagged, buf is not.
+        tagged = "memory tagging: enabled"
+        self.expect("memory region {}".format(self.MTE_BUF_ADDR),
+                patterns=[tagged])
+        self.expect("memory region {}".format(self.BUF_ADDR),
+                patterns=[tagged], matching=False)
+
+    @skipIfLLVMTargetMissing("AArch64")
+    def test_mte_tag_core_file_tag_write(self):
+        """ Test that "memory tag write" does not work with core files
+            as they are read only. """
+        self.runCmd("target create --core core.mte")
+
+        self.expect("memory tag write {} 1".format(self.MTE_BUF_ADDR), error=True,
+                patterns=["error: elf-core does not support writing memory tags"])
+
+    @skipIfLLVMTargetMissing("AArch64")
+    def test_mte_tag_core_file_tag_read(self):
+        """ Test that "memory tag read" works with core files."""
+        self.runCmd("target create --core core.mte")
+
+        # Tags are packed 2 per byte meaning that in addition to granule alignment
+        # there is also 2 x granule alignment going on.
+
+        # All input validation should work as normal.
+        not_tagged_pattern = ("error: Address range 0x[A-Fa-f0-9]+:0x[A-Fa-f0-9]+ "
+                              "is not in a memory tagged region")
+        self.expect("memory tag read {}".format(self.BUF_ADDR),
+                    error=True, patterns=[not_tagged_pattern])
+        # The first part of this range is not tagged.
+        self.expect("memory tag read {addr}-16 {addr}+16".format(
+                addr=self.MTE_BUF_ADDR), error=True,
+                patterns=[not_tagged_pattern])
+        # The last part of this range is not tagged.
+        self.expect("memory tag read {addr}+4096-16 {addr}+4096+16".format(
+                addr=self.MTE_BUF_ADDR), error=True,
+                patterns=[not_tagged_pattern])
+
+        self.expect("memory tag read {addr}+16 {addr}".format(
+                addr=self.MTE_BUF_ADDR), error=True,
+                patterns=["error: End address \(0x[A-Fa-f0-9]+\) "
+                          "must be greater than the start address "
+                          "\(0x[A-Fa-f0-9]+\)"])
+
+        # The simplest scenario. 2 granules means 1 byte of packed tags
+        # with no realignment required.
+        self.expect("memory tag read {addr} {addr}+32".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+                    "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)$"])
+
+        # Here we want just one tag so must use half of the first byte.
+        # (start is aligned length is not)
+        self.expect("memory tag read {addr} {addr}+16".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0$"])
+        # Get the other half of the first byte.
+        # (end is aligned start is not)
+        self.expect("memory tag read {addr}+16 {addr}+32".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)$"])
+
+        # Same thing but with a starting range > 1 granule.
+        self.expect("memory tag read {addr} {addr}+48".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+                    "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n"
+                    "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)$"])
+        self.expect("memory tag read {addr}+16 {addr}+64".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n"
+                    "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)\n"
+                    "\[0x[A-Fa-f0-9]+30, 0x[A-Fa-f0-9]+40\): 0x3 \(mismatch\)$"])
+        # Here both start and end are unaligned.
+        self.expect("memory tag read {addr}+16 {addr}+80".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n"
+                    "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)\n"
+                    "\[0x[A-Fa-f0-9]+30, 0x[A-Fa-f0-9]+40\): 0x3 \(mismatch\)\n"
+                    "\[0x[A-Fa-f0-9]+40, 0x[A-Fa-f0-9]+50\): 0x4 \(mismatch\)$"])
+
+        # For the intial alignment of start/end to granule boundaries the tag manager
+        # is used, so this reads 1 tag as it would normally.
+        self.expect("memory tag read {addr} {addr}+1".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0$"])
+
+        # This range is aligned to granules as mte_buf to mte_buf+32 so the result
+        # should be 2 granules.
+        self.expect("memory tag read {addr} {addr}+17".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+                    "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)$"])
+
+        # Alignment of this range causes it to become unaligned to 2*granule boundaries.
+        self.expect("memory tag read {addr} {addr}+33".format(
+                addr=self.MTE_BUF_ADDR),
+                patterns=[
+                    "Allocation tags:\n"
+                    "\[0x[A-Fa-f0-9]+00, 0x[A-Fa-f0-9]+10\): 0x0\n"
+                    "\[0x[A-Fa-f0-9]+10, 0x[A-Fa-f0-9]+20\): 0x1 \(mismatch\)\n",
+                    "\[0x[A-Fa-f0-9]+20, 0x[A-Fa-f0-9]+30\): 0x2 \(mismatch\)$"])
+
+    @skipIfLLVMTargetMissing("AArch64")
+    def test_mte_commands_no_mte(self):
+        """ Test that memory tagging commands fail on an AArch64 corefile without
+            any tag segments."""
+
+        self.runCmd("target create --core core.nomte")
+
+        self.expect("memory tag read 0 1",
+                substrs=["error: Process does not support memory tagging"], error=True)
+        # Note that this tells you memory tagging is not supported at all, versus
+        # the MTE core file which does support it but does not allow writing tags.
+        self.expect("memory tag write 0 1",
+                substrs=["error: Process does not support memory tagging"], error=True)

diff  --git a/lldb/test/API/linux/aarch64/mte_core_file/core.mte b/lldb/test/API/linux/aarch64/mte_core_file/core.mte
new file mode 100644
index 0000000000000..84a3266667e77
Binary files /dev/null and b/lldb/test/API/linux/aarch64/mte_core_file/core.mte 
diff er

diff  --git a/lldb/test/API/linux/aarch64/mte_core_file/core.nomte b/lldb/test/API/linux/aarch64/mte_core_file/core.nomte
new file mode 100644
index 0000000000000..201f2880e6dc2
Binary files /dev/null and b/lldb/test/API/linux/aarch64/mte_core_file/core.nomte 
diff er

diff  --git a/lldb/test/API/linux/aarch64/mte_core_file/main.c b/lldb/test/API/linux/aarch64/mte_core_file/main.c
new file mode 100644
index 0000000000000..89027e0ea75d2
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/mte_core_file/main.c
@@ -0,0 +1,78 @@
+// Program to generate core files to test MTE tag features.
+//
+// This file uses ACLE intrinsics as detailed in:
+// https://developer.arm.com/documentation/101028/0012/10--Memory-tagging-intrinsics?lang=en
+//
+// Compile with:
+// <gcc or clang> -march=armv8.5-a+memtag -g main.c -o a.out.mte
+// <gcc or clang> -march=armv8.5-a+memtag -g main.c -DNO_MTE -o a.out.nomte
+//
+// /proc/self/coredump_filter was set to 2 when the core files were made.
+
+#include <arm_acle.h>
+#include <asm/mman.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+int main(int argc, char const *argv[]) {
+#ifdef NO_MTE
+  *(char *)(0) = 0;
+#endif
+
+  if (prctl(PR_SET_TAGGED_ADDR_CTRL,
+            PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+                // Allow all tags to be generated by the addg
+                // instruction __arm_mte_increment_tag produces.
+                (0xffff << PR_MTE_TAG_SHIFT),
+            0, 0, 0)) {
+    return 1;
+  }
+
+  size_t page_size = sysconf(_SC_PAGESIZE);
+  char *mte_buf = mmap(0, page_size, PROT_READ | PROT_WRITE | PROT_MTE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (!mte_buf)
+    return 1;
+
+  printf("mte_buf: %p\n", mte_buf);
+
+  // Allocate some untagged memory before the tagged memory.
+  char *buf = mmap(0, page_size, PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (!buf)
+    return 1;
+
+  printf("buf: %p\n", buf);
+
+  // This write means that the memory for buf is included in the corefile.
+  // So we can read from the end of it into mte_buf during the test.
+  *buf = 1;
+
+  // These must be next to each other for the tests to work.
+  // <high address>
+  // mte_buf
+  // buf
+  // <low address>
+  if ((mte_buf - buf) != page_size) {
+    return 1;
+  }
+
+  // Set incrementing tags until end of the page.
+  char *tagged_ptr = mte_buf;
+  // This ignores tag bits when subtracting the addresses.
+  while (__arm_mte_ptr
diff (tagged_ptr, mte_buf) < page_size) {
+    // Set the allocation tag for this location.
+    __arm_mte_set_tag(tagged_ptr);
+    // + 16 for 16 byte granules.
+    // Earlier we allowed all tag values, so this will give us an
+    // incrementing pattern 0-0xF wrapping back to 0.
+    tagged_ptr = __arm_mte_increment_tag(tagged_ptr + 16, 1);
+  }
+
+  // Will fault because logical tag 0 != allocation tag 1.
+  *(mte_buf + 16) = 1;
+
+  return 0;
+}

diff  --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst
index 193898a4c31c8..3b374e2c613dd 100644
--- a/llvm/docs/ReleaseNotes.rst
+++ b/llvm/docs/ReleaseNotes.rst
@@ -306,6 +306,8 @@ Changes to LLDB
     locations. This prevents us reading locations multiple times, or not
     writing out new values if the addresses have 
diff erent non-address bits.
 
+* LLDB now supports reading memory tags from AArch64 Linux core files.
+
 Changes to Sanitizers
 ---------------------
 

diff  --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index fb74834bb57d9..a0bb50db8c544 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1386,6 +1386,8 @@ enum {
   // These all contain stack unwind tables.
   PT_ARM_EXIDX = 0x70000001,
   PT_ARM_UNWIND = 0x70000001,
+  // MTE memory tag segment type
+  PT_AARCH64_MEMTAG_MTE = 0x70000002,
 
   // MIPS program header types.
   PT_MIPS_REGINFO = 0x70000000,  // Register usage information.


        


More information about the llvm-commits mailing list