[llvm] 7c1a631 - [MTE] [llvm-readobj] Add globals section parsing to --memtag

Mitch Phillips via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 12 10:24:44 PDT 2023


Author: Mitch Phillips
Date: 2023-04-12T10:24:13-07:00
New Revision: 7c1a6319ca03298b5d76af6a814178bafe9f2eba

URL: https://github.com/llvm/llvm-project/commit/7c1a6319ca03298b5d76af6a814178bafe9f2eba
DIFF: https://github.com/llvm/llvm-project/commit/7c1a6319ca03298b5d76af6a814178bafe9f2eba.diff

LOG: [MTE] [llvm-readobj] Add globals section parsing to --memtag

Global variables are described in a metadata table called
SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC. It's basically a ULEB-encoded skip
list with some other fancy encoding tricks to make it smaller. You can
see the ABI at
https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#83encoding-of-sht_aarch64_memtag_globals_dynamic

This extends readelf/readobj to understand these sections.

Reviewed By: pcc, MaskRay, jhenderson

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

Added: 
    

Modified: 
    llvm/lib/Object/ELF.cpp
    llvm/lib/ObjectYAML/ELFYAML.cpp
    llvm/test/tools/llvm-readobj/ELF/AArch64/memtag.test
    llvm/test/tools/llvm-readobj/ELF/machine-specific-section-types.test
    llvm/tools/llvm-readobj/ELFDumper.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index 715cb5c7765e4..b9e73848b171a 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -270,6 +270,11 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
   case ELF::EM_RISCV:
     switch (Type) { STRINGIFY_ENUM_CASE(ELF, SHT_RISCV_ATTRIBUTES); }
     break;
+  case ELF::EM_AARCH64:
+    switch (Type) {
+      STRINGIFY_ENUM_CASE(ELF, SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC);
+      STRINGIFY_ENUM_CASE(ELF, SHT_AARCH64_MEMTAG_GLOBALS_STATIC);
+    }
   default:
     break;
   }

