diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -377,6 +377,20 @@ 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(rel.sym->getSize()))) + write64(loc, -1 * rel.addend); + else + write64(loc, val); + break; case R_AARCH64_PREL64: write64(loc, val); break; @@ -745,6 +759,12 @@ 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 && 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(&sec)) @@ -756,6 +776,12 @@ 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 && diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -1556,7 +1556,7 @@ 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 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -776,13 +776,6 @@ 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") @@ -2585,6 +2578,40 @@ } } +template +static void +addTaggedSymbolReferences(InputSectionBase *sec, + DenseMap &referenceCount) { + assert(config->emachine == EM_AARCH64); + assert(sec && sec->type == SHT_AARCH64_MEMTAG_GLOBALS_STATIC); + + auto f = [&](auto relArray) { + for (const typename ELFT::Rela &rel : relArray) { + Symbol &sym = sec->getFile()->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 blocks. + 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]++; + } + }; + + const RelsOrRelas rels = sec->relsOrRelas(); + if (rels.areRelocsRel()) + error("non-RELA relocations are not allowed with memtag globals"); + f(rels.relas); + sec->markDead(); +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. void LinkerDriver::link(opt::InputArgList &args) { @@ -2915,6 +2942,77 @@ // partition. copySectionsIntoPartitions(); + // 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. + if (config->emachine == EM_AARCH64 && + config->androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE) { + llvm::TimeTraceScope timeScope("Process memory tagged symbols"); + + // First, collect all symbols that are marked as tagged, and count how many + // times they're marked as tagged. + DenseMap taggedSymbolReferenceCount; + for (InputFile* file : files) { + // TODO(hctim): Add bitcode (LTO) support. + if (file->kind() != InputFile::BinaryKind && + 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 : ctx.objectFiles) { + // TODO(hctim): Add bitcode (LTO) support. + 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--; + } + } + + // 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); + } + } + // 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.h b/lld/ELF/Relocations.h --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -211,6 +211,11 @@ } 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 diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- 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 @@ -199,10 +200,7 @@ 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(expr); @@ -854,6 +852,17 @@ RelType type) { Partition &part = isec.getPartition(); + if (sym.isTagged()) { + std::lock_guard lock(relocMutex); + part.relaDyn->addRelativeReloc(target->relativeRel, isec, offsetInSec, sym, + addend, type, expr); + // For tagged symbols with addends, store the relative offset to where the + // tag should be derived from. + if (addend < 0 || static_cast(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. @@ -1633,6 +1642,10 @@ 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/Symbols.h b/lld/ELF/Symbols.h --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -254,8 +254,8 @@ 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 @@ // 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.h b/lld/ELF/SyntheticSections.h --- 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" @@ -524,8 +526,10 @@ uint64_t offsetInSec, Symbol &sym, int64_t addend, RelExpr expr, RelType addendRelType) { // Write the addends to the relocated address if required. We skip - // it if the written value would be zero. - if (config->writeAddends && (expr != R_ADDEND || addend != 0)) + // it if the written value would be zero. For tagged globals, we use the + // relocated place for a special addend, and so don't replace it here. + if (config->writeAddends && (expr != R_ADDEND || addend != 0) && + !sym.isTagged()) sec.addReloc({expr, addendRelType, offsetInSec, addend, &sym}); addReloc({dynType, &sec, offsetInSec, kind, sym, addend, expr}); } @@ -1245,6 +1249,33 @@ size_t getSize() const override; }; +class MemtagDescriptors : 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) { + assert(sym && sym->isDefined()); + symbols.push_back(sym); + } + + bool isNeeded() const override { + return !config->isStatic && !symbols.empty(); + } + +private: + std::vector symbols; +}; + InputSection *createInterpSection(); MergeInputSection *createCommentSection(); template void splitSections(); @@ -1278,6 +1309,7 @@ std::unique_ptr hashTab; std::unique_ptr memtagAndroidNote; std::unique_ptr packageMetadataNote; + std::unique_ptr memtagDescriptors; std::unique_ptr relaDyn; std::unique_ptr relrDyn; std::unique_ptr verDef; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -1453,6 +1453,10 @@ 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 && mainPart->memtagDescriptors->getSize()) { + addInSec(DT_AARCH64_MEMTAG_GLOBALS, *mainPart->memtagDescriptors); + addInt(DT_AARCH64_MEMTAG_GLOBALSSZ, mainPart->memtagDescriptors->getSize()); + } } } @@ -3900,6 +3904,74 @@ 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 std::vector &symbols, + uint8_t *buf = nullptr) { + size_t sectionSize = 0; + uint64_t lastGlobalEnd = 0; + + for (const auto &sym : symbols) { + if (!includeInSymtab(*sym)) + continue; + const uint64_t vAddr = sym->getVA(); + const uint64_t size = sym->getSize(); + + if (vAddr <= kMemtagGranuleSize && buf != nullptr) + error("address of the tagged symbol \"" + sym->getName() + + "\" falls in the ELF header. This is indicative of a " + "compiler/linker bug."); + if (vAddr % kMemtagGranuleSize != 0) + error("address of the tagged symbol \"" + sym->getName() + "\" at 0x" + + Twine::utohexstr(vAddr) + "\" is not granule (16-byte) aligned"); + if (size == 0) + error("size of the tagged symbol \"" + sym->getName() + "\" at 0x" + + Twine::utohexstr(vAddr) + "\" is not allowed to be zero"); + if (size % kMemtagGranuleSize != 0) + error("size of the tagged symbol \"" + sym->getName() + "\" at 0x" + + Twine::utohexstr(vAddr) + "\" (size 0x" + Twine::utohexstr(size) + + ") is not granule (16-byte) aligned"); + + const uint64_t sizeToEncode = size / kMemtagGranuleSize; + const uint64_t stepToEncode = ((vAddr - 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 = vAddr + size; + } + + return sectionSize; +} + +bool MemtagDescriptors::updateAllocSize() { + size_t oldSize = getSize(); + std::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 elf::partitions; diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -1138,7 +1138,7 @@ 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::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.h b/lld/ELF/Writer.h --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -55,6 +55,7 @@ bool isMipsN32Abi(const InputFile *f); bool isMicroMips(); bool isMipsR6(); +bool includeInSymtab(const Symbol &b); } // namespace lld::elf #endif diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -349,6 +349,8 @@ config->androidMemtagMode != ELF::NT_MEMTAG_LEVEL_NONE) { part.memtagAndroidNote = std::make_unique(); add(*part.memtagAndroidNote); + part.memtagDescriptors = std::make_unique(); + add(*part.memtagDescriptors); } if (config->androidPackDynRelocs) @@ -672,7 +674,7 @@ return true; } -static bool includeInSymtab(const Symbol &b) { +bool lld::elf::includeInSymtab(const Symbol &b) { if (auto *d = dyn_cast(&b)) { // Always include absolute symbols. SectionBase *sec = d->section; @@ -1652,6 +1654,8 @@ 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/test/ELF/Inputs/aarch64-memtag-globals.s b/lld/test/ELF/Inputs/aarch64-memtag-globals.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/aarch64-memtag-globals.s @@ -0,0 +1,383 @@ +#--- input_1.s +## Generated with: +## +## - clang -fsanitize=memtag-globals -O2 -S -fPIC -o - \ +## --target=aarch64-linux-android31 -fno-asynchronous-unwind-tables +## +## 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 -fsanitize=memtag-globals -O2 -S -o - \ +## --target=aarch64-linux-android31 -fno-asynchronous-unwind-tables +## +## 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 + diff --git a/lld/test/ELF/aarch64-memtag-android-abi.s b/lld/test/ELF/aarch64-memtag-android-abi.s --- 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 --- /dev/null +++ b/lld/test/ELF/aarch64-memtag-globals.s @@ -0,0 +1,148 @@ +# REQUIRES: aarch64 + +# 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: cat %t.out | FileCheck %s +# RUN: llvm-objdump -Dz %t.so | FileCheck %s --check-prefix=CHECK-SPECIAL-RELOCS + +# CHECK: Symbol table '.dynsym' contains +# CHECK-DAG: {{0*}}[[GLOBAL:[0-9a-f]+]] 32 OBJECT GLOBAL DEFAULT {{.*}} global{{$}} +# CHECK-DAG: {{0*}}[[GLOBAL_UNTAGGED:[0-9a-f]+]] 4 OBJECT GLOBAL DEFAULT {{.*}} global_untagged{{$}} +# CHECK-DAG: {{0*}}[[CONST_GLOBAL:[0-9a-f]+]] 4 OBJECT GLOBAL DEFAULT {{.*}} const_global{{$}} +# CHECK-DAG: {{0*}}[[GLOBAL_EXTERN:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} global_extern{{$}} +# CHECK-DAG: {{0*}}[[GLOBAL_EXTERN_UNTAGGED:[0-9a-f]+]] 4 OBJECT GLOBAL DEFAULT {{.*}} global_extern_untagged{{$}} +# CHECK-DAG: 0 NOTYPE GLOBAL DEFAULT {{.*}} global_extern_outside_this_dso{{$}} +# CHECK-DAG: {{0*}}[[GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT:[0-9a-f]+]] 4 OBJECT GLOBAL DEFAULT {{.*}} global_extern_untagged_definition_but_tagged_import{{$}} +# CHECK-DAG: {{0*}}[[GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT:[0-9a-f]+]] 4 OBJECT GLOBAL DEFAULT {{.*}} global_extern_const_definition_but_nonconst_import{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global{{$}} +# CHECK-DAG: {{0*}}[[POINTER_INSIDE_GLOBAL:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_inside_global{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL_END:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global_end{{$}} +# CHECK-DAG: {{0*}}[[POINTER_PAST_GLOBAL_END:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_past_global_end{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL_UNTAGGED:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global_untagged{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_CONST_GLOBAL:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_const_global{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_HIDDEN_CONST_GLOBAL:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_hidden_const_global{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_HIDDEN_GLOBAL:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_hidden_global{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_HIDDEN_GLOBAL_END:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_hidden_global_end{{$}} +# CHECK-DAG: {{0*}}[[POINTER_PAST_HIDDEN_GLOBAL_END:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_past_hidden_global_end{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_HIDDEN_ATTR_GLOBAL:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_hidden_attr_global{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_HIDDEN_ATTR_CONST_GLOBAL:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_hidden_attr_const_global{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL_EXTERN:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global_extern{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL_EXTERN_UNTAGGED:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global_extern_untagged{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL_EXTERN_OUTSIDE_THIS_DSO:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global_extern_outside_this_dso{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global_extern_const_definition_but_nonconst_import{{$}} +# CHECK-DAG: {{0*}}[[POINTER_TO_GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT:[0-9a-f]+]] 16 OBJECT GLOBAL DEFAULT {{.*}} pointer_to_global_extern_untagged_definition_but_tagged_import{{$}} + +# CHECK: Symbol table '.symtab' contains +# CHECK-DAG: {{0*}}[[HIDDEN_CONST_GLOBAL:[0-9a-f]+]] 4 OBJECT LOCAL DEFAULT {{.*}} hidden_const_global{{$}} +# CHECK-DAG: {{0*}}[[#%x,HIDDEN_GLOBAL:]] 16 OBJECT LOCAL DEFAULT {{.*}} hidden_global{{$}} +# CHECK-DAG: {{0*}}[[HIDDEN_ATTR_GLOBAL:[0-9a-f]+]] 16 OBJECT LOCAL HIDDEN {{.*}} hidden_attr_global{{$}} +# CHECK-DAG: {{0*}}[[HIDDEN_ATTR_CONST_GLOBAL:[0-9a-f]+]] 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: : +# 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: : +# CHECK-SPECIAL-RELOCS-NEXT: .word 0xfffffff0 +# CHECK-SPECIAL-RELOCS-NEXT: .word 0xffffffff + +## More ABS64 relocations. +# CHECK-DAG: [[POINTER_TO_GLOBAL_EXTERN]] {{.*}} R_AARCH64_ABS64 {{.*}} global_extern + 0 +# CHECK-DAG: [[POINTER_TO_GLOBAL_EXTERN_UNTAGGED]] {{.*}} R_AARCH64_ABS64 {{.*}} global_extern_untagged + 0 +# CHECK-DAG: [[POINTER_TO_GLOBAL_EXTERN_OUTSIDE_THIS_DSO]] {{.*}} R_AARCH64_ABS64 {{.*}} global_extern_outside_this_dso + 0 +# CHECK-DAG: [[POINTER_TO_GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT]] {{.*}} R_AARCH64_ABS64 {{.*}} global_extern_const_definition_but_nonconst_import + 0 +# CHECK-DAG: [[POINTER_TO_GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT]] {{.*}} R_AARCH64_ABS64 {{.*}} global_extern_untagged_definition_but_tagged_import + 0 + +# CHECK: Memtag Dynamic Entries +# CHECK: AARCH64_MEMTAG_MODE: Synchronous (0) +# CHECK: AARCH64_MEMTAG_HEAP: Disabled (0) +# CHECK: AARCH64_MEMTAG_STACK: Disabled (0) +# CHECK: AARCH64_MEMTAG_GLOBALS: {{.*[1-9a-f]+}} +# CHECK: AARCH64_MEMTAG_GLOBALSSZ: 23 + +# CHECK: Memtag Android Note +# CHECK: Tagging Mode: SYNC +# CHECK: Heap: Disabled +# CHECK: Stack: Disabled + +# CHECK: Memtag Global Descriptors: +# CHECK-NOT: 0x[[GLOBAL_UNTAGGED]]: +# CHECK-NOT: 0x[[CONST_GLOBAL]]: +# CHECK-NOT: 0x[[HIDDEN_CONST_GLOBAL]]: +# CHECK-NOT: 0x[[GLOBAL_EXTERN_UNTAGGED]]: +# CHECK-NOT: 0x[[GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT]]: +# CHECK-NOT: 0x[[GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT]]: + +## 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: 0x[[POINTER_TO_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_INSIDE_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_GLOBAL_END]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_PAST_GLOBAL_END]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_GLOBAL_UNTAGGED]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_CONST_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_HIDDEN_CONST_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_HIDDEN_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_HIDDEN_ATTR_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_HIDDEN_ATTR_CONST_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_HIDDEN_GLOBAL_END]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_PAST_HIDDEN_GLOBAL_END]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN_UNTAGGED]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN_OUTSIDE_THIS_DSO]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[GLOBAL]]: 0x20 +# CHECK-NOT: 0x +# CHECK: 0x[[HIDDEN_ATTR_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[#HIDDEN_GLOBAL]]: 0x10 +# CHECK-NOT: 0x +# CHECK: 0x[[GLOBAL_EXTERN]]: 0x10 +# CHECK-NOT: 0x