[llvm-readobj] Add --memtag
authorMitch Phillips <31459023+hctim@users.noreply.github.com>
Wed, 1 Mar 2023 18:49:39 +0000 (10:49 -0800)
committerMitch Phillips <31459023+hctim@users.noreply.github.com>
Wed, 1 Mar 2023 18:59:59 +0000 (10:59 -0800)
This adds functionality to readelf/readobj to specifically handle
MTE-related bits, like the AARCH64_MEMTAG_* dynamic entries, and a
decoder for the Android-specific ELF note.

Reviewed By: jhenderson, MaskRay

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

llvm/docs/CommandGuide/llvm-readelf.rst
llvm/docs/CommandGuide/llvm-readobj.rst
llvm/include/llvm/BinaryFormat/DynamicTags.def
llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test [new file with mode: 0644]
llvm/test/tools/llvm-readobj/ELF/AArch64/note-android-memtag.test [deleted file]
llvm/tools/llvm-readobj/ELFDumper.cpp
llvm/tools/llvm-readobj/ObjDumper.h
llvm/tools/llvm-readobj/Opts.td
llvm/tools/llvm-readobj/llvm-readobj.cpp

index dd4d821..2ad217c 100644 (file)
@@ -116,6 +116,12 @@ OPTIONS
  Display the specified section(s) as hexadecimal bytes. ``section`` may be a
  section index or section name.
 
+.. option:: --memtag
+
+ Display information about memory tagging present in the binary. This includes
+ various memtag-specific dynamic entries, decoded global descriptor sections,
+ and decoded Android-specific ELF notes.
+
 .. option:: --needed-libs
 
  Display the needed libraries.
index 6167190..cb9232e 100644 (file)
@@ -79,6 +79,12 @@ file formats.
  Display the specified section(s) as hexadecimal bytes. ``section`` may be a
  section index or section name.
 
+ .. option:: --memtag
+
+ Display information about memory tagging present in the binary. This includes
+ various memtag-specific dynamic entries, decoded global descriptor sections,
+ and decoded Android-specific ELF notes.
+
 .. option:: --needed-libs
 
  Display the needed libraries.
@@ -208,6 +214,12 @@ The following options are implemented only for the ELF file format.
 
  Display the hash table for dynamic symbols.
 
+.. option:: --memtag
+
+ Display information about memory tagging present in the binary. This includes
+ various dynamic entries, decoded global descriptor sections, and decoded
+ Android-specific ELF notes.
+
 .. option:: --notes, -n
 
  Display all notes.
index ae25ec5..7bb248c 100644 (file)
@@ -126,6 +126,11 @@ DYNAMIC_TAG(VERNEEDNUM, 0X6FFFFFFF) // The number of entries in DT_VERNEED.
 AARCH64_DYNAMIC_TAG(AARCH64_BTI_PLT, 0x70000001)
 AARCH64_DYNAMIC_TAG(AARCH64_PAC_PLT, 0x70000003)
 AARCH64_DYNAMIC_TAG(AARCH64_VARIANT_PCS, 0x70000005)
+AARCH64_DYNAMIC_TAG(AARCH64_MEMTAG_MODE, 0x70000009)
+AARCH64_DYNAMIC_TAG(AARCH64_MEMTAG_HEAP, 0x7000000b)
+AARCH64_DYNAMIC_TAG(AARCH64_MEMTAG_STACK, 0x7000000c)
+AARCH64_DYNAMIC_TAG(AARCH64_MEMTAG_GLOBALS, 0x7000000d)
+AARCH64_DYNAMIC_TAG(AARCH64_MEMTAG_GLOBALSSZ, 0x7000000f)
 
 // Hexagon specific dynamic table entries
 HEXAGON_DYNAMIC_TAG(HEXAGON_SYMSZ, 0x70000000)
diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test b/llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test
new file mode 100644 (file)
index 0000000..107aafc
--- /dev/null
@@ -0,0 +1,220 @@
+# RUN: yaml2obj -D DESC='0d000000' -D MODE=1 -D HEAP=1 -D STACK=1 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,ASYNC,HEAP,STACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,ASYNC,HEAP,STACK
+
+# RUN: yaml2obj -D DESC='0e000000' -D MODE=0 -D HEAP=1 -D STACK=1 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,SYNC,HEAP,STACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,SYNC,HEAP,STACK
+
+# RUN: yaml2obj -D DESC='05000000' -D MODE=1 -D HEAP=1 -D STACK=0 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,ASYNC,HEAP,NOSTACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,ASYNC,HEAP,NOSTACK
+
+# RUN: yaml2obj -D DESC='06000000' -D MODE=0 -D HEAP=1 -D STACK=0 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,SYNC,HEAP,NOSTACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,SYNC,HEAP,NOSTACK
+
+# RUN: yaml2obj -D DESC='09000000' -D MODE=1 -D HEAP=0 -D STACK=1 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,ASYNC,NOHEAP,STACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,ASYNC,NOHEAP,STACK
+
+# RUN: yaml2obj -D DESC='0a000000' -D MODE=0 -D HEAP=0 -D STACK=1 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,SYNC,NOHEAP,STACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,SYNC,NOHEAP,STACK
+
+# RUN: yaml2obj -D DESC='03000000' -D MODE=2 -D HEAP=0 -D STACK=0 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,UNKNOWN,NOHEAP,NOSTACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,UNKNOWN,NOHEAP,NOSTACK
+
+# RUN: yaml2obj -D DESC='00000000' -D MODE=2 -D HEAP=0 -D STACK=0 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-OK,NONE,NOHEAP,NOSTACK
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,NONE,NOHEAP,NOSTACK
+
+# RUN: yaml2obj -D DESC='""' -D MODE=2 -D HEAP=2 -D STACK=2 %s -o %t
+# RUN: llvm-readelf --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=GNU,GNU-BAD,INVALID
+# RUN: llvm-readobj --notes --dynamic --memtag %t | FileCheck %s --check-prefixes=LLVM,LLVM-BAD,INVALID
+
+# LLVM: DynamicSection [ (6 entries)
+# LLVM:  Tag                Type                     Name/Value
+# GNU: Dynamic section
+# GNU-SAME: contains 6 entries
+
+# ASYNC:      0x0000000070000009
+# ASYNC-SAME: AARCH64_MEMTAG_MODE
+# ASYNC-SAME: Asynchronous (1)
+
+# SYNC:      0x0000000070000009
+# SYNC-SAME: AARCH64_MEMTAG_MODE
+# SYNC-SAME: Synchronous (0)
+
+# UNKNOWN:      0x0000000070000009
+# UNKNOWN-SAME: AARCH64_MEMTAG_MODE
+# UNKNOWN-SAME: Unknown (2)
+
+# INVALID:      0x0000000070000009
+# INVALID-SAME: AARCH64_MEMTAG_MODE
+# INVALID-SAME: Unknown (2)
+
+# HEAP:      0x000000007000000{{[bB]}}
+# HEAP-SAME: AARCH64_MEMTAG_HEAP
+# HEAP-SAME: Enabled (1)
+
+# NOHEAP:      0x000000007000000{{[bB]}}
+# NOHEAP-SAME: AARCH64_MEMTAG_HEAP
+# NOHEAP-SAME: Disabled (0)
+
+# INVALID:      0x000000007000000{{[bB]}}
+# INVALID-SAME: AARCH64_MEMTAG_HEAP
+# INVALID-SAME: Unknown (2)
+
+# STACK:      0x000000007000000{{[cC]}}
+# STACK-SAME: AARCH64_MEMTAG_STACK
+# STACK-SAME: Enabled (1)
+
+# NOSTACK:      0x000000007000000{{[cC]}}
+# NOSTACK-SAME: AARCH64_MEMTAG_STACK
+# NOSTACK-SAME: Disabled (0)
+
+# INVALID:      0x000000007000000{{[cC]}}
+# INVALID-SAME: AARCH64_MEMTAG_STACK
+# INVALID-SAME: Unknown (2)
+
+# LLVM:  0x000000007000000D AARCH64_MEMTAG_GLOBALS   0xdeadbeef
+# LLVM:  0x000000007000000F AARCH64_MEMTAG_GLOBALSSZ 1234
+
+# GNU: 0x000000007000000d (AARCH64_MEMTAG_GLOBALS)   0xdeadbeef0
+# GNU: 0x000000007000000f (AARCH64_MEMTAG_GLOBALSSZ) 1234
+
+# GNU:          Displaying notes found in: .note.android.memtag
+# GNU-NEXT:     Owner    Data size      Description
+# GNU-OK-NEXT:  Android  0x00000004  NT_ANDROID_TYPE_MEMTAG (Android memory tagging information)
+# GNU-BAD-NEXT: Android  0x00000000  NT_ANDROID_TYPE_MEMTAG (Android memory tagging information)
+
+# LLVM:      Notes [
+# LLVM-NEXT:   NoteSection {
+# LLVM-NEXT:     Name: .note.android.memtag
+# LLVM-NEXT:     Offset: 0x40
+# LLVM-OK-NEXT:  Size: 0x18
+# LLVM-BAD-NEXT: Size: 0x14
+# LLVM-NEXT:     Note {
+# LLVM-NEXT:       Owner: Android
+# LLVM-OK-NEXT:    Data size: 0x4
+# LLVM-BAD-NEXT:   Data size: 0x0
+# LLVM-NEXT:       Type: NT_ANDROID_TYPE_MEMTAG (Android memory tagging information)
+
+## Hint: Also used for the GNU tests.
+# INVALID-NEXT:    Invalid .note.android.memtag
+# NONE-NEXT:       Tagging Mode: NONE
+# ASYNC-NEXT:      Tagging Mode: ASYNC
+# SYNC-NEXT:       Tagging Mode: SYNC
+# UNKNOWN-NEXT:    Tagging Mode: Unknown (3)
+# HEAP-NEXT:       Heap: Enabled
+# NOHEAP-NEXT:     Heap: Disabled
+# STACK-NEXT:      Stack: Enabled
+# NOSTACK-NEXT:    Stack: Disabled
+
+# LLVM-NEXT:     }
+# LLVM-NEXT:   }
+# LLVM-NEXT: ]
+
+# LLVM: Memtag Dynamic Entries
+# GNU:  Memtag Dynamic Entries
+
+## Ensure that for --memtag, we don't print irrelevant dynamic entries.
+# LLVM-NOT: DT_INIT_ARRAY
+# GNU-NOT: DT_INIT_ARRAY
+
+# SYNC:    AARCH64_MEMTAG_MODE: Synchronous (0)
+# ASYNC:   AARCH64_MEMTAG_MODE: Asynchronous (1)
+# HEAP:    AARCH64_MEMTAG_HEAP: Enabled (1)
+# NOHEAP:  AARCH64_MEMTAG_HEAP: Disabled (0)
+# STACK:   AARCH64_MEMTAG_STACK: Enabled (1)
+# 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-OK: Memtag Android Note
+# GNU-OK:  Memtag Android Note
+
+# SYNC:    Tagging Mode: SYNC
+# ASYNC:   Tagging Mode: ASYNC
+# UNKNOWN: Tagging Mode: Unknown (3)
+# HEAP:    Heap: Enabled
+# NOHEAP:  Heap: Disabled
+# STACK:   Stack: Enabled
+# NOSTACK: Stack: Disabled
+
+#########################################
+## --docnum=1 (default)
+#########################################
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_AARCH64
+Sections:
+  - Name: .note.android.memtag
+    Type: SHT_NOTE
+    Notes:
+      - Name: Android
+        Type: NT_ANDROID_TYPE_MEMTAG
+        Desc: [[DESC]]
+  - Name: .dynamic
+    Type: SHT_DYNAMIC
+    Entries:
+      - Tag:   DT_AARCH64_MEMTAG_MODE
+        Value: [[MODE]]
+      - Tag:   DT_AARCH64_MEMTAG_HEAP
+        Value: [[HEAP]]
+      - Tag:   DT_AARCH64_MEMTAG_STACK
+        Value: [[STACK]]
+      - Tag:   DT_AARCH64_MEMTAG_GLOBALS
+        Value: 0xdeadbeef0
+      - Tag:   DT_AARCH64_MEMTAG_GLOBALSSZ
+        Value: 1234
+      - Tag:   DT_INIT_ARRAY
+        Value: 0x1000
+
+# 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 >
+# MISSING-GNU-NOT:  {{.}}
+
+# MISSING-LLVM-NOT:  {{.}}
+# MISSING-LLVM:      File: {{.*}}memtag.test
+# MISSING-LLVM-NEXT: Format: elf64-littleaarch64
+# MISSING-LLVM-NEXT: Arch: aarch64
+# MISSING-LLVM-NEXT: AddressSize: 64bit
+# MISSING-LLVM-NEXT: LoadName:
+# MISSING-LLVM-NEXT: Memtag Dynamic Entries: [
+# MISSING-LLVM-NEXT:   < none found >
+# MISSING-LLVM-NEXT: ]
+# MISSING-LLVM-NOT:  {{.}}
+
+#########################################
+## --docnum=2
+#########################################
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_AARCH64
+Sections:
+  - Name: .dynamic
+    Type: SHT_DYNAMIC
+    Entries:
+      - Tag:   DT_INIT_ARRAY
+        Value: 0x1000
diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/note-android-memtag.test b/llvm/test/tools/llvm-readobj/ELF/AArch64/note-android-memtag.test
deleted file mode 100644 (file)
index c500e7d..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-# RUN: yaml2obj -D DESC='0d000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,ASYNC,HEAP,STACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,ASYNC,HEAP,STACK
-
-# RUN: yaml2obj -D DESC='0e000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,SYNC,HEAP,STACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,SYNC,HEAP,STACK
-
-# RUN: yaml2obj -D DESC='05000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,ASYNC,HEAP,NOSTACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,ASYNC,HEAP,NOSTACK
-
-# RUN: yaml2obj -D DESC='06000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,SYNC,HEAP,NOSTACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,SYNC,HEAP,NOSTACK
-
-# RUN: yaml2obj -D DESC='09000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,ASYNC,NOHEAP,STACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,ASYNC,NOHEAP,STACK
-
-# RUN: yaml2obj -D DESC='0a000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,SYNC,NOHEAP,STACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,SYNC,NOHEAP,STACK
-
-# RUN: yaml2obj -D DESC='03000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,UNKNOWN,NOHEAP,NOSTACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,UNKNOWN,NOHEAP,NOSTACK
-
-# RUN: yaml2obj -D DESC='00000000' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-OK,NONE,NOHEAP,NOSTACK
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-OK,NONE,NOHEAP,NOSTACK
-
-# RUN: yaml2obj -D DESC='""' %s -o %t
-# RUN: llvm-readelf --notes %t | FileCheck %s --check-prefixes=GNU,GNU-BAD,INVALID
-# RUN: llvm-readobj --notes %t | FileCheck %s --check-prefixes=LLVM,LLVM-BAD,INVALID
-
-# GNU:          Displaying notes found in: .note.android.memtag
-# GNU-NEXT:     Owner    Data size      Description
-# GNU-OK-NEXT:  Android  0x00000004  NT_ANDROID_TYPE_MEMTAG (Android memory tagging information)
-# GNU-BAD-NEXT: Android  0x00000000  NT_ANDROID_TYPE_MEMTAG (Android memory tagging information)
-
-# LLVM:      Notes [
-# LLVM-NEXT:   NoteSection {
-# LLVM-NEXT:     Name: .note.android.memtag
-# LLVM-NEXT:     Offset: 0x40
-# LLVM-OK-NEXT:  Size: 0x18
-# LLVM-BAD-NEXT: Size: 0x14
-# LLVM-NEXT:     Note {
-# LLVM-NEXT:       Owner: Android
-# LLVM-OK-NEXT:    Data size: 0x4
-# LLVM-BAD-NEXT:   Data size: 0x0
-# LLVM-NEXT:       Type: NT_ANDROID_TYPE_MEMTAG (Android memory tagging information)
-
-## Hint: Also used for the GNU tests.
-# INVALID-NEXT:    Invalid .note.android.memtag
-# NONE-NEXT:       Tagging Mode: NONE
-# ASYNC-NEXT:      Tagging Mode: ASYNC
-# SYNC-NEXT:       Tagging Mode: SYNC
-# UNKNOWN-NEXT:    Tagging Mode: Unknown (3)
-# HEAP-NEXT:       Heap: Enabled
-# NOHEAP-NEXT:     Heap: Disabled
-# STACK-NEXT:      Stack: Enabled
-# NOSTACK-NEXT:    Stack: Disabled
-
-# LLVM-NEXT:     }
-# LLVM-NEXT:   }
-# LLVM-NEXT: ]
-
---- !ELF
-FileHeader:
-  Class: ELFCLASS64
-  Data:  ELFDATA2LSB
-  Type:  ET_DYN
-Sections:
-  - Name: .note.android.memtag
-    Type: SHT_NOTE
-    Notes:
-      - Name: Android
-        Type: NT_ANDROID_TYPE_MEMTAG
-        Desc: [[DESC]]
index 45fff0c..5615d75 100644 (file)
@@ -220,6 +220,7 @@ public:
   void printVersionInfo() override;
   void printArchSpecificInfo() override;
   void printStackMap() const override;
