diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1715,8 +1715,8 @@ /// an error saying so. /// If it does, either the memory tags or an error describing a /// failure to read or unpack them. - llvm::Expected> ReadMemoryTags(lldb::addr_t addr, - size_t len); + virtual llvm::Expected> + 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.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -86,6 +86,11 @@ 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> ReadMemoryTags(lldb::addr_t addr, + size_t len) override; + lldb::addr_t GetImageInfoAddress() override; lldb_private::ArchSpec GetArchitecture(); @@ -105,6 +110,8 @@ DoGetMemoryRegionInfo(lldb::addr_t load_addr, lldb_private::MemoryRegionInfo ®ion_info) override; + bool SupportsMemoryTagging() override { return !m_core_tag_ranges.IsEmpty(); } + private: struct NT_FILE_Entry { lldb::addr_t start; @@ -139,6 +146,9 @@ // 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 m_nt_file_entries; @@ -154,6 +164,10 @@ 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> parseSegment(const lldb_private::DataExtractor &segment); llvm::Error parseFreeBSDNotes(llvm::ArrayRef notes); diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -144,6 +144,18 @@ 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 @@ 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 @@ 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 @@ ? 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 @@ 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 @@ 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 @@ return bytes_copied; } +llvm::Expected> +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 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/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py new file mode 100644 --- /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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ -march=armv8.5-a+memtag -g main.c -o a.out.mte +// -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 +#include +#include +#include +#include +#include + +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. + // + // mte_buf + // buf + // + 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_ptrdiff(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 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -306,6 +306,8 @@ locations. This prevents us reading locations multiple times, or not writing out new values if the addresses have different 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 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1386,6 +1386,8 @@ // 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.