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 @@ -376,7 +376,19 @@ break; case R_AARCH64_ABS64: case R_AARCH64_PREL64: - write64(loc, val); + // 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 >= rel.sym->getSize())) { + write64(loc, -1 * rel.addend); + } else { + write64(loc, val); + } break; case R_AARCH64_ADD_ABS_LO12_NC: or32AArch64Imm(loc, val); @@ -653,6 +665,11 @@ return false; Symbol &sym = *adrpRel.sym; + // 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. + if (sym.isTagged()) + return false; + // Check if the address difference is within 1MiB range. int64_t val = sym.getVA() - (secAddr + addRel.offset); if (val < -1024 * 1024 || val >= 1024 * 1024) @@ -724,6 +741,11 @@ if (val != llvm::SignExtend64(val, 33)) return false; + // 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. + if (sym.isTagged()) + return false; + Relocation adrpSymRel = {R_AARCH64_PAGE_PC, R_AARCH64_ADR_PREL_PG_HI21, adrpRel.offset, /*addend=*/0, &sym}; Relocation addRel = {R_ABS, R_AARCH64_ADD_ABS_LO12_NC, ldrRel.offset, 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 @@ -350,7 +350,7 @@ if (config->fixCortexA8 && !config->isLE) error("--fix-cortex-a8 is not supported on big endian targets"); - + if (config->tocOptimize && config->emachine != EM_PPC64) error("--toc-optimize is only supported on PowerPC64 targets"); @@ -754,13 +754,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") @@ -2553,6 +2546,26 @@ } } +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); + referenceCount[&sym]++; + } + }; + + const RelsOrRelas rels = sec->relsOrRelas(); + assert(!rels.areRelocsRel() && + "non-RELA relocations are not allowed with memtag globals"); + f(rels.relas); +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. void LinkerDriver::link(opt::InputArgList &args) { @@ -2878,6 +2891,73 @@ // 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 == nullptr) continue; + if (section->type != SHT_AARCH64_MEMTAG_GLOBALS_STATIC) 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) { + // TODO(hctim): Add bitcode (LTO) support. + if (file->kind() != InputFile::BinaryKind && + file->kind() != InputFile::ObjKind) + continue; + + for (Symbol *symbol : file->getSymbols()) { + if (symbol == nullptr) 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.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 @@ -854,6 +855,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 +1645,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}); } @@ -1220,6 +1224,27 @@ 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 addSymbol(const Symbol *sym) { + if (sym && sym->isDefined()) + symbols.push_back(sym); + } + + void writeTo(uint8_t *buf) override; + size_t getSize() const override; + bool isNeeded() const override { + return !config->isStatic && !symbols.empty(); + } + +private: + std::vector symbols; +}; + InputSection *createInterpSection(); MergeInputSection *createCommentSection(); template void splitSections(); @@ -1253,6 +1278,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 @@ -1446,6 +1446,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()); + } } } @@ -3889,6 +3893,81 @@ 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 computeULEB128(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(std::vector *symbols, + uint8_t *buf = nullptr) { + // Unfortunately, the `symbols` vector can't be stored sorted, as insertion + // happens before the VA is assigned. + std::sort(symbols->begin(), symbols->end(), + [](const Symbol *s1, const Symbol *s2) { + return s1->getVA() < s2->getVA(); + }); + 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 += computeULEB128(stepToEncode | sizeToEncode, buf, sectionSize); + } else { + sectionSize += computeULEB128(stepToEncode, buf, sectionSize); + sectionSize += computeULEB128(sizeToEncode - 1, buf, sectionSize); + } + lastGlobalEnd = vAddr + size; + } + + return sectionSize; +} + +// Some callers can't allow in-place modification of *symbols. In these cases, +// make a copy, to allow those who are okay with in-place to avoid it. +static size_t +createMemtagDescriptors(const std::vector *symbols, + uint8_t *buf = nullptr) { + std::vector symbolsCopy(symbols->begin(), symbols->end()); + // Non-const argument variant. + return createMemtagDescriptors(&symbolsCopy); +} + +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 @@ -1090,7 +1090,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; } @@ -1134,7 +1134,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) @@ -664,7 +666,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; diff --git a/lld/test/ELF/Inputs/aarch64-memtag-globals-1.s b/lld/test/ELF/Inputs/aarch64-memtag-globals-1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/aarch64-memtag-globals-1.s @@ -0,0 +1,228 @@ +## Generated with: +## +## - clang -fsanitize=memtag-globals -O2 -S -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] = {}; +## +## /// 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]; +## /// 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; + + .text + .file "a.c" + .memtag global + .type global,@object + .bss + .globl global + .p2align 4, 0x0 +global: + .zero 32 + .size global, 32 + + .type global_untagged,@object + .globl global_untagged + .p2align 2, 0x0 +global_untagged: + .word 0 + .size global_untagged, 4 + + .type const_global,@object + .section .rodata,"a",@progbits + .globl const_global + .p2align 2, 0x0 +const_global: + .word 0 + .size const_global, 4 + + .memtag 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 + .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 + .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 + .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 + .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 + .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 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +hidden_const_global: + .word 0 + .size hidden_const_global, 4 + + .memtag 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 + .type hidden_global,@object + .local hidden_global + .comm hidden_global,16,16 + .memtag 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_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 + .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 + .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 + .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 + .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 + .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 + .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 diff --git a/lld/test/ELF/Inputs/aarch64-memtag-globals-2.s b/lld/test/ELF/Inputs/aarch64-memtag-globals-2.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/aarch64-memtag-globals-2.s @@ -0,0 +1,49 @@ +## 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,114 @@ +# REQUIRES: aarch64 + +# RUN: llvm-mc --filetype=obj -triple=aarch64-none-linux-android \ +# RUN: %S/Inputs/aarch64-memtag-globals-1.s -o %t1.o +# RUN: llvm-mc --filetype=obj -triple=aarch64-none-linux-android \ +# RUN: %S/Inputs/aarch64-memtag-globals-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 relocatiosn up with the +## symbols. +# RUN: llvm-readelf %t.so -s > %t.out +# RUN: llvm-readelf %t.so --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_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: 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: 20 + +# 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: 0x[[POINTER_INSIDE_GLOBAL]]: 0x10 +# CHECK: 0x[[POINTER_TO_GLOBAL_END]]: 0x10 +# CHECK: 0x[[POINTER_PAST_GLOBAL_END]]: 0x10 +# CHECK: 0x[[POINTER_TO_GLOBAL_UNTAGGED]]: 0x10 +# CHECK: 0x[[POINTER_TO_CONST_GLOBAL]]: 0x10 +# CHECK: 0x[[POINTER_TO_HIDDEN_CONST_GLOBAL]]: 0x10 +# CHECK: 0x[[POINTER_TO_HIDDEN_GLOBAL]]: 0x10 +# CHECK: 0x[[POINTER_TO_HIDDEN_GLOBAL_END]]: 0x10 +# CHECK: 0x[[POINTER_PAST_HIDDEN_GLOBAL_END]]: 0x10 +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN]]: 0x10 +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN_OUTSIDE_THIS_DSO]]: 0x10 +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN_CONST_DEFINITION_BUT_NONCONST_IMPORT]]: 0x10 +# CHECK: 0x[[POINTER_TO_GLOBAL_EXTERN_UNTAGGED_DEFINITION_BUT_TAGGED_IMPORT]]: 0x10 +# CHECK: 0x[[GLOBAL]]: 0x20 +# CHECK: 0x[[#HIDDEN_GLOBAL]]: 0x10 +# CHECK: 0x[[GLOBAL_EXTERN]]: 0x10 +# CHECK-NOT: 0x