[lld] ca35a19 - [lld] Synthesize metadata for MTE globals

Mitch Phillips via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 31 08:08:07 PDT 2023


Author: Mitch Phillips
Date: 2023-07-31T17:07:42+02:00
New Revision: ca35a19acab1cf6788c42037bbedeca86e34a455

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

LOG: [lld] Synthesize metadata for MTE globals

As per the ABI at
https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst,
this patch interprets the SHT_AARCH64_MEMTAG_GLOBALS_STATIC section,
which contains R_NONE relocations to tagged globals, and emits a
SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC section, with the correct
DT_AARCH64_MEMTAG_GLOBALS and DT_AARCH64_MEMTAG_GLOBALSSZ dynamic
entries. This section describes, in a uleb-encoded stream, global memory
ranges that should be tagged with MTE.

We are also out of bits to spare in the LLD Symbol class. As a result,
I've reused the 'needsTocRestore' bit, which is a PPC64 only feature.
Now, it's also used for 'isTagged' on AArch64.

An entry in SHT_AARCH64_MEMTAG_GLOBALS_STATIC is practically a guarantee
from an objfile that all references to the linked symbol are through the
GOT, and meet correct alignment requirements. As a result, we go through
all symbols and make sure that, for all symbols $SYM, all object files
that reference $SYM also have a SHT_AARCH64_MEMTAG_GLOBALS_STATIC entry
for $SYM. If this isn't the case, we demote the symbol to being
untagged. Symbols that are imported from other DSOs should always be
fine, as they're GOT-referenced (and thus the GOT entry either has the
correct tag or not, depending on whether it's tagged in the defining DSO
or not).

Additionally hand-tested by building {libc, libm, libc++, libm, and libnetd}
on Android with some experimental MTE globals support in the
linker/libc.

Reviewed By: MaskRay, peter.smith

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

Added: 
    lld/test/ELF/Inputs/aarch64-memtag-globals.s
    lld/test/ELF/aarch64-memtag-globals.s

Modified: 
    lld/ELF/Arch/AArch64.cpp
    lld/ELF/Arch/PPC64.cpp
    lld/ELF/Driver.cpp
    lld/ELF/Relocations.cpp
    lld/ELF/Relocations.h
    lld/ELF/Symbols.h
    lld/ELF/SyntheticSections.cpp
    lld/ELF/SyntheticSections.h
    lld/ELF/Target.h
    lld/ELF/Thunks.cpp
    lld/ELF/Writer.cpp
    lld/ELF/Writer.h
    lld/test/ELF/aarch64-memtag-android-abi.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index c83a159e3f0530..174a0a3624f776 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "InputFiles.h"
 #include "OutputSections.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
@@ -377,6 +378,20 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
     write32(loc, val);
     break;
   case R_AARCH64_ABS64:
+    // AArch64 relocations to tagged symbols have extended semantics, as
+    // described here:
+    // https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#841extended-semantics-of-r_aarch64_relative.
+    // tl;dr: encode the symbol's special addend in the place, which is an
+    // offset to the point where the logical tag is derived from. Quick hack, if
+    // the addend is within the symbol's bounds, no need to encode the tag
+    // derivation offset.
+    if (rel.sym && rel.sym->isTagged() &&
+        (rel.addend < 0 ||
+         rel.addend >= static_cast<int64_t>(rel.sym->getSize())))
+      write64(loc, -rel.addend);
+    else
+      write64(loc, val);
+    break;
   case R_AARCH64_PREL64:
     write64(loc, val);
     break;
@@ -745,6 +760,12 @@ bool AArch64Relaxer::tryRelaxAdrpLdr(const Relocation &adrpRel,
   return true;
 }
 
+// Tagged symbols have upper address bits that are added by the dynamic loader,
+// and thus need the full 64-bit GOT entry. Do not relax such symbols.
+static bool needsGotForMemtag(const Relocation &rel) {
+  return rel.sym->isTagged() && needsGot(rel.expr);
+}
+
 void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
   uint64_t secAddr = sec.getOutputSection()->addr;
   if (auto *s = dyn_cast<InputSection>(&sec))
@@ -756,6 +777,12 @@ void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
     const uint64_t val =
         sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
                              secAddr + rel.offset, *rel.sym, rel.expr);
