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 @@ -976,6 +976,7 @@ // https://android.googlesource.com/platform/bionic/+/6f12bfece5dcc01325e0abba56a46b1bcf991c69/tools/relocation_packer/src/elf_file.cc#37 SHT_ANDROID_REL = 0x60000001, SHT_ANDROID_RELA = 0x60000002, + SHT_ANDROID_MEMTAG_GLOBALS = 0x60000003, SHT_LLVM_ODRTAB = 0x6fff4c00, // LLVM ODR table. SHT_LLVM_LINKER_OPTIONS = 0x6fff4c01, // LLVM Linker Options. SHT_LLVM_ADDRSIG = 0x6fff4c03, // List of address-significant symbols @@ -1578,6 +1579,7 @@ NT_ANDROID_TYPE_IDENT = 1, NT_ANDROID_TYPE_KUSER = 3, NT_ANDROID_TYPE_MEMTAG = 4, + NT_ANDROID_TYPE_MEMTAG_GLOBALS = 8, }; // Memory tagging values used in NT_ANDROID_TYPE_MEMTAG notes. @@ -1593,7 +1595,8 @@ NT_MEMTAG_LEVEL_SYNC = 2, NT_MEMTAG_LEVEL_MASK = 3, // Bits indicating whether the loader should prepare for MTE to be enabled on - // the heap and/or stack. + // the heap and/or stack. Whether globals are protected or not depends on the + // presence of the NT_ANDROID_TYPE_MEMTAG_GLOBALS note. NT_MEMTAG_HEAP = 4, NT_MEMTAG_STACK = 8, }; 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 TaggedAttr = MCSA_Tagged; + //===--- Dwarf Emission Directives -----------------------------------===// /// True if target supports emission of debugging information. Defaults to @@ -772,6 +774,8 @@ return ProtectedVisibilityAttr; } + MCSymbolAttr getTaggedAttr() const { return TaggedAttr; } + 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_Tagged ///< .tagged (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, returns 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; + bool isMemoryTagged() const; + void setTagged(bool Tagged); + 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 @@ -710,6 +710,12 @@ // GV's or GVSym's attributes will be used for the EmittedSym. emitVisibility(EmittedSym, GV->getVisibility(), !GV->isDeclaration()); + if (GV->hasSanitizerMetadata() && GV->isTagged()) { + assert(TM.getTargetTriple().getArch() == Triple::aarch64 && + "Tagged symbols are only supported on aarch64."); + OutStreamer->emitSymbolAttribute(EmittedSym, MAI->getTaggedAttr()); + } + 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 @@ -1084,6 +1084,21 @@ // Write out the ELF header ... writeHeader(Asm); + MCSectionELF *MemtagRelocs = nullptr; + for (const MCSymbol &Sym : Asm.symbols()) { + const auto &SymE = cast(Sym); + if (!SymE.isMemoryTagged()) + continue; + if (MemtagRelocs == nullptr) { + MemtagRelocs = OWriter.TargetObjectWriter->getMemtagRelocsSection(Ctx); + assert(MemtagRelocs != nullptr && + "Tagged globals are only supported on AArch64"); + Asm.registerSection(*MemtagRelocs); + } + ELFRelocationEntry Rec(0, &SymE, ELF::R_AARCH64_NONE, 0, nullptr, 0); + OWriter.Relocations[MemtagRelocs].push_back(Rec); + } + // ... then the sections ... SectionOffsetsTy SectionOffsets; std::vector Groups; @@ -1319,6 +1334,15 @@ 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_ANDROID_MEMTAG_GLOBALS) 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->isMemoryTagged()) + return true; + // An undefined symbol is not in any section, so the relocation has to point // to the symbol itself. assert(Sym && "Expected a symbol"); 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 @@ -764,6 +764,9 @@ case MCSA_Exported: // Non-AIX assemblers currently do not support exported visibility. return false; + case MCSA_Tagged: + OS << "\t.tagged\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 @@ -286,6 +286,10 @@ Symbol->setVisibility(ELF::STV_PROTECTED); break; + case MCSA_Tagged: + Symbol->setTagged(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 @@ -360,6 +360,7 @@ case MCSA_Local: case MCSA_LGlobal: case MCSA_Exported: + case MCSA_Tagged: 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 @@ -541,6 +541,7 @@ DK_LTO_DISCARD, DK_LTO_SET_CONDITIONAL, DK_CFI_MTE_TAGGED_FRAME, + DK_TAGGED, DK_END }; @@ -2298,6 +2299,8 @@ return parseDirectivePseudoProbe(); case DK_LTO_DISCARD: return parseDirectiveLTODiscard(); + case DK_TAGGED: + return parseDirectiveSymbolAttribute(MCSA_Tagged); } return Error(IDLoc, "unknown directive"); @@ -4986,8 +4989,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_Tagged) return Error(Loc, "non-local symbol required"); if (!getStreamer().emitSymbolAttribute(Sym, Attr)) @@ -5600,6 +5604,7 @@ DirectiveKindMap[".pseudoprobe"] = DK_PSEUDO_PROBE; DirectiveKindMap[".lto_discard"] = DK_LTO_DISCARD; DirectiveKindMap[".lto_set_conditional"] = DK_LTO_SET_CONDITIONAL; + DirectiveKindMap[".tagged"] = DK_TAGGED; } 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::isMemoryTagged() const { + return getFlags() & (0x1 << ELF_IsMemoryTagged_Shift); +} + +void MCSymbolELF::setTagged(bool Tagged) { + uint32_t OtherFlags = getFlags() & ~(0x1 << ELF_IsMemoryTagged_Shift); + if (Tagged) + setFlags(OtherFlags | (0x1 << ELF_IsMemoryTagged_Shift)); + else + setFlags(OtherFlags); +} } diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -181,6 +181,7 @@ ECase(NT_ANDROID_TYPE_IDENT); ECase(NT_ANDROID_TYPE_KUSER); ECase(NT_ANDROID_TYPE_MEMTAG); + ECase(NT_ANDROID_TYPE_MEMTAG_GLOBALS); #undef ECase IO.enumFallback(Value); } 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,11 @@ llvm_unreachable("Unimplemented fixup -> relocation"); } +MCSectionELF* AArch64ELFObjectWriter::getMemtagRelocsSection(MCContext &Ctx) const { + MCSectionELF *Sec = Ctx.getELFSection(".memtag.globals", ELF::SHT_ANDROID_MEMTAG_GLOBALS, 0); + return Sec; +} + std::unique_ptr llvm::createAArch64ELFObjectWriter(uint8_t OSABI, bool IsILP32) { return std::make_unique(OSABI, IsILP32); diff --git a/llvm/test/CodeGen/AArch64/global-tagging.ll b/llvm/test/CodeGen/AArch64/global-tagging.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/global-tagging.ll @@ -0,0 +1,45 @@ +; 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-objdump -r %t.o | FileCheck %s --check-prefix=CHECK-RELOCS + +; RUN: obj2yaml %t.o > %t.yaml +; RUN: FileCheck %s --input-file=%t.yaml --check-prefix=CHECK-YAML +; RUN: yaml2obj %t.yaml -o %t.o +; RUN: llvm-objdump -r %t.o | FileCheck %s --check-prefix=CHECK-RELOCS + +; CHECK-RELOCS: RELOCATION RECORDS FOR [.memtag.globals]: +; CHECK-RELOCS-NEXT: OFFSET TYPE VALUE +; CHECK-YAML: Sections: +; CHECK-YAML: - Name: .rela.memtag.globals +; CHECK-YAML-NOT: - Name: +; CHECK-YAML: Relocations: + +; CHECK-ASM: .tagged internal_four +; CHECK-RELOCS-NEXT: R_AARCH64_NONE internal_four +; CHECK-YAML-NEXT: - Symbol: internal_four +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +@internal_four = internal global i32 1, sanitize_memtag + +; CHECK-ASM: .tagged four +; CHECK-RELOCS-NEXT: R_AARCH64_NONE four +; CHECK-YAML-NEXT: - Symbol: four +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +@four = global i32 1, sanitize_memtag + +; CHECK-ASM: .tagged sixteen +; CHECK-RELOCS-NEXT: R_AARCH64_NONE sixteen +; CHECK-YAML-NEXT: - Symbol: sixteen +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +@sixteen = global [16 x i8] zeroinitializer, sanitize_memtag + +; CHECK-ASM: .tagged huge +; CHECK-RELOCS-NEXT: R_AARCH64_NONE huge +; CHECK-YAML-NEXT: - Symbol: huge +; CHECK-YAML-NEXT: Type: R_AARCH64_NONE +@huge = global [16777232 x i8] zeroinitializer, sanitize_memtag + +; CHECK-ASM-NOT: .tagged specialcaselisted +; CHECK-RELOCS-NOT: R_AARCH64_NONE specialcaselisted +; CHECK-YAML-NEXT: - +@specialcaselisted = global i16 2, no_sanitize_memtag