+  void printMemtag() override;
 
   const object::ELFObjectFile<ELFT> &getElfObject() const { return ObjF; };
 
@@ -296,6 +297,10 @@ protected:
   virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0;
   virtual void printMipsPLT(const MipsGOTParser<ELFT> &Parser) = 0;
 
+  virtual void printMemtag(
+      const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
+      const ArrayRef<uint8_t> AndroidNoteDesc) = 0;
+
   Expected<ArrayRef<Elf_Versym>>
   getVersionTable(const Elf_Shdr &Sec, ArrayRef<Elf_Sym> *SymTab,
                   StringRef *StrTab, const Elf_Shdr **SymTabSec) const;
@@ -577,6 +582,9 @@ public:
   void printNotes() override;
   void printELFLinkerOptions() override;
   void printStackSizes() override;
+  void printMemtag(
+      const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
+      const ArrayRef<uint8_t> AndroidNoteDesc) override;
 
 private:
   void printHashHistogram(const Elf_Hash &HashTable);
@@ -681,6 +689,9 @@ public:
   void printNotes() override;
   void printELFLinkerOptions() override;
   void printStackSizes() override;
+  void printMemtag(
+      const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
+      const ArrayRef<uint8_t> AndroidNoteDesc) override;
 
 private:
   void printRelrReloc(const Elf_Relr &R) override;