+
+    if (needsGotForMemtag(rel)) {
+      relocate(loc, rel, val);
+      continue;
+    }
+
     switch (rel.expr) {
     case R_AARCH64_GOT_PAGE_PC:
       if (i + 1 < size &&
@@ -950,3 +977,107 @@ static TargetInfo *getTargetInfo() {
 }
 
 TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
+
+template <class ELFT>
+static void
+addTaggedSymbolReferences(InputSectionBase &sec,
+                          DenseMap<Symbol *, unsigned> &referenceCount) {
+  assert(sec.type == SHT_AARCH64_MEMTAG_GLOBALS_STATIC);
+
+  const RelsOrRelas<ELFT> rels = sec.relsOrRelas<ELFT>();
+  if (rels.areRelocsRel())
+    error("non-RELA relocations are not allowed with memtag globals");
+
+  for (const typename ELFT::Rela &rel : rels.relas) {
+    Symbol &sym = sec.getFile<ELFT>()->getRelocTargetSym(rel);
+    // Linker-synthesized symbols such as __executable_start may be referenced
+    // as tagged in input objfiles, and we don't want them to be tagged. A
+    // cheap way to exclude them is the type check, but their type is
+    // STT_NOTYPE. In addition, this save us from checking untaggable symbols,
+    // like functions or TLS symbols.
+    if (sym.type != STT_OBJECT)
+      continue;
+    // STB_LOCAL symbols can't be referenced from outside the object file, and
+    // thus don't need to be checked for references from other object files.
+    if (sym.binding == STB_LOCAL) {
+      sym.setIsTagged(true);
+      continue;
+    }
+    ++referenceCount[&sym];
+  }
+  sec.markDead();
+}
+
+// A tagged symbol must be denoted as being tagged by all references and the
+// chosen definition. For simplicity, here, it must also be denoted as tagged
+// for all definitions. Otherwise:
+//
+//  1. A tagged definition can be used by an untagged declaration, in which case
+//     the untagged access may be PC-relative, causing a tag mismatch at
+//     runtime.
+//  2. An untagged definition can be used by a tagged declaration, where the
+//     compiler has taken advantage of the increased alignment of the tagged
+//     declaration, but the alignment at runtime is wrong, causing a fault.
+//
+// Ideally, this isn't a problem, as any TU that imports or exports tagged
+// symbols should also be built with tagging. But, to handle these cases, we
+// demote the symbol to be untagged.
+void lld::elf::createTaggedSymbols(const SmallVector<ELFFileBase *, 0> &files) {
+  assert(config->emachine == EM_AARCH64 &&
+         config->androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE);
+
+  // First, collect all symbols that are marked as tagged, and count how many
+  // times they're marked as tagged.
+  DenseMap<Symbol *, unsigned> taggedSymbolReferenceCount;
+  for (InputFile* file : files) {
+    if (file->kind() != InputFile::ObjKind)
+      continue;
+    for (InputSectionBase *section : file->getSections()) {
+      if (!section || section->type != SHT_AARCH64_MEMTAG_GLOBALS_STATIC ||
+          section == &InputSection::discarded)
+        continue;
+      invokeELFT(addTaggedSymbolReferences, *section,
+                 taggedSymbolReferenceCount);
+    }
+  }
+
+  // Now, go through all the symbols. If the number of declarations +
+  // definitions to a symbol exceeds the amount of times they're marked as
+  // tagged, it means we have an objfile that uses the untagged variant of the
+  // symbol.
+  for (InputFile *file : files) {
+    if (file->kind() != InputFile::BinaryKind &&
+        file->kind() != InputFile::ObjKind)
+      continue;
+
+    for (Symbol *symbol : file->getSymbols()) {
+      // See `addTaggedSymbolReferences` for more details.
+      if (symbol->type != STT_OBJECT ||
+          symbol->binding == STB_LOCAL)
+        continue;
+      auto it = taggedSymbolReferenceCount.find(symbol);
+      if (it == taggedSymbolReferenceCount.end()) continue;
+      unsigned &remainingAllowedTaggedRefs = it->second;
+      if (remainingAllowedTaggedRefs == 0) {
+        taggedSymbolReferenceCount.erase(it);
+        continue;
+      }
+      --remainingAllowedTaggedRefs;
+    }
+  }
+
+  // `addTaggedSymbolReferences` has already checked that we have RELA
+  // relocations, the only other way to get written addends is with
+  // --apply-dynamic-relocs.
+  if (!taggedSymbolReferenceCount.empty() && config->writeAddends)
+    error("--apply-dynamic-relocs cannot be used with MTE globals");
+
+  // Now, `taggedSymbolReferenceCount` should only contain symbols that are
+  // defined as tagged exactly the same amount as it's referenced, meaning all
+  // uses are tagged.
+  for (auto &[symbol, remainingTaggedRefs] : taggedSymbolReferenceCount) {
+    assert(remainingTaggedRefs == 0 &&
+            "Symbol is defined as tagged more times than it's used");
+    symbol->setIsTagged(true);
+  }
+}

diff  --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 36b1d0e3c9be44..15c792fd4b4cef 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -1556,7 +1556,7 @@ void PPC64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
         break;
 
       // Patch a nop (0x60000000) to a ld.
-      if (rel.sym->needsTocRestore) {
+      if (rel.sym->needsTocRestore()) {
         // gcc/gfortran 5.4, 6.3 and earlier versions do not add nop for
         // recursive calls even if the function is preemptible. This is not
         // wrong in the common case where the function is not preempted at

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index c2059c70e15a3d..8ce2f481f2df6d 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -786,13 +786,6 @@ static int getMemtagMode(opt::InputArgList &args) {
     return ELF::NT_MEMTAG_LEVEL_NONE;
   }
 
-  if (!config->androidMemtagHeap && !config->androidMemtagStack) {
-    error("when using --android-memtag-mode, at least one of "
-          "--android-memtag-heap or "
-          "--android-memtag-stack is required");
-    return ELF::NT_MEMTAG_LEVEL_NONE;
-  }
-
   if (memtagModeArg == "sync")
     return ELF::NT_MEMTAG_LEVEL_SYNC;
   if (memtagModeArg == "async")
@@ -2940,6 +2933,12 @@ void LinkerDriver::link(opt::InputArgList &args) {
   // partition.
   copySectionsIntoPartitions();
 
+  if (config->emachine == EM_AARCH64 &&
+      config->androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE) {
+    llvm::TimeTraceScope timeScope("Process memory tagged symbols");
+    createTaggedSymbols(ctx.objectFiles);
+  }
+
   // Create synthesized sections such as .got and .plt. This is called before
   // processSectionCommands() so that they can be placed by SECTIONS commands.
   invokeELFT(createSyntheticSections,);

diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index af15f5a92546e2..9305c959d0f0f6 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -53,6 +53,7 @@
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Demangle/Demangle.h"
 #include "llvm/Support/Endian.h"
 #include <algorithm>
@@ -199,10 +200,7 @@ static bool needsPlt(RelExpr expr) {
                R_PPC32_PLTREL, R_PPC64_CALL_PLT>(expr);
 }
 
-// Returns true if Expr refers a GOT entry. Note that this function
-// returns false for TLS variables even though they need GOT, because
-// TLS variables uses GOT 
diff erently than the regular variables.
-static bool needsGot(RelExpr expr) {
+bool lld::elf::needsGot(RelExpr expr) {
   return oneof<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
                R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT,
                R_AARCH64_GOT_PAGE, R_LOONGARCH_GOT, R_LOONGARCH_GOT_PAGE_PC>(
@@ -859,6 +857,23 @@ static void addRelativeReloc(InputSectionBase &isec, uint64_t offsetInSec,
                              RelType type) {
   Partition &part = isec.getPartition();
 
+  if (sym.isTagged()) {
+    std::lock_guard<std::mutex> lock(relocMutex);
+    part.relaDyn->addRelativeReloc(target->relativeRel, isec, offsetInSec, sym,
+                                   addend, type, expr);
+    // With MTE globals, we always want to derive the address tag by `ldg`-ing
+    // the symbol. When we have a RELATIVE relocation though, we no longer have
+    // a reference to the symbol. Because of this, when we have an addend that
+    // puts the result of the RELATIVE relocation out-of-bounds of the symbol
+    // (e.g. the addend is outside of [0, sym.getSize()]), the AArch64 MemtagABI
+    // says we should store the offset to the start of the symbol in the target
+    // field. This is described in further detail in:
+    // https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#841extended-semantics-of-r_aarch64_relative
+    if (addend < 0 || static_cast<uint64_t>(addend) >= sym.getSize())
+      isec.relocations.push_back({expr, type, offsetInSec, addend, &sym});
+    return;
+  }
+
   // Add a relative relocation. If relrDyn section is enabled, and the
   // relocation offset is guaranteed to be even, add the relocation to
   // the relrDyn section, otherwise add it to the relaDyn section.
@@ -1645,6 +1660,10 @@ void elf::postScanRelocations() {
     auto flags = sym.flags.load(std::memory_order_relaxed);
     if (handleNonPreemptibleIfunc(sym, flags))
       return;
+
+    if (sym.isTagged() && sym.isDefined())
+      mainPart->memtagDescriptors->addSymbol(sym);
+
     if (!sym.needsDynReloc())
       return;
     sym.allocateAux();

diff  --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index e36215bd0d936b..0559245a8f2c00 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -220,6 +220,11 @@ ArrayRef<RelTy> sortRels(ArrayRef<RelTy> rels, SmallVector<RelTy, 0> &storage) {
   }
   return rels;
 }
+
+// Returns true if Expr refers a GOT entry. Note that this function returns
+// false for TLS variables even though they need GOT, because TLS variables uses
+// GOT 
diff erently than the regular variables.
+bool needsGot(RelExpr expr);
 } // namespace lld::elf
 
 #endif

diff  --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index bb440530b4df9c..4addb79d125791 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -254,8 +254,8 @@ class Symbol {
   Symbol(Kind k, InputFile *file, StringRef name, uint8_t binding,
          uint8_t stOther, uint8_t type)
       : file(file), nameData(name.data()), nameSize(name.size()), type(type),
-        binding(binding), stOther(stOther), symbolKind(k),
-        exportDynamic(false) {}
+        binding(binding), stOther(stOther), symbolKind(k), exportDynamic(false),
+        archSpecificBit(false) {}
 
   void overwrite(Symbol &sym, Kind k) const {
     if (sym.traced)
@@ -279,9 +279,18 @@ class Symbol {
   // True if defined relative to a section discarded by ICF.
   uint8_t folded : 1;
 
-  // True if a call to this symbol needs to be followed by a restore of the
-  // PPC64 toc pointer.
-  uint8_t needsTocRestore : 1;
+  // Allow reuse of a bit between architecture-exclusive symbol flags.
+  // - needsTocRestore(): On PPC64, true if a call to this symbol needs to be
+  //   followed by a restore of the toc pointer.
+  // - isTagged(): On AArch64, true if the symbol needs special relocation and
+  //   metadata semantics because it's tagged, under the AArch64 MemtagABI.
+  uint8_t archSpecificBit : 1;
+  bool needsTocRestore() const { return archSpecificBit; }
+  bool isTagged() const { return archSpecificBit; }
+  void setNeedsTocRestore(bool v) { archSpecificBit = v; }
+  void setIsTagged(bool v) {
+    archSpecificBit = v;
+  }
 
   // True if this symbol is defined by a symbol assignment or wrapped by --wrap.
   //

diff  --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index de25750bf9ebf5..09090835af4a62 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1453,6 +1453,10 @@ DynamicSection<ELFT>::computeContents() {
       addInt(DT_AARCH64_MEMTAG_MODE, config->androidMemtagMode == NT_MEMTAG_LEVEL_ASYNC);
       addInt(DT_AARCH64_MEMTAG_HEAP, config->androidMemtagHeap);
       addInt(DT_AARCH64_MEMTAG_STACK, config->androidMemtagStack);
+      if (mainPart->memtagDescriptors->isNeeded()) {
+        addInSec(DT_AARCH64_MEMTAG_GLOBALS, *mainPart->memtagDescriptors);
+        addInt(DT_AARCH64_MEMTAG_GLOBALSSZ, mainPart->memtagDescriptors->getSize());
+      }
     }
   }
 
@@ -3900,6 +3904,76 @@ size_t PackageMetadataNote::getSize() const {
          alignTo(config->packageMetadata.size() + 1, 4);
 }
 
+// Helper function, return the size of the ULEB128 for 'v', optionally writing
+// it to `*(buf + offset)` if `buf` is non-null.
+static size_t computeOrWriteULEB128(uint64_t v, uint8_t *buf, size_t offset) {
+  if (buf)
+    return encodeULEB128(v, buf + offset);
+  return getULEB128Size(v);
+}
+
+// https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#83encoding-of-sht_aarch64_memtag_globals_dynamic
+constexpr uint64_t kMemtagStepSizeBits = 3;
+constexpr uint64_t kMemtagGranuleSize = 16;
+static size_t createMemtagDescriptors(const SmallVector<const Symbol *, 0> &symbols,
+                                      uint8_t *buf = nullptr) {
+  size_t sectionSize = 0;
+  uint64_t lastGlobalEnd = 0;
+
+  for (const Symbol *sym : symbols) {
+    if (!includeInSymtab(*sym))
+      continue;
+    const uint64_t addr = sym->getVA();
+    const uint64_t size = sym->getSize();
+
+    if (addr <= kMemtagGranuleSize && buf != nullptr)
+      errorOrWarn("address of the tagged symbol \"" + sym->getName() +
+                  "\" falls in the ELF header. This is indicative of a "
+                  "compiler/linker bug");
+    if (addr % kMemtagGranuleSize != 0)
+      errorOrWarn("address of the tagged symbol \"" + sym->getName() +
+                  "\" at 0x" + Twine::utohexstr(addr) +
+                  "\" is not granule (16-byte) aligned");
+    if (size == 0)
+      errorOrWarn("size of the tagged symbol \"" + sym->getName() +
+                  "\" is not allowed to be zero");
+    if (size % kMemtagGranuleSize != 0)
+      errorOrWarn("size of the tagged symbol \"" + sym->getName() +
+                  "\" (size 0x" + Twine::utohexstr(size) +
+                  ") is not granule (16-byte) aligned");
+
+    const uint64_t sizeToEncode = size / kMemtagGranuleSize;
+    const uint64_t stepToEncode = ((addr - lastGlobalEnd) / kMemtagGranuleSize)
+                                  << kMemtagStepSizeBits;
+    if (sizeToEncode < (1 << kMemtagStepSizeBits)) {
+      sectionSize += computeOrWriteULEB128(stepToEncode | sizeToEncode, buf, sectionSize);
+    } else {
+      sectionSize += computeOrWriteULEB128(stepToEncode, buf, sectionSize);
+      sectionSize += computeOrWriteULEB128(sizeToEncode - 1, buf, sectionSize);
+    }
+    lastGlobalEnd = addr + size;
+  }
+
+  return sectionSize;
+}
+
+bool MemtagDescriptors::updateAllocSize() {
+  size_t oldSize = getSize();
+  std::stable_sort(symbols.begin(), symbols.end(),
+                   [](const Symbol *s1, const Symbol *s2) {
+                     return s1->getVA() < s2->getVA();
+                   });
+  return oldSize != getSize();
+}
+
+void MemtagDescriptors::writeTo(uint8_t *buf) {
+  createMemtagDescriptors(symbols, buf);
+}
+
+size_t MemtagDescriptors::getSize() const {
+  return createMemtagDescriptors(symbols);
+}
+
 InStruct elf::in;
 
 std::vector<Partition> elf::partitions;

diff  --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 38d0c80a073de5..21a3f768ffcf8e 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -22,8 +22,10 @@
 
 #include "Config.h"
 #include "InputSection.h"
+#include "Symbols.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/MapVector.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/MC/StringTableBuilder.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Endian.h"
@@ -1245,6 +1247,32 @@ class PackageMetadataNote final : public SyntheticSection {
   size_t getSize() const override;
 };
 
+class MemtagDescriptors final : public SyntheticSection {
+public:
+  MemtagDescriptors()
+      : SyntheticSection(llvm::ELF::SHF_ALLOC,
+                         llvm::ELF::SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC,
+                         /*alignment=*/4, ".memtag.globals.dynamic") {}
+  void writeTo(uint8_t *buf) override;
+  // The size of the section is non-computable until all addresses are
+  // synthetized, because the section's contents contain a sorted
+  // varint-compressed list of pointers to global variables. We only know the
+  // final size after `finalizeAddressDependentContent()`.
+  size_t getSize() const override;
+  bool updateAllocSize() override;
+
+  void addSymbol(const Symbol &sym) {
+    symbols.push_back(&sym);
+  }
+
+  bool isNeeded() const override {
+    return !symbols.empty();
+  }
+
+private:
+  SmallVector<const Symbol *, 0> symbols;
+};
+
 InputSection *createInterpSection();
 MergeInputSection *createCommentSection();
 template <class ELFT> void splitSections();
@@ -1277,6 +1305,7 @@ struct Partition {
   std::unique_ptr<GnuHashTableSection> gnuHashTab;
   std::unique_ptr<HashTableSection> hashTab;
   std::unique_ptr<MemtagAndroidNote> memtagAndroidNote;
+  std::unique_ptr<MemtagDescriptors> memtagDescriptors;
   std::unique_ptr<PackageMetadataNote> packageMetadataNote;
   std::unique_ptr<RelocationBaseSection> relaDyn;
   std::unique_ptr<RelrBaseSection> relrDyn;

diff  --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 9d4f22dd93f1bf..95109a3f35a5ac 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -14,6 +14,7 @@
 #include "lld/Common/ErrorHandler.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFTypes.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/MathExtras.h"
 #include <array>
@@ -233,6 +234,7 @@ void addArmInputSectionMappingSymbols();
 void addArmSyntheticSectionMappingSymbol(Defined *);
 void sortArmMappingSymbols();
 void convertArmInstructionstoBE8(InputSection *sec, uint8_t *buf);
+void createTaggedSymbols(const SmallVector<ELFFileBase *, 0> &files);
 
 LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
 TargetInfo *getTarget();
@@ -306,17 +308,17 @@ inline void write64(void *p, uint64_t v) {
 #endif
 #define invokeELFT(f, ...)                                                     \
   switch (config->ekind) {                                                     \
-  case ELF32LEKind:                                                            \
-    f<ELF32LE>(__VA_ARGS__);                                                   \
+  case lld::elf::ELF32LEKind:                                                  \
+    f<llvm::object::ELF32LE>(__VA_ARGS__);                                     \
     break;                                                                     \
-  case ELF32BEKind:                                                            \
-    f<ELF32BE>(__VA_ARGS__);                                                   \
+  case lld::elf::ELF32BEKind:                                                  \
+    f<llvm::object::ELF32BE>(__VA_ARGS__);                                     \
     break;                                                                     \
-  case ELF64LEKind:                                                            \
-    f<ELF64LE>(__VA_ARGS__);                                                   \
+  case lld::elf::ELF64LEKind:                                                  \
+    f<llvm::object::ELF64LE>(__VA_ARGS__);                                     \
     break;                                                                     \
-  case ELF64BEKind:                                                            \
-    f<ELF64BE>(__VA_ARGS__);                                                   \
+  case lld::elf::ELF64BEKind:                                                  \
+    f<llvm::object::ELF64BE>(__VA_ARGS__);                                     \
     break;                                                                     \
   default:                                                                     \
     llvm_unreachable("unknown config->ekind");                                 \

diff  --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index 30559dbe826312..5f543ffdcfaa32 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -1138,7 +1138,7 @@ void PPC64PltCallStub::writeTo(uint8_t *buf) {
 void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
   Defined *s = addSymbol(saver().save("__plt_" + destination.getName()),
                          STT_FUNC, 0, isec);
-  s->needsTocRestore = true;
+  s->setNeedsTocRestore(true);
   s->file = destination.file;
 }
 
@@ -1182,7 +1182,7 @@ void PPC64R2SaveStub::writeTo(uint8_t *buf) {
 void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
   Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()),
                          STT_FUNC, 0, isec);
-  s->needsTocRestore = true;
+  s->setNeedsTocRestore(true);
 }
 
 bool PPC64R2SaveStub::isCompatibleWith(const InputSection &isec,

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 368c9aabceae81..255f8c334b969b 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -347,8 +347,13 @@ template <class ELFT> void elf::createSyntheticSections() {
 
     if (config->emachine == EM_AARCH64 &&
         config->androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE) {
+      if (!config->relocatable && !config->shared && !needsInterpSection())
+        error("--android-memtag-mode is incompatible with fully-static "
+              "executables (-static)");
       part.memtagAndroidNote = std::make_unique<MemtagAndroidNote>();
       add(*part.memtagAndroidNote);
+      part.memtagDescriptors = std::make_unique<MemtagDescriptors>();
+      add(*part.memtagDescriptors);
     }
 
     if (config->androidPackDynRelocs)
@@ -672,7 +677,7 @@ static bool shouldKeepInSymtab(const Defined &sym) {
   return true;
 }
 
-static bool includeInSymtab(const Symbol &b) {
+bool lld::elf::includeInSymtab(const Symbol &b) {
   if (auto *d = dyn_cast<Defined>(&b)) {
     // Always include absolute symbols.
     SectionBase *sec = d->section;
@@ -1652,6 +1657,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
       changed |= part.relaDyn->updateAllocSize();
       if (part.relrDyn)
         changed |= part.relrDyn->updateAllocSize();
+      if (part.memtagDescriptors)
+        changed |= part.memtagDescriptors->updateAllocSize();
     }
 
     const Defined *changedSym = script->assignAddresses();

diff  --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h
index a302caad339f62..c69de54f76e9b4 100644
--- a/lld/ELF/Writer.h
+++ b/lld/ELF/Writer.h
@@ -46,6 +46,7 @@ struct PhdrEntry {
 };
 
 void addReservedSymbols();
+bool includeInSymtab(const Symbol &b);
 
 template <class ELFT> uint32_t calcMipsEFlags();
 

diff  --git a/lld/test/ELF/Inputs/aarch64-memtag-globals.s b/lld/test/ELF/Inputs/aarch64-memtag-globals.s
new file mode 100644
index 00000000000000..c48083f5550f87
--- /dev/null
+++ b/lld/test/ELF/Inputs/aarch64-memtag-globals.s
@@ -0,0 +1,382 @@
+#--- input_1.s
+## Generated with:
+##
+##  - clang <input_file.c> -fsanitize=memtag-globals -O2 -S -fPIC -o - \
+##          --target=aarch64-linux-android31 -fno-asynchronous-unwind-tables
+##
+## <input_file.c> contents:
+##
+##    /// Global variables defined here, of various semantics.
+##    char global[30] = {};
+##    __attribute__((no_sanitize("memtag"))) int global_untagged = 0;
+##    const int const_global = 0;
+##    static const int hidden_const_global = 0;
+##    static char hidden_global[12] = {};
+##    __attribute__((visibility("hidden"))) int hidden_attr_global = 0;
+##    __attribute__((visibility("hidden"))) const int hidden_attr_const_global = 0;
+##
+##    /// Should be untagged.
+##    __thread int tls_global;
+##    __thread static int hidden_tls_global;
+##
+##    /// Tagged, from the other file.
+##    extern int global_extern;
+##    /// Untagged, from the other file.
+##    extern __attribute__((no_sanitize("memtag"))) int global_extern_untagged;
+##    /// Tagged, but from a 
diff erent DSO (i.e. not this or the sister objfile).
+##    extern int global_extern_outside_this_dso;
+##    /// Tagged here (because it's non-const), but untagged in the definition found
+##    /// in the sister objfile as it's marked as const there.
+##    extern int global_extern_const_definition_but_nonconst_import;
+##    /// Tagged here, but untagged in the definition found in the sister objfile
+##    /// (explicitly).
+##    extern int global_extern_untagged_definition_but_tagged_import;
+##
+##    /// ABS64 relocations. Also, forces symtab entries for local and external
+##    /// globals.
+##    char *pointer_to_global = &global[0];
+##    char *pointer_inside_global = &global[17];
+##    char *pointer_to_global_end = &global[30];
+##    char *pointer_past_global_end = &global[48];
+##    int *pointer_to_global_untagged = &global_untagged;
+##    const int *pointer_to_const_global = &const_global;
+##    /// RELATIVE relocations.
+##    const int *pointer_to_hidden_const_global = &hidden_const_global;
+##    char *pointer_to_hidden_global = &hidden_global[0];
+##    const int *pointer_to_hidden_attr_global = &hidden_attr_global;
+##    const int *pointer_to_hidden_attr_const_global = &hidden_attr_const_global;
+##    /// RELATIVE relocations with special AArch64 MemtagABI semantics, with the
+##    /// offset ('12' or '16') encoded in the place.
+##    char *pointer_to_hidden_global_end = &hidden_global[12];
+##    char *pointer_past_hidden_global_end = &hidden_global[16];
+##    /// ABS64 relocations.
+##    int *pointer_to_global_extern = &global_extern;
+##    int *pointer_to_global_extern_untagged = &global_extern_untagged;
+##    int *pointer_to_global_extern_outside_this_dso = &global_extern_outside_this_dso;
+##    int *pointer_to_global_extern_const_definition_but_nonconst_import =
+##        &global_extern_const_definition_but_nonconst_import;
+##    int *pointer_to_global_extern_untagged_definition_but_tagged_import =
+##        &global_extern_untagged_definition_but_tagged_import;
+##
+##    int *get_address_to_tls_global() { return &tls_global; }
+##    int *get_address_to_hidden_tls_global() { return &hidden_tls_global; }
+
+	.text
+	.file	"a.c"
+	.globl	get_address_to_tls_global       // -- Begin function get_address_to_tls_global
+	.p2align	2
+	.type	get_address_to_tls_global, at function
+get_address_to_tls_global:              // @get_address_to_tls_global
+// %bb.0:                               // %entry
+	stp	x29, x30, [sp, #-16]!           // 16-byte Folded Spill
+	mov	x29, sp
+	adrp	x0, :tlsdesc:tls_global
+	ldr	x1, [x0, :tlsdesc_lo12:tls_global]
+	add	x0, x0, :tlsdesc_lo12:tls_global
+	.tlsdesccall tls_global
+	blr	x1
+	mrs	x8, TPIDR_EL0
+	add	x0, x8, x0
+	ldp	x29, x30, [sp], #16             // 16-byte Folded Reload
+	ret
+.Lfunc_end0:
+	.size	get_address_to_tls_global, .Lfunc_end0-get_address_to_tls_global
+                                        // -- End function
+	.globl	get_address_to_hidden_tls_global // -- Begin function get_address_to_hidden_tls_global
+	.p2align	2
+	.type	get_address_to_hidden_tls_global, at function
+get_address_to_hidden_tls_global:       // @get_address_to_hidden_tls_global
+// %bb.0:                               // %entry
+	stp	x29, x30, [sp, #-16]!           // 16-byte Folded Spill
+	mov	x29, sp
+	adrp	x0, :tlsdesc:hidden_tls_global
+	ldr	x1, [x0, :tlsdesc_lo12:hidden_tls_global]
+	add	x0, x0, :tlsdesc_lo12:hidden_tls_global
+	.tlsdesccall hidden_tls_global
+	blr	x1
+	mrs	x8, TPIDR_EL0
+	add	x0, x8, x0
+	ldp	x29, x30, [sp], #16             // 16-byte Folded Reload
+	ret
+.Lfunc_end1:
+	.size	get_address_to_hidden_tls_global, .Lfunc_end1-get_address_to_hidden_tls_global
+                                        // -- End function
+	.memtag	global                          // @global
+	.type	global, at object
+	.bss
+	.globl	global
+	.p2align	4, 0x0
+global:
+	.zero	32
+	.size	global, 32
+
+	.type	global_untagged, at object         // @global_untagged
+	.globl	global_untagged
+	.p2align	2, 0x0
+global_untagged:
+	.word	0                               // 0x0
+	.size	global_untagged, 4
+
+	.type	const_global, at object            // @const_global
+	.section	.rodata,"a", at progbits
+	.globl	const_global
+	.p2align	2, 0x0
+const_global:
+	.word	0                               // 0x0
+	.size	const_global, 4
+
+	.hidden	hidden_attr_global              // @hidden_attr_global
+	.memtag	hidden_attr_global
+	.type	hidden_attr_global, at object
+	.bss
+	.globl	hidden_attr_global
+	.p2align	4, 0x0
+hidden_attr_global:
+	.zero	16
+	.size	hidden_attr_global, 16
+
+	.hidden	hidden_attr_const_global        // @hidden_attr_const_global
+	.type	hidden_attr_const_global, at object
+	.section	.rodata,"a", at progbits
+	.globl	hidden_attr_const_global
+	.p2align	2, 0x0
+hidden_attr_const_global:
+	.word	0                               // 0x0
+	.size	hidden_attr_const_global, 4
+
+	.memtag	pointer_to_global               // @pointer_to_global
+	.type	pointer_to_global, at object
+	.data
+	.globl	pointer_to_global
+	.p2align	4, 0x0
+pointer_to_global:
+	.xword	global
+	.zero	8
+	.size	pointer_to_global, 16
+
+	.memtag	pointer_inside_global           // @pointer_inside_global
+	.type	pointer_inside_global, at object
+	.globl	pointer_inside_global
+	.p2align	4, 0x0
+pointer_inside_global:
+	.xword	global+17
+	.zero	8
+	.size	pointer_inside_global, 16
+
+	.memtag	pointer_to_global_end           // @pointer_to_global_end
+	.type	pointer_to_global_end, at object
+	.globl	pointer_to_global_end
+	.p2align	4, 0x0
+pointer_to_global_end:
+	.xword	global+30
+	.zero	8
+	.size	pointer_to_global_end, 16
+
+	.memtag	pointer_past_global_end         // @pointer_past_global_end
+	.type	pointer_past_global_end, at object
+	.globl	pointer_past_global_end
+	.p2align	4, 0x0
+pointer_past_global_end:
+	.xword	global+48
+	.zero	8
+	.size	pointer_past_global_end, 16
+
+	.memtag	pointer_to_global_untagged      // @pointer_to_global_untagged
+	.type	pointer_to_global_untagged, at object
+	.globl	pointer_to_global_untagged
+	.p2align	4, 0x0
+pointer_to_global_untagged:
+	.xword	global_untagged
+	.zero	8
+	.size	pointer_to_global_untagged, 16
+
+	.memtag	pointer_to_const_global         // @pointer_to_const_global
+	.type	pointer_to_const_global, at object
+	.globl	pointer_to_const_global
+	.p2align	4, 0x0
+pointer_to_const_global:
+	.xword	const_global
+	.zero	8
+	.size	pointer_to_const_global, 16
+
+	.type	hidden_const_global, at object     // @hidden_const_global
+	.section	.rodata,"a", at progbits
+	.p2align	2, 0x0
+hidden_const_global:
+	.word	0                               // 0x0
+	.size	hidden_const_global, 4
+
+	.memtag	pointer_to_hidden_const_global  // @pointer_to_hidden_const_global
+	.type	pointer_to_hidden_const_global, at object
+	.data
+	.globl	pointer_to_hidden_const_global
+	.p2align	4, 0x0
+pointer_to_hidden_const_global:
+	.xword	hidden_const_global
+	.zero	8
+	.size	pointer_to_hidden_const_global, 16
+
+	.memtag	hidden_global                   // @hidden_global
+	.type	hidden_global, at object
+	.local	hidden_global
+	.comm	hidden_global,16,16
+	.memtag	pointer_to_hidden_global        // @pointer_to_hidden_global
+	.type	pointer_to_hidden_global, at object
+	.globl	pointer_to_hidden_global
+	.p2align	4, 0x0
+pointer_to_hidden_global:
+	.xword	hidden_global
+	.zero	8
+	.size	pointer_to_hidden_global, 16
+
+	.memtag	pointer_to_hidden_attr_global   // @pointer_to_hidden_attr_global
+	.type	pointer_to_hidden_attr_global, at object
+	.globl	pointer_to_hidden_attr_global
+	.p2align	4, 0x0
+pointer_to_hidden_attr_global:
+	.xword	hidden_attr_global
+	.zero	8
+	.size	pointer_to_hidden_attr_global, 16
+
+	.memtag	pointer_to_hidden_attr_const_global // @pointer_to_hidden_attr_const_global
+	.type	pointer_to_hidden_attr_const_global, at object
+	.globl	pointer_to_hidden_attr_const_global
+	.p2align	4, 0x0
+pointer_to_hidden_attr_const_global:
+	.xword	hidden_attr_const_global
+	.zero	8
+	.size	pointer_to_hidden_attr_const_global, 16
+
+	.memtag	pointer_to_hidden_global_end    // @pointer_to_hidden_global_end
+	.type	pointer_to_hidden_global_end, at object
+	.globl	pointer_to_hidden_global_end
+	.p2align	4, 0x0
+pointer_to_hidden_global_end:
+	.xword	hidden_global+12
+	.zero	8
+	.size	pointer_to_hidden_global_end, 16
+
+	.memtag	pointer_past_hidden_global_end  // @pointer_past_hidden_global_end
+	.type	pointer_past_hidden_global_end, at object
+	.globl	pointer_past_hidden_global_end
+	.p2align	4, 0x0
+pointer_past_hidden_global_end:
+	.xword	hidden_global+16
+	.zero	8
+	.size	pointer_past_hidden_global_end, 16
+
+	.memtag	global_extern
+	.memtag	pointer_to_global_extern        // @pointer_to_global_extern
+	.type	pointer_to_global_extern, at object
+	.globl	pointer_to_global_extern
+	.p2align	4, 0x0
+pointer_to_global_extern:
+	.xword	global_extern
+	.zero	8
+	.size	pointer_to_global_extern, 16
+
+	.memtag	pointer_to_global_extern_untagged // @pointer_to_global_extern_untagged
+	.type	pointer_to_global_extern_untagged, at object
+	.globl	pointer_to_global_extern_untagged
+	.p2align	4, 0x0
+pointer_to_global_extern_untagged:
+	.xword	global_extern_untagged
+	.zero	8
+	.size	pointer_to_global_extern_untagged, 16
+
+	.memtag	global_extern_outside_this_dso
+	.memtag	pointer_to_global_extern_outside_this_dso // @pointer_to_global_extern_outside_this_dso
+	.type	pointer_to_global_extern_outside_this_dso, at object
+	.globl	pointer_to_global_extern_outside_this_dso
+	.p2align	4, 0x0
+pointer_to_global_extern_outside_this_dso:
+	.xword	global_extern_outside_this_dso
+	.zero	8
+	.size	pointer_to_global_extern_outside_this_dso, 16
+
+	.memtag	global_extern_const_definition_but_nonconst_import
+	.memtag	pointer_to_global_extern_const_definition_but_nonconst_import // @pointer_to_global_extern_const_definition_but_nonconst_import
+	.type	pointer_to_global_extern_const_definition_but_nonconst_import, at object
+	.globl	pointer_to_global_extern_const_definition_but_nonconst_import
+	.p2align	4, 0x0
+pointer_to_global_extern_const_definition_but_nonconst_import:
+	.xword	global_extern_const_definition_but_nonconst_import
+	.zero	8
+	.size	pointer_to_global_extern_const_definition_but_nonconst_import, 16
+
+	.memtag	global_extern_untagged_definition_but_tagged_import
+	.memtag	pointer_to_global_extern_untagged_definition_but_tagged_import // @pointer_to_global_extern_untagged_definition_but_tagged_import
+	.type	pointer_to_global_extern_untagged_definition_but_tagged_import, at object
+	.globl	pointer_to_global_extern_untagged_definition_but_tagged_import
+	.p2align	4, 0x0
+pointer_to_global_extern_untagged_definition_but_tagged_import:
+	.xword	global_extern_untagged_definition_but_tagged_import
+	.zero	8
+	.size	pointer_to_global_extern_untagged_definition_but_tagged_import, 16
+
+	.type	tls_global, at object              // @tls_global
+	.section	.tbss,"awT", at nobits
+	.globl	tls_global
+	.p2align	2, 0x0
+tls_global:
+	.word	0                               // 0x0
+	.size	tls_global, 4
+
+	.type	hidden_tls_global, at object       // @hidden_tls_global
+	.p2align	2, 0x0
+hidden_tls_global:
+	.word	0                               // 0x0
+	.size	hidden_tls_global, 4
+
+	.ident	"clang version 17.0.0 (https://github.com/llvm/llvm-project.git 6130c9df99a7a7eb9c6adc118a48f8f2acc534ab)"
+	.section	".note.GNU-stack","", at progbits
+
+#--- input_2.s
+## Generated with:
+##
+##  - clang <input_file.c> -fsanitize=memtag-globals -O2 -S -o - \
+##          --target=aarch64-linux-android31 -fno-asynchronous-unwind-tables
+##
+## <input_file.c> contents:
+##
+##     int global_extern;
+##     static int global_extern_hidden;
+##     __attribute__((no_sanitize("memtag"))) int global_extern_untagged;
+##     const int global_extern_const_definition_but_nonconst_import;
+##     __attribute__((no_sanitize(
+##         "memtag"))) int global_extern_untagged_definition_but_tagged_import;
+##
+
+	.text
+	.file	"b.c"
+	.memtag	global_extern
+	.type	global_extern, at object
+	.bss
+	.globl	global_extern
+	.p2align	4, 0x0
+global_extern:
+	.zero	16
+	.size	global_extern, 16
+
+	.type	global_extern_untagged, at object
+	.globl	global_extern_untagged
+	.p2align	2, 0x0
+global_extern_untagged:
+	.word	0
+	.size	global_extern_untagged, 4
+
+	.type	global_extern_const_definition_but_nonconst_import, at object
+	.section	.rodata,"a", at progbits
+	.globl	global_extern_const_definition_but_nonconst_import
+	.p2align	2, 0x0
+global_extern_const_definition_but_nonconst_import:
+	.word	0
+	.size	global_extern_const_definition_but_nonconst_import, 4
+
+	.type	global_extern_untagged_definition_but_tagged_import, at object
+	.bss
+	.globl	global_extern_untagged_definition_but_tagged_import
+	.p2align	2, 0x0
+global_extern_untagged_definition_but_tagged_import:
+	.word	0
+	.size	global_extern_untagged_definition_but_tagged_import, 4

diff  --git a/lld/test/ELF/aarch64-memtag-android-abi.s b/lld/test/ELF/aarch64-memtag-android-abi.s
index 0e1d5deddbb2fb..e5744483e447e1 100644
--- a/lld/test/ELF/aarch64-memtag-android-abi.s
+++ b/lld/test/ELF/aarch64-memtag-android-abi.s
@@ -56,11 +56,6 @@
 # BAD-MODE: error: unknown --android-memtag-mode value: "asymm", should be one of
 # BAD-MODE: {async, sync, none}
 
-# RUN: not ld.lld -shared --android-memtag-mode=async 2>&1 | \
-# RUN:    FileCheck %s --check-prefix=MISSING-STACK-OR-HEAP
-# MISSING-STACK-OR-HEAP: error: when using --android-memtag-mode, at least one of
-# MISSING-STACK-OR-HEAP: --android-memtag-heap or --android-memtag-stack is required
-
 .globl _start
 _start:
   ret

diff  --git a/lld/test/ELF/aarch64-memtag-globals.s b/lld/test/ELF/aarch64-memtag-globals.s
new file mode 100644
index 00000000000000..5a92cf7f5112b4
--- /dev/null
+++ b/lld/test/ELF/aarch64-memtag-globals.s
@@ -0,0 +1,184 @@
+# REQUIRES: aarch64
+
+# RUN: rm -rf %t
+
+## Ensure MTE globals doesn't work with REL (only RELA).
+# RUN: yaml2obj %s -o %t.rel.o
+# RUN: not ld.lld -shared --android-memtag-mode=sync %t.rel.o 2>&1 | FileCheck %s --check-prefix=CHECK-RELA
+# CHECK-RELA: non-RELA relocations are not allowed with memtag globals
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_AARCH64
+  SectionHeaderStringTable: .strtab
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x4
+    Content:         '00'
+  - Name:            .data
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    AddressAlign:    0x10
+    Content:         '00'
+  - Name:            .memtag.globals.static
+    Type:            SHT_AARCH64_MEMTAG_GLOBALS_STATIC
+    AddressAlign:    0x1
+  - Name:            .rel.memtag.globals.static
+    Type:            SHT_REL
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .memtag.globals.static
+    Relocations:
+      - Symbol:          four
+        Type:            R_AARCH64_NONE
+  - Type:            SectionHeaderTable
+    Sections:
+      - Name:            .strtab
+      - Name:            .text
+      - Name:            .data
+      - Name:            .memtag.globals.static
+      - Name:            .rel.memtag.globals.static
+      - Name:            .symtab
+Symbols:
+  - Name:            four
+    Type:            STT_OBJECT
+    Section:         .data
+    Binding:         STB_GLOBAL
+    Value:           0x0
+    Size:            0x10
+
+## Functional testing for MTE globals.
+# RUN: split-file %S/Inputs/aarch64-memtag-globals.s %t
+# RUN: llvm-mc --filetype=obj -triple=aarch64-none-linux-android \
+# RUN:   %t/input_1.s -o %t1.o
+# RUN: llvm-mc --filetype=obj -triple=aarch64-none-linux-android \
+# RUN:   %t/input_2.s -o %t2.o
+# RUN: ld.lld -shared --android-memtag-mode=sync %t1.o %t2.o -o %t.so
+
+## Normally relocations are printed before the symbol tables, so reorder it a
+## bit to make it easier on matching addresses of relocations up with the
+## symbols.
+# RUN: llvm-readelf %t.so -s > %t.out
+# RUN: llvm-readelf %t.so --section-headers --relocs --memtag >> %t.out
+# RUN: FileCheck %s < %t.out
+# RUN: llvm-objdump -Dz %t.so | FileCheck %s --check-prefix=CHECK-SPECIAL-RELOCS
+
+## And ensure that --apply-dynamic-relocs is banned.
+# RUN: not ld.lld --apply-dynamic-relocs -shared --android-memtag-mode=sync \
+# RUN:   %t1.o %t2.o 2>&1 | FileCheck %s --check-prefix=CHECK-DYNRELOC
+# CHECK-DYNRELOC: --apply-dynamic-relocs cannot be used with MTE globals
+
+## And ensure that fully-static executables are banned.
+# RUN: not ld.lld --static --android-memtag-mode=sync \
+# RUN:   %t1.o %t2.o 2>&1 | FileCheck %s --check-prefix=CHECK-NOSTATIC
+# CHECK-NOSTATIC: --android-memtag-mode is incompatible with fully-static executables (-static)
+
+# CHECK:     Symbol table '.dynsym' contains
+# CHECK-DAG: [[#%x,GLOBAL:]] 32 OBJECT GLOBAL DEFAULT [[#]] global{{$}}
+# CHECK-DAG: [[#%x,GLOBAL_UNTAGGED:]] 4 OBJECT GLOBAL DEFAULT [[#]] global_untagged{{$}}
+# CHECK-DAG: [[#%x,CONST_GLOBAL:]] 4 OBJECT GLOBAL DEFAULT [[#]] const_global{{$}}
+# CHECK-DAG: [[#%x,GLOBAL_EXTERN:]] 16 OBJECT GLOBAL DEFAULT [[#]] global_extern{{$}}
+# CHECK-DAG: [[#%x,GLOBAL_EXTERN_UNTAGGED:]] 4 OBJECT GLOBAL DEFAULT [[#]] global_extern_untagged{{$}}
+# CHECK-DAG: 0 NOTYPE GLOBAL DEFAULT UND global_extern_outside_this_dso{{$}}
+# CHECK-DAG: [[#%x,GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT:]] 4 OBJECT GLOBAL DEFAULT [[#]] global_extern_untagged_definition_but_tagged_import{{$}}
+# CHECK-DAG: [[#%x,GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT:]] 4 OBJECT GLOBAL DEFAULT [[#]] global_extern_const_definition_but_nonconst_import{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global{{$}}
+# CHECK-DAG: [[#%x,POINTER_INSIDE_GLOBAL:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_inside_global{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL_END:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global_end{{$}}
+# CHECK-DAG: [[#%x,POINTER_PAST_GLOBAL_END:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_past_global_end{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL_UNTAGGED:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global_untagged{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_CONST_GLOBAL:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_const_global{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_HIDDEN_CONST_GLOBAL:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_hidden_const_global{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_HIDDEN_GLOBAL:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_hidden_global{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_HIDDEN_GLOBAL_END:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_hidden_global_end{{$}}
+# CHECK-DAG: [[#%x,POINTER_PAST_HIDDEN_GLOBAL_END:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_past_hidden_global_end{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_HIDDEN_ATTR_GLOBAL:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_hidden_attr_global{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_HIDDEN_ATTR_CONST_GLOBAL:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_hidden_attr_const_global{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL_EXTERN:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global_extern{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL_EXTERN_UNTAGGED:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global_extern_untagged{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL_EXTERN_OUTSIDE_THIS_DSO:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global_extern_outside_this_dso{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global_extern_const_definition_but_nonconst_import{{$}}
+# CHECK-DAG: [[#%x,POINTER_TO_GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT:]] 16 OBJECT GLOBAL DEFAULT [[#]] pointer_to_global_extern_untagged_definition_but_tagged_import{{$}}
+
+# CHECK:     Symbol table '.symtab' contains
+# CHECK-DAG: [[#%x,HIDDEN_CONST_GLOBAL:]] 4 OBJECT LOCAL DEFAULT [[#]] hidden_const_global{{$}}
+# CHECK-DAG: [[#%x,HIDDEN_GLOBAL:]] 16 OBJECT LOCAL DEFAULT [[#]] hidden_global{{$}}
+# CHECK-DAG: [[#%x,HIDDEN_ATTR_GLOBAL:]] 16 OBJECT LOCAL HIDDEN [[#]] hidden_attr_global{{$}}
+# CHECK-DAG: [[#%x,HIDDEN_ATTR_CONST_GLOBAL:]] 4 OBJECT LOCAL HIDDEN [[#]] hidden_attr_const_global{{$}}
+
+# CHECK:     Section Headers:
+# CHECK:     .memtag.globals.dynamic AARCH64_MEMTAG_GLOBALS_DYNAMIC
+# CHECK-NOT: .memtag.globals.static
+# CHECK-NOT: AARCH64_MEMTAG_GLOBALS_STATIC
+
+# CHECK: Relocation section '.rela.dyn'
+# CHECK-DAG: [[#POINTER_TO_GLOBAL]] {{.*}} R_AARCH64_ABS64 {{.*}} global + 0
+# CHECK-DAG: [[#POINTER_INSIDE_GLOBAL]] {{.*}} R_AARCH64_ABS64 {{.*}} global + 11
+# CHECK-DAG: [[#POINTER_TO_GLOBAL_END]] {{.*}} R_AARCH64_ABS64 {{.*}} global + 1e
+# CHECK-DAG: [[#POINTER_PAST_GLOBAL_END]] {{.*}} R_AARCH64_ABS64 {{.*}} global + 30
+# CHECK-DAG: [[#POINTER_TO_GLOBAL_UNTAGGED]] {{.*}} R_AARCH64_ABS64 {{.*}} global_untagged + 0
+# CHECK-DAG: [[#POINTER_TO_CONST_GLOBAL]] {{.*}} R_AARCH64_ABS64 {{.*}} const_global + 0
+
+## RELATIVE relocations.
+# CHECK-DAG: [[#POINTER_TO_HIDDEN_CONST_GLOBAL]] {{.*}} R_AARCH64_RELATIVE {{0*}}[[#HIDDEN_CONST_GLOBAL]]
+# CHECK-DAG: [[#POINTER_TO_HIDDEN_GLOBAL]] {{.*}} R_AARCH64_RELATIVE {{0*}}[[#HIDDEN_GLOBAL]]
+
+## AArch64 MemtagABI special RELATIVE relocation semantics, where the offset is encoded in the place.
+# CHECK-DAG:                 [[#POINTER_TO_HIDDEN_GLOBAL_END]] {{.*}} R_AARCH64_RELATIVE {{0*}}[[#HIDDEN_GLOBAL + 12]]
+# CHECK-SPECIAL-RELOCS:      <pointer_to_hidden_global_end>:
+# CHECK-SPECIAL-RELOCS-NEXT:   .word 0x00000000
+# CHECK-SPECIAL-RELOCS-NEXT:   .word 0x00000000
+# CHECK-DAG:                 [[#POINTER_PAST_HIDDEN_GLOBAL_END]] {{.*}} R_AARCH64_RELATIVE {{0*}}[[#HIDDEN_GLOBAL + 16]]
+# CHECK-SPECIAL-RELOCS:      <pointer_past_hidden_global_end>:
+# CHECK-SPECIAL-RELOCS-NEXT:   .word 0xfffffff0
+# CHECK-SPECIAL-RELOCS-NEXT:   .word 0xffffffff
+
+## More ABS64 relocations.
+# CHECK-DAG: [[#POINTER_TO_GLOBAL_EXTERN]] {{[0-9a-f]+}} R_AARCH64_ABS64 {{[0-9a-f]+}} global_extern + 0
+# CHECK-DAG: [[#POINTER_TO_GLOBAL_EXTERN_UNTAGGED]] {{[0-9a-f]+}} R_AARCH64_ABS64 {{[0-9a-f]+}} global_extern_untagged + 0
+# CHECK-DAG: [[#POINTER_TO_GLOBAL_EXTERN_OUTSIDE_THIS_DSO]] {{[0-9a-f]+}} R_AARCH64_ABS64 {{[0-9a-f]+}} global_extern_outside_this_dso + 0
+# CHECK-DAG: [[#POINTER_TO_GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT]] {{[0-9a-f]+}} R_AARCH64_ABS64 {{[0-9a-f]+}} global_extern_const_definition_but_nonconst_import + 0
+# CHECK-DAG: [[#POINTER_TO_GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT]] {{[0-9a-f]+}} R_AARCH64_ABS64 {{[0-9a-f]+}} global_extern_untagged_definition_but_tagged_import + 0
+
+# CHECK:      Memtag Dynamic Entries
+# CHECK-NEXT: AARCH64_MEMTAG_MODE: Synchronous (0)
+# CHECK-NEXT: AARCH64_MEMTAG_HEAP: Disabled (0)
+# CHECK-NEXT: AARCH64_MEMTAG_STACK: Disabled (0)
+# CHECK-NEXT: AARCH64_MEMTAG_GLOBALS: 0x{{[0-9a-f]+}}
+# CHECK-NEXT: AARCH64_MEMTAG_GLOBALSSZ: 23
+
+# CHECK:      Memtag Android Note
+# CHECK-NEXT: Tagging Mode: SYNC
+# CHECK-NEXT: Heap: Disabled
+# CHECK-NEXT: Stack: Disabled
+
+## Global variable order hopefully isn't too brittle of a test here, but this allows us to make sure
+## that we have all the global variables we expect, and no more.
+# CHECK:      Memtag Global Descriptors:
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_INSIDE_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL_END]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_PAST_GLOBAL_END]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL_UNTAGGED]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_CONST_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_HIDDEN_CONST_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_HIDDEN_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_HIDDEN_ATTR_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_HIDDEN_ATTR_CONST_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_HIDDEN_GLOBAL_END]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_PAST_HIDDEN_GLOBAL_END]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL_EXTERN]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL_EXTERN_UNTAGGED]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL_EXTERN_OUTSIDE_THIS_DSO]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT]]: 0x10
+# CHECK-NEXT: 0x[[#POINTER_TO_GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT]]: 0x10
+# CHECK-NEXT: 0x[[#GLOBAL]]: 0x20
+# CHECK-NEXT: 0x[[#HIDDEN_ATTR_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#HIDDEN_GLOBAL]]: 0x10
+# CHECK-NEXT: 0x[[#GLOBAL_EXTERN]]: 0x10
+# CHECK-NOT:  0x


        


More information about the llvm-commits mailing list