diff --git a/clang/test/Driver/memtag-stack.c b/clang/test/Driver/memtag-stack.c --- a/clang/test/Driver/memtag-stack.c +++ b/clang/test/Driver/memtag-stack.c @@ -1,7 +1,7 @@ -// RUN: %clang -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SAFETY -// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O2 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O3 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SAFETY +// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -O2 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -O3 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY // REQUIRES: aarch64-registered-target diff --git a/clang/test/Driver/memtag_lto.c b/clang/test/Driver/memtag-stack_lto.c rename from clang/test/Driver/memtag_lto.c rename to clang/test/Driver/memtag-stack_lto.c --- a/clang/test/Driver/memtag_lto.c +++ b/clang/test/Driver/memtag-stack_lto.c @@ -33,14 +33,14 @@ // RUN: rm -f %t* // -O0: both are unsafe. -// RUN: %clang -O0 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print %s -S -o - 2>&1 | FileCheck %s +// RUN: %clang -O0 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -mllvm -stack-safety-print %s -S -o - 2>&1 | FileCheck %s // No LTO: just one is safe. -// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print %s -S -o /dev/null 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE +// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -mllvm -stack-safety-print %s -S -o /dev/null 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE // Full LTO: both are safe. -// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -Xclang -opaque-pointers -flto=full -o %t.ltonewpm1.bc -// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -Xclang -opaque-pointers -flto=full -o %t.ltonewpm2.bc +// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -c %s -Xclang -opaque-pointers -flto=full -o %t.ltonewpm1.bc +// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -c -DBUILD2 %s -Xclang -opaque-pointers -flto=full -o %t.ltonewpm2.bc // RUN: llvm-lto2 run -lto-opaque-pointers -o %t.ltonewpm %t.ltonewpm1.bc %t.ltonewpm2.bc -save-temps -stack-safety-print -thinlto-threads 1 -O1 \ // RUN: -r %t.ltonewpm1.bc,fn,plx \ // RUN: -r %t.ltonewpm1.bc,use,lx \ @@ -50,8 +50,8 @@ // RUN: -r %t.ltonewpm2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XSAFE,YSAFE // Thin LTO: both are safe. -// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -Xclang -opaque-pointers -flto=thin -o %t.thinltonewpm1.bc -// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -Xclang -opaque-pointers -flto=thin -o %t.thinltonewpm2.bc +// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -c %s -Xclang -opaque-pointers -flto=thin -o %t.thinltonewpm1.bc +// RUN: %clang -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag-stack -c -DBUILD2 %s -Xclang -opaque-pointers -flto=thin -o %t.thinltonewpm2.bc // RUN: llvm-lto2 run -lto-opaque-pointers -o %t.thinltonewpm %t.thinltonewpm1.bc %t.thinltonewpm2.bc -save-temps -stack-safety-print -thinlto-threads 1 -O1 \ // RUN: -r %t.thinltonewpm1.bc,fn,plx \ // RUN: -r %t.thinltonewpm1.bc,use,lx \ diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1035,6 +1035,10 @@ SHT_ARM_ATTRIBUTES = 0x70000003U, SHT_ARM_DEBUGOVERLAY = 0x70000004U, SHT_ARM_OVERLAYSECTION = 0x70000005U, + // Special aarch64-specific sections for MTE support, as described in: + // https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#7section-types + SHT_AARCH64_MEMTAG_GLOBALS_STATIC = 0x70000007U, + SHT_AARCH64_MEMTAG_GLOBALS_DYNAMIC = 0x70000008U, SHT_HEX_ORDERED = 0x70000000, // Link editor is to sort the entries in // this section based on their sizes SHT_X86_64_UNWIND = 0x70000001, // Unwind information diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -442,6 +442,8 @@ /// protected visibility. Defaults to MCSA_Protected MCSymbolAttr ProtectedVisibilityAttr = MCSA_Protected; + MCSymbolAttr MemtagAttr = MCSA_Memtag; + //===--- Dwarf Emission Directives -----------------------------------===// /// True if target supports emission of debugging information. Defaults to @@ -772,6 +774,8 @@ return ProtectedVisibilityAttr; } + MCSymbolAttr getMemtagAttr() const { return MemtagAttr; } + bool doesSupportDebugInformation() const { return SupportsDebugInformation; } ExceptionHandling getExceptionHandlingType() const { return ExceptionsType; } diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h --- a/llvm/include/llvm/MC/MCDirectives.h +++ b/llvm/include/llvm/MC/MCDirectives.h @@ -45,7 +45,8 @@ MCSA_Weak, ///< .weak MCSA_WeakDefinition, ///< .weak_definition (MachO) MCSA_WeakReference, ///< .weak_reference (MachO) - MCSA_WeakDefAutoPrivate ///< .weak_def_can_be_hidden (MachO) + MCSA_WeakDefAutoPrivate, ///< .weak_def_can_be_hidden (MachO) + MCSA_Memtag, ///< .memtag (ELF) }; enum MCAssemblerFlag { diff --git a/llvm/include/llvm/MC/MCELFObjectWriter.h b/llvm/include/llvm/MC/MCELFObjectWriter.h --- a/llvm/include/llvm/MC/MCELFObjectWriter.h +++ b/llvm/include/llvm/MC/MCELFObjectWriter.h @@ -139,6 +139,14 @@ unsigned setRSsym(unsigned Value, unsigned Type) const { return (Type & R_SSYM_MASK) | ((Value & 0xff) << R_SSYM_SHIFT); } + + // On AArch64, return a new section to be added to the ELF object that + // contains relocations used to describe every symbol that should have memory + // tags applied. Returns nullptr if no such section is necessary (i.e. there's + // no tagged globals). + virtual MCSectionELF *getMemtagRelocsSection(MCContext &Ctx) const { + return nullptr; + } }; /// Construct a new ELF writer instance. diff --git a/llvm/include/llvm/MC/MCSymbolELF.h b/llvm/include/llvm/MC/MCSymbolELF.h --- a/llvm/include/llvm/MC/MCSymbolELF.h +++ b/llvm/include/llvm/MC/MCSymbolELF.h @@ -43,6 +43,9 @@ void setIsSignature() const; bool isSignature() const; + void setMemtag(bool Tagged); + bool isMemtag() const; + static bool classof(const MCSymbol *S) { return S->isELF(); } private: diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -712,6 +712,16 @@ // GV's or GVSym's attributes will be used for the EmittedSym. emitVisibility(EmittedSym, GV->getVisibility(), !GV->isDeclaration()); + if (GV->isTagged()) { + Triple T = TM.getTargetTriple(); + + if (T.getArch() != Triple::aarch64 || !T.isAndroid()) + OutContext.reportError(SMLoc(), + "Tagged symbols (-fsanitize=memtag-globals) are " + "only supported on aarch64 + Android."); + OutStreamer->emitSymbolAttribute(EmittedSym, MAI->getMemtagAttr()); + } + if (!GV->hasInitializer()) // External globals require no extra code. return; diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -192,6 +192,8 @@ MCSectionELF *createRelocationSection(MCContext &Ctx, const MCSectionELF &Sec); + void createMemtagRelocs(MCAssembler &Asm); + void writeSectionHeader(const MCAsmLayout &Layout, const SectionIndexMapTy &SectionIndexMap, const SectionOffsetsTy &SectionOffsets); @@ -609,6 +611,23 @@ return true; } +void ELFWriter::createMemtagRelocs(MCAssembler &Asm) { + MCSectionELF *MemtagRelocs = nullptr; + for (const MCSymbol &Sym : Asm.symbols()) { + const auto &SymE = cast(Sym); + if (!SymE.isMemtag()) + continue; + if (MemtagRelocs == nullptr) { + MemtagRelocs = OWriter.TargetObjectWriter->getMemtagRelocsSection(Asm.getContext()); + if (MemtagRelocs == nullptr) + report_fatal_error("Tagged globals are not available on this architecture."); + Asm.registerSection(*MemtagRelocs); + } + ELFRelocationEntry Rec(0, &SymE, ELF::R_AARCH64_NONE, 0, nullptr, 0); + OWriter.Relocations[MemtagRelocs].push_back(Rec); + } +} + void ELFWriter::computeSymbolTable( MCAssembler &Asm, const MCAsmLayout &Layout, const SectionIndexMapTy &SectionIndexMap, const RevGroupMapTy &RevGroupMap, @@ -1068,6 +1087,8 @@ Ctx.getELFSection(".strtab", ELF::SHT_STRTAB, 0); StringTableIndex = addToSectionTable(StrtabSection); + createMemtagRelocs(Asm); + RevGroupMapTy RevGroupMap; SectionIndexMapTy SectionIndexMap; @@ -1317,6 +1338,15 @@ if (Sym->isUndefined()) return true; + // For memory-tagged symbols, ensure that the relocation uses the symbol. For + // tagged symbols, we emit an empty relocation (R_AARCH64_NONE) in a special + // section (SHT_AARCH64_MEMTAG_GLOBALS_STATIC) to indicate to the linker that + // this global needs to be tagged. In addition, the linker needs to know + // whether to emit a special addend when relocating `end` symbols, and this + // can only be determined by the attributes of the symbol itself. + if (Sym->isMemtag()) + return true; + unsigned Binding = Sym->getBinding(); switch(Binding) { default: diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -769,6 +769,9 @@ case MCSA_Exported: // Non-AIX assemblers currently do not support exported visibility. return false; + case MCSA_Memtag: + OS << "\t.memtag\t"; + break; } Symbol->print(OS, MAI); diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -287,6 +287,10 @@ Symbol->setVisibility(ELF::STV_PROTECTED); break; + case MCSA_Memtag: + Symbol->setMemtag(true); + break; + case MCSA_Hidden: Symbol->setVisibility(ELF::STV_HIDDEN); break; diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -358,6 +358,7 @@ case MCSA_Local: case MCSA_LGlobal: case MCSA_Exported: + case MCSA_Memtag: return false; case MCSA_Global: diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -539,6 +539,7 @@ DK_LTO_DISCARD, DK_LTO_SET_CONDITIONAL, DK_CFI_MTE_TAGGED_FRAME, + DK_MEMTAG, DK_END }; @@ -2298,6 +2299,8 @@ return parseDirectivePseudoProbe(); case DK_LTO_DISCARD: return parseDirectiveLTODiscard(); + case DK_MEMTAG: + return parseDirectiveSymbolAttribute(MCSA_Memtag); } return Error(IDLoc, "unknown directive"); @@ -4984,8 +4987,9 @@ MCSymbol *Sym = getContext().getOrCreateSymbol(Name); - // Assembler local symbols don't make any sense here. Complain loudly. - if (Sym->isTemporary()) + // Assembler local symbols don't make any sense here, except for directives + // that the symbol should be tagged. + if (Sym->isTemporary() && Attr != MCSA_Memtag) return Error(Loc, "non-local symbol required"); if (!getStreamer().emitSymbolAttribute(Sym, Attr)) @@ -5598,6 +5602,7 @@ DirectiveKindMap[".pseudoprobe"] = DK_PSEUDO_PROBE; DirectiveKindMap[".lto_discard"] = DK_LTO_DISCARD; DirectiveKindMap[".lto_set_conditional"] = DK_LTO_SET_CONDITIONAL; + DirectiveKindMap[".memtag"] = DK_MEMTAG; } MCAsmMacro *AsmParser::parseMacroLikeBody(SMLoc DirectiveLoc) { diff --git a/llvm/lib/MC/MCSymbolELF.cpp b/llvm/lib/MC/MCSymbolELF.cpp --- a/llvm/lib/MC/MCSymbolELF.cpp +++ b/llvm/lib/MC/MCSymbolELF.cpp @@ -33,7 +33,10 @@ ELF_WeakrefUsedInReloc_Shift = 11, // One bit. - ELF_BindingSet_Shift = 12 + ELF_BindingSet_Shift = 12, + + // One bit. + ELF_IsMemoryTagged_Shift = 13, }; } @@ -193,4 +196,16 @@ bool MCSymbolELF::isBindingSet() const { return getFlags() & (0x1 << ELF_BindingSet_Shift); } + +bool MCSymbolELF::isMemtag() const { + return getFlags() & (0x1 << ELF_IsMemoryTagged_Shift); +} + +void MCSymbolELF::setMemtag(bool Tagged) { + uint32_t OtherFlags = getFlags() & ~(1 << ELF_IsMemoryTagged_Shift); + if (Tagged) + setFlags(OtherFlags | (1 << ELF_IsMemoryTagged_Shift)); + else + setFlags(OtherFlags); +} } diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp @@ -34,6 +34,8 @@ ~AArch64ELFObjectWriter() override = default; + MCSectionELF *getMemtagRelocsSection(MCContext &Ctx) const override; + protected: unsigned getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const override; @@ -453,6 +455,12 @@ llvm_unreachable("Unimplemented fixup -> relocation"); } +MCSectionELF * +AArch64ELFObjectWriter::getMemtagRelocsSection(MCContext &Ctx) const { + return Ctx.getELFSection(".memtag.globals.static", + ELF::SHT_AARCH64_MEMTAG_GLOBALS_STATIC, 0); +} + std::unique_ptr llvm::createAArch64ELFObjectWriter(uint8_t OSABI, bool IsILP32) { return std::make_unique(OSABI, IsILP32); diff --git a/llvm/test/MC/AArch64/global-tagging.ll b/llvm/test/MC/AArch64/global-tagging.ll new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AArch64/global-tagging.ll @@ -0,0 +1,46 @@ +;; Tagged symbols are only available on aarch64-linux-android. +; RUN: not llc %s -mtriple=aarch64-linux-unknown +; RUN: not llc %s -mtriple=x86_64-linux-unknown + +; RUN: llc %s -mtriple=aarch64-linux-android31 -o %t.S +; RUN: FileCheck %s --input-file=%t.S --check-prefix=CHECK-ASM +; RUN: llvm-mc -filetype=obj %t.S -triple=aarch64-linux-android31 -o %t.o +; RUN: llvm-readelf -r %t.o | FileCheck %s --check-prefix=CHECK-RELOCS + +; RUN: obj2yaml %t.o -o %t.yaml +; RUN: FileCheck %s --input-file=%t.yaml --check-prefix=CHECK-YAML +; RUN: yaml2obj %t.yaml -o %t.o +; RUN: llvm-readelf -r %t.o | FileCheck %s --check-prefix=CHECK-RELOCS + +; CHECK-RELOCS: Relocation section '.rela.memtag.globals.static' {{.*}} contains 4 entries +; CHECK-RELOCS: R_AARCH64_NONE {{.*}} internal_four +; CHECK-RELOCS: R_AARCH64_NONE {{.*}} four +; CHECK-RELOCS: R_AARCH64_NONE {{.*}} sixteen +; CHECK-RELOCS: R_AARCH64_NONE {{.*}} huge +; CHECK-RELOCS-NOT: specialcaselisted + +; CHECK-YAML: Sections: +; CHECK-YAML: - Name: .rela.memtag.globals.static +; CHECK-YAML-NOT: - Name: +; CHECK-YAML: Relocations: +; CHECK-YAML-NEXT: - Symbol: internal_four +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +; CHECK-YAML-NEXT: - Symbol: four +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +; CHECK-YAML-NEXT: - Symbol: sixteen +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +; CHECK-YAML-NEXT: - Symbol: huge +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +; CHECK-YAML-NEXT: - + +; CHECK-ASM: .memtag internal_four +; CHECK-ASM: .memtag four +; CHECK-ASM: .memtag sixteen +; CHECK-ASM: .memtag huge +; CHECK-ASM-NOT: .memtag specialcaselisted + +@internal_four = internal global i32 1, sanitize_memtag +@four = global i32 1, sanitize_memtag +@sixteen = global [16 x i8] zeroinitializer, sanitize_memtag +@huge = global [16777232 x i8] zeroinitializer, sanitize_memtag +@specialcaselisted = global i16 2