diff  --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 0ca0348f36ed8..826bd05632511 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -707,6 +707,10 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration(
   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
index 107aafc571b01..c27577e10ddff 100644
--- 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,71 @@
 # STACK:   Stack: Enabled
 # NOSTACK: Stack: Disabled
 
+## Below is the maths for calculating the hand-written `.memtag.globals.dynamic` section contents.
+## This is based on the AArch64 MemtagABI:
+## https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#83encoding-of-sht_aarch64_memtag_globals_dynamic
+## You may find the following python one-liner helpful to encode your own values:
+##     `binascii.hexlify(leb128.u.encode(value_to_encode))`
+## Remember that a granule is 16 bytes.
+##   1. Tagged region of 16 bytes at 0xdead0000
+##      - Distance from the end of the last tagged region: 0xdead0000 bytes, 0xdead000 granules
+##      - Size: 0x10 bytes, 0x1 granules
+##      - Value to encode: (0xdead000 << 3) + 0x1 = 0x6f568001
+##      - ULEB-encoded value: 0x81 0x80 0xda 0xfa 0x06
+##   2. Tagged region of 32 bytes at 0xdead0010
+##      - Distance from the end of the last tagged region: 0x0 bytes, 0x0 granules
+##      - Size: 0x20 bytes, 0x2 granules
+##      - Value to encode: (0 << 3) + 0x2 == 0x2
+##      - ULEB-encoded value: 0x2
+##   3. Tagged region of 64 bytes at 0xdead0100
+##      - Distance: 0xdead0100 - 0xdead0010 - 32 = 0xd0 bytes, 0xd granules
+##      - Size: 0x40 bytes, 0x4 granules.
+##      - Value to encode: (0xd << 3) + 0x4 = 0x6c
+##      - ULEB-encoded value: 0x6c
+##   4. Tagged region of 0x1000 bytes at 0xdeadf000
+##      - Distance: 0xdeadf000 - 0xdead0100 - 64 = 0xeec0 bytes, 0xeec granules
+##      - Size: 0x1000 bytes, 0x100 granules.
+##        (note: size of 0x100 granules exceeds the 3-bit size allowance in value to encode, so the
+##         size needs to go to its own value, minus one).
+##      - 1st value to encode (distance only): (0xeec << 3) == 0x7760
+##      - 1st ULEB-encoded value: 0xe0 0xee 0x01
+##      - 2nd value to encode (size only): 0x100 - 1
+##      - 2nd ULEB-encoded value: 0xff 0x01
+##   5. Tagged region of 16 bytes at 0xdeae0000
+##      - Distance: 0xdeae000 - 0xdeadf000 - 0x1000 = 0x0 bytes, 0x0 granules (regions are adjacent)
+##      - Size: 0x10 bytes, 0x1 granules
+##      - Value to encode: (0x0 << 3) + 0x1
+##      - ULEB-encoded value: 0x01
+##   6. Tagged region of 16 bytes at 0xdeae0010
+##      - Distance: 0x0 (regions are adjacent)
+##      - Size: 0x10 bytes, 0x1 granules
+##      - Value to encode: (0x0 << 3) + 0x1
+##      - ULEB-encoded value: 0x01
+##   6. Tagged region of 16 bytes at 0xdeae0020
+##      - Distance: 0x0 (regions are adjacent)
+##      - Size: 0x10 bytes, 0x1 granules
+##      - Value to encode: (0x0 << 3) + 0x1
+##      - ULEB-encoded value: 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 +240,25 @@ Sections:
       - 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:         0xdeadbeef0
+    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 +291,96 @@ Sections:
     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:   -D DT_AARCH64_MEMTAG_GLOBALSSZ=0x1337 \
+# RUN:   -D GLOBALS_SECTION_CONTENTS=12345678901234567890
+# 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:      warning: {{.*}} mismatch between DT_AARCH64_MEMTAG_GLOBALSSZ (0x1337) and
+# SIZE-MISMATCH-SAME: SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section size (0xa)
+
+#########################################
+## Ensure that we fail if DT_AARCH64_MEMTAG_GLOBALS doesn't agree with the address of the section.
+#########################################
+
+# RUN: yaml2obj --docnum=3 %s -o %t \
+# RUN:   -D DT_AARCH64_MEMTAG_GLOBALS=0xdeadbeef123 \
+# RUN:   -D DT_AARCH64_MEMTAG_GLOBALSSZ=10 \
+# RUN:   -D GLOBALS_SECTION_CONTENTS=00000000000000000000
+# RUN: llvm-readelf --memtag %t 2>&1 | FileCheck %s --check-prefixes=BAD-SECTION
+# RUN: llvm-readobj --memtag %t 2>&1 | FileCheck %s --check-prefixes=BAD-SECTION
+
+# BAD-SECTION:      warning: {{.*}} SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section was unexpectedly at
+# BAD-SECTION-SAME: 0xdeadbeef, when DT_AARCH64_MEMTAG_GLOBALS says it should be at 0xdeadbeef123
+
+#########################################
+## Ensure that we fail if the ULEB-encoded globals stream can't be decoded.
+#########################################
+
+# RUN: yaml2obj --docnum=3 %s -o %t \
+# RUN:   -D DT_AARCH64_MEMTAG_GLOBALSSZ=3 \
+# RUN:   -D GLOBALS_SECTION_CONTENTS=808080
+# RUN: llvm-readelf --memtag %t 2>&1 | FileCheck %s --check-prefixes=BAD-STREAM1
+# RUN: llvm-readobj --memtag %t 2>&1 | FileCheck %s --check-prefixes=BAD-STREAM1
+
+# BAD-STREAM1:      warning: {{.*}} error decoding distance uleb, 3 byte(s) into
+# BAD-STREAM1-SAME: SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC
+
+# RUN: yaml2obj --docnum=3 %s -o %t \
+# RUN:   -D DT_AARCH64_MEMTAG_GLOBALSSZ=2 \
+# RUN:   -D GLOBALS_SECTION_CONTENTS=0080
+# RUN: llvm-readelf --memtag %t 2>&1 | FileCheck %s --check-prefixes=BAD-STREAM2
+# RUN: llvm-readobj --memtag %t 2>&1 | FileCheck %s --check-prefixes=BAD-STREAM2
+
+# BAD-STREAM2:      warning: {{.*}} error decoding size-only uleb, 1 byte(s) into
+# BAD-STREAM2-SAME: SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC
+
+# RUN: yaml2obj --docnum=3 %s -o %t \
+# RUN:   -D SH_OFFSET=0xffff \
+# RUN:   -D DT_AARCH64_MEMTAG_GLOBALSSZ=10 \
+# RUN:   -D GLOBALS_SECTION_CONTENTS=00000000000000000000
+# RUN: llvm-readelf --memtag %t 2>&1 | FileCheck %s --check-prefixes=UNREADABLE-SECTION
+# RUN: llvm-readobj --memtag %t 2>&1 | FileCheck %s --check-prefixes=UNREADABLE-SECTION
+
+# UNREADABLE-SECTION:      couldn't get SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section contents: section
+# UNREADABLE-SECTION-SAME: [index 2] has a sh_offset (0xffff) + sh_size (0xa) that is greater than
+# UNREADABLE-SECTION-SAME: the file size
+
+#########################################
+## --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: [[DT_AARCH64_MEMTAG_GLOBALS=0xdeadbeef]]
+      - Tag:   DT_AARCH64_MEMTAG_GLOBALSSZ
+        Value: [[DT_AARCH64_MEMTAG_GLOBALSSZ]]
+  - Name:         .memtag.globals.dynamic
+    Type:         SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC
+    Flags:        [ SHF_ALLOC ]
+    Address:      0xdeadbeef
+    AddressAlign: 0x4
+    Content:      [[GLOBALS_SECTION_CONTENTS]]
+    ShOffset:     [[SH_OFFSET=<none>]]

diff  --git a/llvm/test/tools/llvm-readobj/ELF/machine-specific-section-types.test b/llvm/test/tools/llvm-readobj/ELF/machine-specific-section-types.test
index e564bf199873a..99fafe35fa443 100644
--- a/llvm/test/tools/llvm-readobj/ELF/machine-specific-section-types.test
+++ b/llvm/test/tools/llvm-readobj/ELF/machine-specific-section-types.test
@@ -13,6 +13,10 @@
 # RUN: llvm-readobj --section-headers %t-mips.o | FileCheck %s --check-prefix=MIPS-LLVM
 # RUN: llvm-readelf --section-headers %t-mips.o | FileCheck %s --check-prefix=MIPS-GNU
 
+# RUN: yaml2obj %s --docnum=4 -o %t-aarch64.o
+# RUN: llvm-readobj --section-headers %t-aarch64.o | FileCheck %s --check-prefix=AARCH64-LLVM
+# RUN: llvm-readelf --section-headers %t-aarch64.o | FileCheck %s --check-prefix=AARCH64-GNU
+
 # ARM-LLVM: Name: exidx
 # ARM-LLVM: Type: SHT_ARM_EXIDX
 # ARM-LLVM: Name: preemptmap
@@ -49,6 +53,14 @@
 # MIPS-GNU: abiflags MIPS_ABIFLAGS
 # MIPS-GNU: dwarf    MIPS_DWARF
 
+# AARCH64-LLVM: Name: .memtag.globals.dynamic
+# AARCH64-LLVM: Type: SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC
+# AARCH64-LLVM: Name: .memtag.globals.static
+# AARCH64-LLVM: Type: SHT_AARCH64_MEMTAG_GLOBALS_STATIC
+
+# AARCH64-GNU: .memtag.globals.dynamic AARCH64_MEMTAG_GLOBALS_DYNAMIC
+# AARCH64-GNU: .memtag.globals.static  AARCH64_MEMTAG_GLOBALS_STATIC
+
 --- !ELF
 FileHeader:
   Class:   ELFCLASS64
@@ -93,3 +105,15 @@ Sections:
     ISA:  MIPS64
   - Name: dwarf
     Type: SHT_MIPS_DWARF
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_AARCH64
+Sections:
+  - Name:  .memtag.globals.dynamic
+    Type:  SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC
+  - Name:  .memtag.globals.static
+    Type:  SHT_AARCH64_MEMTAG_GLOBALS_STATIC

diff  --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 0298e8dce50ed..1d0c795c3a205 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -221,6 +221,7 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   void printArchSpecificInfo() override;
   void printStackMap() const override;
   void printMemtag() override;
+  ArrayRef<uint8_t> getMemtagGlobalsSectionContents(uintptr_t ExpectedAddr);
 
   // Hash histogram shows statistics of how efficient the hash was for the
   // dynamic symbol table. The table shows the number of hash buckets for
@@ -309,7 +310,8 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
 
   virtual void printMemtag(
       const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
-      const ArrayRef<uint8_t> AndroidNoteDesc) = 0;
+      const ArrayRef<uint8_t> AndroidNoteDesc,
+      const ArrayRef<std::pair<uint64_t, uint64_t>> Descriptors) = 0;
 
   virtual void printHashHistogram(const Elf_Hash &HashTable) const;
   virtual void printGnuHashHistogram(const Elf_GnuHash &GnuHashTable) const;
