[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