@@ -2251,7 +2262,29 @@ std::string ELFDumper<ELFT>::getDynamicEntry(uint64_t Type,
     case DT_AARCH64_BTI_PLT:
     case DT_AARCH64_PAC_PLT:
     case DT_AARCH64_VARIANT_PCS:
+    case DT_AARCH64_MEMTAG_GLOBALSSZ:
       return std::to_string(Value);
+    case DT_AARCH64_MEMTAG_MODE:
+      switch (Value) {
+        case 0:
+          return "Synchronous (0)";
+        case 1:
+          return "Asynchronous (1)";
+        default:
+          return (Twine("Unknown (") + Twine(Value) + ")").str();
+      }
+    case DT_AARCH64_MEMTAG_HEAP:
+    case DT_AARCH64_MEMTAG_STACK:
+      switch (Value) {
+        case 0:
+          return "Disabled (0)";
+        case 1:
+          return "Enabled (1)";
+        default:
+          return (Twine("Unknown (") + Twine(Value) + ")").str();
+      }
+    case DT_AARCH64_MEMTAG_GLOBALS:
+      return (Twine("0x") + utohexstr(Value, /*LowerCase=*/true)).str();
     default:
       break;
     }
@@ -5198,6 +5231,23 @@ static bool printAndroidNote(raw_ostream &OS, uint32_t NoteType,
   return true;
 }
 