@@ -593,7 +595,8 @@ template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
   void printStackSizes() override;
   void printMemtag(
       const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
-      const ArrayRef<uint8_t> AndroidNoteDesc) override;
+      const ArrayRef<uint8_t> AndroidNoteDesc,
+      const ArrayRef<std::pair<uint64_t, uint64_t>> Descriptors) override;
   void printHashHistogramStats(size_t NBucket, size_t MaxChain,
                                size_t TotalSyms, ArrayRef<size_t> Count,
                                bool IsGnu) const override;
@@ -700,7 +703,8 @@ template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> {
   void printStackSizes() override;
   void printMemtag(
       const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
-      const ArrayRef<uint8_t> AndroidNoteDesc) override;
+      const ArrayRef<uint8_t> AndroidNoteDesc,
+      const ArrayRef<std::pair<uint64_t, uint64_t>> Descriptors) override;
   void printSymbolSection(const Elf_Sym &Symbol, unsigned SymIndex,
                           DataRegion<Elf_Word> ShndxTable) const;
   void printHashHistogramStats(size_t NBucket, size_t MaxChain,
@@ -5292,14 +5296,14 @@ static bool printAndroidNote(raw_ostream &OS, uint32_t NoteType,
     return false;
   for (const auto &KV : Props)
     OS << "    " << KV.first << ": " << KV.second << '\n';
-  OS << '\n';
   return true;
 }
 
 template <class ELFT>
 void GNUELFDumper<ELFT>::printMemtag(
     const ArrayRef<std::pair<std::string, std::string>> DynamicEntries,
-    const ArrayRef<uint8_t> AndroidNoteDesc) {
+    const ArrayRef<uint8_t> AndroidNoteDesc,
+    const ArrayRef<std::pair<uint64_t, uint64_t>> Descriptors) {
   OS << "Memtag Dynamic Entries:\n";
   if (DynamicEntries.empty())
     OS << "    < none found >\n";
@@ -5311,6 +5315,15 @@ void GNUELFDumper<ELFT>::printMemtag(
     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 &[Addr, BytesToTag] : Descriptors) {
+    OS << "    0x" << utohexstr(Addr, /*LowerCase=*/true) << ": 0x"
+       << utohexstr(BytesToTag, /*LowerCase=*/true) << "\n";
+  }
 }
 
 template <typename ELFT>
@@ -5956,19 +5969,62 @@ template <class ELFT> void GNUELFDumper<ELFT>::printNotes() {
                      /*ProcessNoteFn=*/ProcessNote, /*FinishNotesFn=*/[]() {});
 }
 
