diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -707,6 +707,10 @@ case ELF::EM_MSP430: ECase(SHT_MSP430_ATTRIBUTES); break; + case ELF::EM_AARCH64: + ECase(SHT_AARCH64_MEMTAG_GLOBALS_STATIC); + ECase(SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC); + break; default: // Nothing to do. break; diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test b/llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test --- a/llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test +++ b/llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test @@ -80,10 +80,10 @@ # INVALID-SAME: Unknown (2) # LLVM: 0x000000007000000D AARCH64_MEMTAG_GLOBALS 0xdeadbeef -# LLVM: 0x000000007000000F AARCH64_MEMTAG_GLOBALSSZ 1234 +# LLVM: 0x000000007000000F AARCH64_MEMTAG_GLOBALSSZ 15 # GNU: 0x000000007000000d (AARCH64_MEMTAG_GLOBALS) 0xdeadbeef0 -# GNU: 0x000000007000000f (AARCH64_MEMTAG_GLOBALSSZ) 1234 +# GNU: 0x000000007000000f (AARCH64_MEMTAG_GLOBALSSZ) 15 # GNU: Displaying notes found in: .note.android.memtag # GNU-NEXT: Owner Data size Description @@ -132,8 +132,8 @@ # NOSTACK: AARCH64_MEMTAG_STACK: Disabled (0) # LLVM: AARCH64_MEMTAG_GLOBALS: 0xdeadbeef0 # GNU: AARCH64_MEMTAG_GLOBALS: 0xdeadbeef0 -# LLVM: AARCH64_MEMTAG_GLOBALSSZ: 1234 -# GNU: AARCH64_MEMTAG_GLOBALSSZ: 1234 +# LLVM: AARCH64_MEMTAG_GLOBALSSZ: 15 +# GNU: AARCH64_MEMTAG_GLOBALSSZ: 15 # LLVM-OK: Memtag Android Note # GNU-OK: Memtag Android Note @@ -146,6 +146,50 @@ # STACK: Stack: Enabled # NOSTACK: Stack: Disabled +## https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#83encoding-of-sht_aarch64_memtag_globals_dynamic +## 16 bytes at 0xdead0000 (0xdead0000 bytes, skip == 0xdead000 granules) +## -> /* skip */ (0xdead000 << 3) + /* size in granules */ 1 +## -> 0x6f568001 +## => 0x81 0x80 0xda 0xfa 0x06 +## 32 bytes at 0xdead0010 (adjacent) +## -> /* skip */ (0 << 3) + /* size in granules */ 2 +## => 0x02 +## 64 bytes at 0xdead0100 (0xdead0100 - 0xdead0010 - 32 == 0xd0 bytes, skip == 0xd granules) +## -> /* skip */ (0xd << 3) + /* size in granules */ 4 +## -> 0x6c +## => 0x6c +## 0x1000 bytes at 0xdeadf000 (0xdeadf000 - 0xdead0100 - 64 == 0xeec0 bytes, skip == 0xeec granules) +## -> /* skip */ (0xeec << 3) + /* size won't fit in reserved bits */ 0 +## => 0xe0 0xee 0x01 +## -> /* size in granules (minus 1) */ 0x100 - 1 +## => 0xff 0x01 +## 16 bytes at 0xdeae0000 (adjacent) +## -> /* skip */ (0 << 3) + /* size in granules */ 1 +## => 0x01 +## 16 bytes at 0xdeae0010 (adjacent) +## => 0x01 +## 16 bytes at 0xdeaef020 (adjacent) +## => 0x01 + +# LLVM-OK: Memtag Global Descriptors: [ +# LLVM-OK-NEXT: 0xDEAD0000: 0x10 +# LLVM-OK-NEXT: 0xDEAD0010: 0x20 +# LLVM-OK-NEXT: 0xDEAD0100: 0x40 +# LLVM-OK-NEXT: 0xDEADF000: 0x1000 +# LLVM-OK-NEXT: 0xDEAE0000: 0x10 +# LLVM-OK-NEXT: 0xDEAE0010: 0x10 +# LLVM-OK-NEXT: 0xDEAE0020: 0x10 +# LLVM-OK-NEXT: ] +# GNU-OK: Memtag Global Descriptors: +# GNU-OK-NEXT: 0xdead0000: 0x10 +# GNU-OK-NEXT: 0xdead0010: 0x20 +# GNU-OK-NEXT: 0xdead0100: 0x40 +# GNU-OK-NEXT: 0xdeadf000: 0x1000 +# GNU-OK-NEXT: 0xdeae0000: 0x10 +# GNU-OK-NEXT: 0xdeae0010: 0x10 +# GNU-OK-NEXT: 0xdeae0020: 0x10 +# GNU-OK-NOT: {{.}} + ######################################### ## --docnum=1 (default) ######################################### @@ -175,17 +219,25 @@ - Tag: DT_AARCH64_MEMTAG_GLOBALS Value: 0xdeadbeef0 - Tag: DT_AARCH64_MEMTAG_GLOBALSSZ - Value: 1234 + Value: 15 - Tag: DT_INIT_ARRAY Value: 0x1000 + - Name: .memtag.globals.dynamic + Type: SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC + Flags: [ SHF_ALLOC ] + Address: 0xdeadbeef + AddressAlign: 0x4 + Content: 8180DAFA06026CE0EE01ff01010101 + +######################################### +## Ensure the header is printed, even if there's no relevant dynamic entries, +## and that nothing else is printed. +######################################### # RUN: yaml2obj --docnum=2 %s -o %t # RUN: llvm-readelf --memtag %t | FileCheck %s --check-prefixes=MISSING-GNU # RUN: llvm-readobj --memtag %t | FileCheck %s --check-prefixes=MISSING-LLVM -## Ensure the header is printed, even if there's no relevant dynamic entries, -## and that nothing else is printed. - # MISSING-GNU-NOT: {{.}} # MISSING-GNU: Memtag Dynamic Entries: # MISSING-GNU-NEXT: < none found > @@ -218,3 +270,46 @@ Entries: - Tag: DT_INIT_ARRAY Value: 0x1000 + +######################################### +## Ensure that we fail if DT_AARCH64_MEMTAG_GLOBALSSZ doesn't match the actual +## section size. +######################################### + +# RUN: yaml2obj --docnum=3 %s -o %t +# RUN: llvm-readelf --memtag %t 2>&1 | FileCheck %s --check-prefixes=SIZE-MISMATCH +# RUN: llvm-readobj --memtag %t 2>&1 | FileCheck %s --check-prefixes=SIZE-MISMATCH + +# SIZE-MISMATCH: Mismatch between DT_AARCH64_MEMTAG_GLOBALSSZ (0x1337) and +# SIZE-MISMATCH: SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section size (0xa) + +######################################### +## --docnum=3 +######################################### + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .dynamic + Type: SHT_DYNAMIC + Entries: + - Tag: DT_AARCH64_MEMTAG_MODE + Value: 0 + - Tag: DT_AARCH64_MEMTAG_HEAP + Value: 0 + - Tag: DT_AARCH64_MEMTAG_STACK + Value: 0 + - Tag: DT_AARCH64_MEMTAG_GLOBALS + Value: 0xdeadbeef0 + - Tag: DT_AARCH64_MEMTAG_GLOBALSSZ + Value: 0x1337 + - Name: .memtag.globals.dynamic + Type: SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC + Flags: [ SHF_ALLOC ] + Address: 0xdeadbeef + AddressAlign: 0x4 + Content: 12345678901234567890 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -299,7 +299,8 @@ virtual void printMemtag( const ArrayRef> DynamicEntries, - const ArrayRef AndroidNoteDesc) = 0; + const ArrayRef AndroidNoteDesc, + const ArrayRef> Descriptors) = 0; Expected> getVersionTable(const Elf_Shdr &Sec, ArrayRef *SymTab, @@ -584,7 +585,8 @@ void printStackSizes() override; void printMemtag( const ArrayRef> DynamicEntries, - const ArrayRef AndroidNoteDesc) override; + const ArrayRef AndroidNoteDesc, + const ArrayRef> Descriptors) override; private: void printHashHistogram(const Elf_Hash &HashTable); @@ -691,7 +693,8 @@ void printStackSizes() override; void printMemtag( const ArrayRef> DynamicEntries, - const ArrayRef AndroidNoteDesc) override; + const ArrayRef AndroidNoteDesc, + const ArrayRef> Descriptors) override; private: void printRelrReloc(const Elf_Relr &R) override; @@ -5227,14 +5230,14 @@ return false; for (const auto &KV : Props) OS << " " << KV.first << ": " << KV.second << '\n'; - OS << '\n'; return true; } template void GNUELFDumper::printMemtag( const ArrayRef> DynamicEntries, - const ArrayRef AndroidNoteDesc) { + const ArrayRef AndroidNoteDesc, + const ArrayRef> Descriptors) { OS << "Memtag Dynamic Entries:\n"; if (DynamicEntries.empty()) OS << " < none found >\n"; @@ -5246,6 +5249,15 @@ OS << "Memtag Android Note:\n"; printAndroidNote(OS, ELF::NT_ANDROID_TYPE_MEMTAG, AndroidNoteDesc); } + + if (Descriptors.empty()) + return; + + OS << "Memtag Global Descriptors:\n"; + for (const auto &Descriptor : Descriptors) { + OS << " 0x" << llvm::utohexstr(Descriptor.first, true) << ": 0x" + << llvm::utohexstr(Descriptor.second, true) << "\n"; + } } template @@ -5891,16 +5903,43 @@ /*ProcessNoteFn=*/ProcessNote, /*FinishNotesFn=*/[]() {}); } +template +static ArrayRef +getMemtagGlobalsSectionContents(const ELFFile &File) { + for (const typename ELFT::Shdr &Sec : cantFail(File.sections())) { + if (Sec.sh_type != SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC) + continue; + Expected> Contents = + cantFail(File.getSectionContents(Sec)); + if (auto E = Contents.takeError()) { + llvm::errs() << "Couldn't get SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section " + "contents\n"; + return ArrayRef(); + } + return Contents.get(); + } + return ArrayRef(); +} + +// Reserve the lower three bits of the first byte of the step distance when +// encoding the memtag descriptors. Found to be the best overall size tradeoff +// when compiling Android T with full MTE globals enabled. +constexpr uint64_t kMemtagStepVarintReservedBits = 3; +constexpr uint64_t kMemtagGranuleSize = 16; + template void ELFDumper::printMemtag() { if (Obj.getHeader().e_machine != EM_AARCH64) return; std::vector> DynamicEntries; + size_t AndroidMemtagGlobalsSz = 0; for (const typename ELFT::Dyn &Entry : dynamic_table()) { uintX_t Tag = Entry.getTag(); switch (Tag) { + case DT_AARCH64_MEMTAG_GLOBALSSZ: + AndroidMemtagGlobalsSz = Entry.getVal(); + [[fallthrough]]; case DT_AARCH64_MEMTAG_MODE: case DT_AARCH64_MEMTAG_HEAP: case DT_AARCH64_MEMTAG_STACK: - case DT_AARCH64_MEMTAG_GLOBALSSZ: case DT_AARCH64_MEMTAG_GLOBALS: DynamicEntries.emplace_back(Obj.getDynamicTagAsString(Tag), getDynamicEntry(Tag, Entry.getVal())); @@ -5922,7 +5961,48 @@ const typename ELFT::Addr) {}, /*ProcessNoteFn=*/FindAndroidNote, /*FinishNotesFn=*/[]() {}); - printMemtag(DynamicEntries, AndroidNoteDesc); + ArrayRef Contents = getMemtagGlobalsSectionContents(this->Obj); + if (Contents.size() != AndroidMemtagGlobalsSz) { + llvm::errs() << "Mismatch between DT_AARCH64_MEMTAG_GLOBALSSZ (" + << format_hex(AndroidMemtagGlobalsSz, 0) + << ") and SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section size (" + << format_hex(Contents.size(), 0) << ")\n"; + return; + } + + std::vector> GlobalDescriptors; + + uint64_t Address = 0; + for (size_t i = 0; i < Contents.size();) { + const char *Error = nullptr; + unsigned DecodedBytes = 0; + uint64_t Value = decodeULEB128(Contents.data() + i, &DecodedBytes, + Contents.end(), &Error); + i += DecodedBytes; + if (Error) { + llvm::errs() << "Error decoding SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC: \"" + << Twine(Error) << "\"\n"; + break; + } + uint64_t Distance = Value >> kMemtagStepVarintReservedBits; + uint64_t GranulesToTag = Value & ((1 << kMemtagStepVarintReservedBits) - 1); + if (GranulesToTag == 0) { + GranulesToTag = decodeULEB128(Contents.data() + i, &DecodedBytes, + Contents.end(), &Error) + + 1; + i += DecodedBytes; + if (Error) { + llvm::errs() << "Error decoding SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC: \"" + << Twine(Error) << "\"\n"; + break; + } + } + Address += Distance * kMemtagGranuleSize; + GlobalDescriptors.emplace_back(Address, GranulesToTag * kMemtagGranuleSize); + Address += GranulesToTag * kMemtagGranuleSize; + } + + printMemtag(DynamicEntries, AndroidNoteDesc, GlobalDescriptors); } template void GNUELFDumper::printELFLinkerOptions() { @@ -7290,7 +7370,8 @@ return true; } -static bool printAndroidNoteLLVMStyle(uint32_t NoteType, ArrayRef Desc, +static bool printAndroidNoteLLVMStyle(uint32_t NoteType, + ArrayRef Desc, ScopedPrinter &W) { // Return true if we were able to pretty-print the note, false otherwise. AndroidNoteProperties Props = getAndroidNoteProperties(NoteType, Desc); @@ -7304,17 +7385,31 @@ template void LLVMELFDumper::printMemtag( const ArrayRef> DynamicEntries, - const ArrayRef AndroidNoteDesc) { - ListScope L(W, "Memtag Dynamic Entries:"); - if (DynamicEntries.empty()) - W.printString("< none found >"); - for (const auto &DynamicEntryKV : DynamicEntries) - W.printString(DynamicEntryKV.first, DynamicEntryKV.second); + const ArrayRef AndroidNoteDesc, + const ArrayRef> Descriptors) { + { + ListScope L(W, "Memtag Dynamic Entries:"); + if (DynamicEntries.empty()) + W.printString("< none found >"); + for (const auto &DynamicEntryKV : DynamicEntries) + W.printString(DynamicEntryKV.first, DynamicEntryKV.second); + } if (!AndroidNoteDesc.empty()) { ListScope L(W, "Memtag Android Note:"); printAndroidNoteLLVMStyle(ELF::NT_ANDROID_TYPE_MEMTAG, AndroidNoteDesc, W); } + + if (Descriptors.empty()) + return; + + { + ListScope L(W, "Memtag Global Descriptors:"); + for (const auto &Descriptor : Descriptors) { + W.printHex(std::string("0x") + llvm::utohexstr(Descriptor.first), + Descriptor.second); + } + } } template