+template <class ELFT>
+void GNUELFDumper<ELFT>::printMemtag(
+    const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
+    const ArrayRef<uint8_t> AndroidNoteDesc) {
+  OS << "Memtag Dynamic Entries:\n";
+  if (DynamicEntries.empty())
+    OS << "    < none found >\n";
+  for (const auto &DynamicEntryKV : DynamicEntries)
+    OS << "    " << DynamicEntryKV.first << ": " << DynamicEntryKV.second
+       << "\n";
+
+  if (!AndroidNoteDesc.empty()) {
+    OS << "Memtag Android Note:\n";
+    printAndroidNote(OS, ELF::NT_ANDROID_TYPE_MEMTAG, AndroidNoteDesc);
+  }
+}
+
 template <typename ELFT>
 static bool printLLVMOMPOFFLOADNote(raw_ostream &OS, uint32_t NoteType,
                                     ArrayRef<uint8_t> Desc) {
@@ -5681,7 +5731,7 @@ StringRef getNoteTypeName(const typename ELFT::Note &Note, unsigned ELFType) {
 }
 
 template <class ELFT>
-static void printNotesHelper(
+static void processNotesHelper(
     const ELFDumper<ELFT> &Dumper,
     llvm::function_ref<void(std::optional<StringRef>, typename ELFT::Off,
                             typename ELFT::Addr)>
@@ -5837,7 +5887,42 @@ template <class ELFT> void GNUELFDumper<ELFT>::printNotes() {
     return Error::success();
   };
 
-  printNotesHelper(*this, PrintHeader, ProcessNote, []() {});
+  processNotesHelper(*this, /*StartNotesFn=*/PrintHeader,
+                     /*ProcessNoteFn=*/ProcessNote, /*FinishNotesFn=*/[]() {});
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::printMemtag() {
+  if (Obj.getHeader().e_machine != EM_AARCH64) return;
+  std::vector<std::pair<std::string, std::string>> DynamicEntries;
+  for (const typename ELFT::Dyn &Entry : dynamic_table()) {
+    uintX_t Tag = Entry.getTag();
+    switch (Tag) {
+    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()));
+    }
+  }
+
+  ArrayRef<uint8_t> AndroidNoteDesc;
+  auto FindAndroidNote = [&](const Elf_Note &Note, bool IsCore) -> Error {
+    if (Note.getName() == "Android" &&
+        Note.getType() == ELF::NT_ANDROID_TYPE_MEMTAG)
+      AndroidNoteDesc = Note.getDesc();
+    return Error::success();
+  };
+
+  processNotesHelper(
+      *this,
+      /*StartNotesFn=*/
+      [](std::optional<StringRef>, const typename ELFT::Off,
+         const typename ELFT::Addr) {},
+      /*ProcessNoteFn=*/FindAndroidNote, /*FinishNotesFn=*/[]() {});
+
+  printMemtag(DynamicEntries, AndroidNoteDesc);
 }
 
 template <class ELFT> void GNUELFDumper<ELFT>::printELFLinkerOptions() {
@@ -7216,6 +7301,22 @@ static bool printAndroidNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc,
   return true;
 }
 
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printMemtag(
+    const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
+    const ArrayRef<uint8_t> 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);
+
+  if (!AndroidNoteDesc.empty()) {
+    ListScope L(W, "Memtag Android Note:");
+    printAndroidNoteLLVMStyle(ELF::NT_ANDROID_TYPE_MEMTAG, AndroidNoteDesc, W);
+  }
+}
+
 template <typename ELFT>
 static bool printLLVMOMPOFFLOADNoteLLVMStyle(uint32_t NoteType,
                                              ArrayRef<uint8_t> Desc,
@@ -7328,7 +7429,8 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printNotes() {
     return Error::success();
   };
 
-  printNotesHelper(*this, StartNotes, ProcessNote, EndNotes);
+  processNotesHelper(*this, /*StartNotesFn=*/StartNotes,
+                     /*ProcessNoteFn=*/ProcessNote, /*FinishNotesFn=*/EndNotes);
 }
 
 template <class ELFT> void LLVMELFDumper<ELFT>::printELFLinkerOptions() {
index 258d872..921792f 100644 (file)
@@ -136,6 +136,7 @@ public:
   virtual void printStackSizes() {}
   virtual void printSectionDetails() {}
   virtual void printArchSpecificInfo() {}
+  virtual void printMemtag() {}
 
   // Only implemented for PE/COFF.
   virtual void printCOFFImports() { }
index 4f7b12f..fec0adb 100644 (file)
@@ -55,6 +55,7 @@ def section_groups : FF<"section-groups", "Display section groups">, Group<grp_e
 def gnu_hash_table : FF<"gnu-hash-table", "Display the GNU hash table for dynamic symbols">, Group<grp_elf>;
 def hash_symbols : FF<"hash-symbols", "Display the dynamic symbols derived from the hash section">, Group<grp_elf>;
 def hash_table : FF<"hash-table", "Display .hash section">, Group<grp_elf>;
+def memtag : FF<"memtag", "Display memory tagging metadata (modes, Android notes, global descriptors)">, Group<grp_elf>;
 def needed_libs : FF<"needed-libs", "Display the needed libraries">, Group<grp_elf>;
 def notes : FF<"notes", "Display notes">, Group<grp_elf>;
 def program_headers : FF<"program-headers", "Display program headers">, Group<grp_elf>;
index aa49e30..d72eec0 100644 (file)
@@ -136,6 +136,7 @@ static bool GnuHashTable;
 static bool HashSymbols;
 static bool HashTable;
 static bool HashHistogram;
+static bool Memtag;
 static bool NeededLibraries;
 static bool Notes;
 static bool ProgramHeaders;
@@ -266,6 +267,7 @@ static void parseOptions(const opt::InputArgList &Args) {
   opts::HashSymbols = Args.hasArg(OPT_hash_symbols);
   opts::HashTable = Args.hasArg(OPT_hash_table);
   opts::HashHistogram = Args.hasArg(OPT_histogram);
+  opts::Memtag = Args.hasArg(OPT_memtag);
   opts::NeededLibraries = Args.hasArg(OPT_needed_libs);
   opts::Notes = Args.hasArg(OPT_notes);
   opts::PrettyPrint = Args.hasArg(OPT_pretty_print);
@@ -473,6 +475,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
       Dumper->printAddrsig();
     if (opts::Notes)
       Dumper->printNotes();
+    if (opts::Memtag)
+      Dumper->printMemtag();
   }
   if (Obj.isCOFF()) {
     if (opts::COFFImports)
@@ -684,6 +688,7 @@ int llvm_readobj_main(int argc, char **argv, const llvm::ToolContext &) {
       opts::Addrsig = true;
       opts::PrintStackSizes = true;
     }
+    opts::Memtag = true;
   }
 
   if (opts::Headers) {