+template <class ELFT>
+ArrayRef<uint8_t>
+ELFDumper<ELFT>::getMemtagGlobalsSectionContents(uintptr_t ExpectedAddr) {
+  for (const typename ELFT::Shdr &Sec : cantFail(Obj.sections())) {
+    if (Sec.sh_type != SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC)
+      continue;
+    if (Sec.sh_addr != ExpectedAddr) {
+      reportUniqueWarning(
+          "SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section was unexpectedly at 0x" +
+          Twine::utohexstr(Sec.sh_addr) +
+          ", when DT_AARCH64_MEMTAG_GLOBALS says it should be at 0x" +
+          Twine::utohexstr(ExpectedAddr));
+      return ArrayRef<uint8_t>();
+    }
+    Expected<ArrayRef<uint8_t>> Contents = Obj.getSectionContents(Sec);
+    if (auto E = Contents.takeError()) {
+      reportUniqueWarning(
+          "couldn't get SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section contents: " +
+          toString(std::move(E)));
+      return ArrayRef<uint8_t>();
+    }
+    return Contents.get();
+  }
+  return ArrayRef<uint8_t>();
+}
+
+// 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 MemtagStepVarintReservedBits = 3;
+constexpr uint64_t MemtagGranuleSize = 16;
+
 template <typename ELFT> void ELFDumper<ELFT>::printMemtag() {
   if (Obj.getHeader().e_machine != EM_AARCH64) return;
   std::vector<std::pair<std::string, std::string>> DynamicEntries;
+  size_t MemtagGlobalsSz = 0;
+  uintptr_t MemtagGlobals = 0;
   for (const typename ELFT::Dyn &Entry : dynamic_table()) {
     uintX_t Tag = Entry.getTag();
     switch (Tag) {
+    case DT_AARCH64_MEMTAG_GLOBALSSZ:
+      MemtagGlobalsSz = Entry.getVal();
+      DynamicEntries.emplace_back(Obj.getDynamicTagAsString(Tag),
+                                  getDynamicEntry(Tag, Entry.getVal()));
+      break;
+    case DT_AARCH64_MEMTAG_GLOBALS:
+      MemtagGlobals = Entry.getVal();
+      DynamicEntries.emplace_back(Obj.getDynamicTagAsString(Tag),
+                                  getDynamicEntry(Tag, Entry.getVal()));
+      break;
     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()));
+      break;
     }
   }
 
