[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
This commit is contained in:
Mitch Phillips
2023-07-31 17:07:26 +02:00
parent 8a677a7ff0
commit ca35a19aca
15 changed files with 870 additions and 33 deletions

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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,);

View File

@@ -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 differently 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();

View File

@@ -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 differently than the regular variables.
bool needsGot(RelExpr expr);
} // namespace lld::elf
#endif

View File

@@ -254,8 +254,8 @@ protected:
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 @@ public:
// 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.
//

View File

@@ -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;

View File

@@ -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 @@ public:
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;

View File

@@ -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"); \

View File

@@ -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,

View File

@@ -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();

View File

@@ -46,6 +46,7 @@ struct PhdrEntry {
};
void addReservedSymbols();
bool includeInSymtab(const Symbol &b);
template <class ELFT> uint32_t calcMipsEFlags();

View File

@@ -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 different 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,@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,@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,@object
.bss
.globl global
.p2align 4, 0x0
global:
.zero 32
.size global, 32
.type global_untagged,@object // @global_untagged
.globl global_untagged
.p2align 2, 0x0
global_untagged:
.word 0 // 0x0
.size global_untagged, 4
.type const_global,@object // @const_global
.section .rodata,"a",@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,@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,@object
.section .rodata,"a",@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,@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,@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,@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,@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,@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,@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,@object // @hidden_const_global
.section .rodata,"a",@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,@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,@object
.local hidden_global
.comm hidden_global,16,16
.memtag pointer_to_hidden_global // @pointer_to_hidden_global
.type pointer_to_hidden_global,@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,@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,@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,@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,@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,@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,@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,@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,@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,@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,@object // @tls_global
.section .tbss,"awT",@nobits
.globl tls_global
.p2align 2, 0x0
tls_global:
.word 0 // 0x0
.size tls_global, 4
.type hidden_tls_global,@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","",@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,@object
.bss
.globl global_extern
.p2align 4, 0x0
global_extern:
.zero 16
.size global_extern, 16
.type global_extern_untagged,@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,@object
.section .rodata,"a",@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,@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

View File

@@ -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

View File

@@ -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