@@ -5987,7 +6043,54 @@ template <typename ELFT> void ELFDumper<ELFT>::printMemtag() {
          const typename ELFT::Addr) {},
       /*ProcessNoteFn=*/FindAndroidNote, /*FinishNotesFn=*/[]() {});
 
-  printMemtag(DynamicEntries, AndroidNoteDesc);
+  ArrayRef<uint8_t> Contents = getMemtagGlobalsSectionContents(MemtagGlobals);
+  if (Contents.size() != MemtagGlobalsSz) {
+    reportUniqueWarning(
+        "mismatch between DT_AARCH64_MEMTAG_GLOBALSSZ (0x" +
+        Twine::utohexstr(MemtagGlobalsSz) +
+        ") and SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section size (0x" +
+        Twine::utohexstr(Contents.size()) + ")");
+    Contents = ArrayRef<uint8_t>();
+  }
+
+  std::vector<std::pair<uint64_t, uint64_t>> GlobalDescriptors;
+  uint64_t Address = 0;
+  // See the AArch64 MemtagABI document for a description of encoding scheme:
+  // https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#83encoding-of-sht_aarch64_memtag_globals_dynamic
+  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) {
+      reportUniqueWarning(
+          "error decoding distance uleb, " + Twine(DecodedBytes) +
+          " byte(s) into SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC: " + Twine(Error));
+      GlobalDescriptors.clear();
+      break;
+    }
+    uint64_t Distance = Value >> MemtagStepVarintReservedBits;
+    uint64_t GranulesToTag = Value & ((1 << MemtagStepVarintReservedBits) - 1);
+    if (GranulesToTag == 0) {
+      GranulesToTag = decodeULEB128(Contents.data() + I, &DecodedBytes,
+                                    Contents.end(), &Error) +
+                      1;
+      I += DecodedBytes;
+      if (Error) {
+        reportUniqueWarning(
+            "error decoding size-only uleb, " + Twine(DecodedBytes) +
+            " byte(s) into SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC: " + Twine(Error));
+        GlobalDescriptors.clear();
+        break;
+      }
+    }
+    Address += Distance * MemtagGranuleSize;
+    GlobalDescriptors.emplace_back(Address, GranulesToTag * MemtagGranuleSize);
+    Address += GranulesToTag * MemtagGranuleSize;
+  }
+
+  printMemtag(DynamicEntries, AndroidNoteDesc, GlobalDescriptors);
 }
 
 template <class ELFT> void GNUELFDumper<ELFT>::printELFLinkerOptions() {
@@ -7410,17 +7513,30 @@ static bool printAndroidNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc,
 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);
+    const ArrayRef<uint8_t> AndroidNoteDesc,
+    const ArrayRef<std::pair<uint64_t, uint64_t>> 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 &[Addr, BytesToTag] : Descriptors) {
+      W.printHex("0x" + utohexstr(Addr), BytesToTag);
+    }
+  }
 }
 
 template <typename ELFT>


        


More information about the llvm-commits mailing list