Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2299,6 +2299,9 @@ def mno_fix_cortex_a53_835769 : Flag<["-"], "mno-fix-cortex-a53-835769">, Group, HelpText<"Don't workaround Cortex-A53 erratum 835769 (AArch64 only)">; +def mmark_bti_property : Flag<["-"], "mmark-bti-property">, + Group, + HelpText<"Add .note.gnu.property with BTI to assembly files (AArch64 only)">; foreach i = {1-31} in def ffixed_x#i : Flag<["-"], "ffixed-x"#i>, Group, HelpText<"Reserve the x"#i#" register (AArch64/RISC-V only)">; Index: clang/lib/Driver/ToolChains/Arch/AArch64.cpp =================================================================== --- clang/lib/Driver/ToolChains/Arch/AArch64.cpp +++ clang/lib/Driver/ToolChains/Arch/AArch64.cpp @@ -474,4 +474,7 @@ if (Args.hasArg(options::OPT_mno_neg_immediates)) Features.push_back("+no-neg-immediates"); + + if (Args.hasArg(options::OPT_mmark_bti_property)) + Features.push_back("+markbtiproperty"); } Index: clang/test/Driver/arm64-markbti.S =================================================================== --- /dev/null +++ clang/test/Driver/arm64-markbti.S @@ -0,0 +1,24 @@ +// When -mbti is passed the generated file object gets BTI marking. +// RUN: %clang -target arm64-linux-none -mmark-bti-property -c %s -o -| llvm-readobj -n | FileCheck -check-prefix=CHECK -check-prefix=CHECK_GEN %s +// RUN: %clang -target arm64-linux-none -DNOTE_PRESENT -c %s -o - | llvm-readobj -n | FileCheck -check-prefix=CHECK -check-prefix=CHECK_PRESET %s +// RUN: %clang -target arm64-linux-none -mmark-bti-property -DNOTE_PRESENT -c %s -o - | llvm-readobj -n | FileCheck -check-prefix=CHECK -check-prefix=CHECK_PRESET %s +// RUN: %clang -target arm64-linux-none -mmark-bti-property -DNOTE_PRESENT -c %s -o - 2>&1 | FileCheck -check-prefix=CHECK_WARNING %s +// +// CHECK_WARNING: The .note.gnu.property is not emitted because it is already present. +// CHECK: Name: .note.gnu.property +// CHECK: Type: NT_GNU_PROPERTY_TYPE_0 +// CHECK_GEN: aarch64 feature: BTI +// CHECK_PRESET: aarch64 feature: BTI, PAC + +#ifdef NOTE_PRESENT + .section .note.gnu.property, "a"; + .balign 8; + .long 4; + .long 0x10; + .long 0x5 + .asciz "GNU" + .long 0xc0000000 + .long 4 + .long 3 + .long 0 +#endif Index: llvm/lib/Target/AArch64/AArch64.td =================================================================== --- llvm/lib/Target/AArch64/AArch64.td +++ llvm/lib/Target/AArch64/AArch64.td @@ -349,6 +349,9 @@ def FeatureBranchTargetId : SubtargetFeature<"bti", "HasBTI", "true", "Enable Branch Target Identification" >; +def FeatureEmitNoteBTIProperty : SubtargetFeature<"markbtiproperty", "MarkBTIProperty", + "true", "Emit .note.gnu.property for Branch Target Identification" >; + def FeatureRandGen : SubtargetFeature<"rand", "HasRandGen", "true", "Enable Random Number generation instructions" >; Index: llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -221,26 +221,9 @@ return; // Emit a .note.gnu.property section with the flags. - MCSection *Cur = OutStreamer->getCurrentSectionOnly(); - MCSection *Nt = MMI->getContext().getELFSection( - ".note.gnu.property", ELF::SHT_NOTE, ELF::SHF_ALLOC); - OutStreamer->SwitchSection(Nt); - - // Emit the note header. - emitAlignment(Align(8)); - OutStreamer->emitInt32(4); // data size for "GNU\0" - OutStreamer->emitInt32(4 * 4); // Elf_Prop size - OutStreamer->emitInt32(ELF::NT_GNU_PROPERTY_TYPE_0); - OutStreamer->emitBytes(StringRef("GNU", 4)); // note name - - // Emit the PAC/BTI properties. - OutStreamer->emitInt32(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND); - OutStreamer->emitInt32(4); // data size - OutStreamer->emitInt32(Flags); // data - OutStreamer->emitInt32(0); // pad - - OutStreamer->endSection(Nt); - OutStreamer->SwitchSection(Cur); + if (auto *TS = static_cast( + OutStreamer->getTargetStreamer())) + TS->emitNoteSection(Flags); } void AArch64AsmPrinter::emitFunctionHeaderComment() { Index: llvm/lib/Target/AArch64/AArch64Subtarget.h =================================================================== --- llvm/lib/Target/AArch64/AArch64Subtarget.h +++ llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -185,6 +185,9 @@ // Enable 64-bit vectorization in SLP. unsigned MinVectorRegisterBitWidth = 64; + // Emitting .note.gnu.property in the assembler + bool MarkBTIProperty = false; + bool UseAA = false; bool PredictableSelectIsExpensive = false; bool BalanceFPOps = false; @@ -481,6 +484,8 @@ bool hasFMI() const { return HasFMI; } bool hasRCPC_IMMO() const { return HasRCPC_IMMO; } + bool markBTIProperty() const { return MarkBTIProperty; } + bool addrSinkUsingGEPs() const override { // Keeping GEPs inbounds is important for exploiting AArch64 // addressing-modes in ILP32 mode. Index: llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp =================================================================== --- llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -240,7 +240,7 @@ MCAsmParserExtension::Initialize(Parser); MCStreamer &S = getParser().getStreamer(); if (S.getTargetStreamer() == nullptr) - new AArch64TargetStreamer(S); + new AArch64TargetStreamer(S, &STI); // Alias .hword/.word/.[dx]word to the target-independent // .2byte/.4byte/.8byte directives as they have the same form and Index: llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h =================================================================== --- llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -20,6 +20,7 @@ class AArch64TargetStreamer : public MCTargetStreamer { public: AArch64TargetStreamer(MCStreamer &S); + AArch64TargetStreamer(MCStreamer &S, const MCSubtargetInfo *STI); ~AArch64TargetStreamer() override; void finish() override; @@ -36,6 +37,9 @@ /// Callback used to implement the .inst directive. virtual void emitInst(uint32_t Inst); + /// Callback used to implement the .note.gnu.property section. + virtual void emitNoteSection(unsigned Flags); + virtual void EmitARM64WinCFIAllocStack(unsigned Size) {} virtual void EmitARM64WinCFISaveFPLR(int Offset) {} virtual void EmitARM64WinCFISaveFPLRX(int Offset) {} @@ -56,6 +60,7 @@ private: std::unique_ptr ConstantPools; + const MCSubtargetInfo *STI; }; class AArch64TargetELFStreamer : public AArch64TargetStreamer { @@ -65,7 +70,8 @@ void emitInst(uint32_t Inst) override; public: - AArch64TargetELFStreamer(MCStreamer &S) : AArch64TargetStreamer(S) {} + AArch64TargetELFStreamer(MCStreamer &S, const MCSubtargetInfo *STI) + : AArch64TargetStreamer(S, STI) {} }; class AArch64TargetWinCOFFStreamer : public llvm::AArch64TargetStreamer { @@ -76,8 +82,8 @@ // Symbol of the current epilog for which we are processing SEH directives. MCSymbol *CurrentEpilog = nullptr; public: - AArch64TargetWinCOFFStreamer(llvm::MCStreamer &S) - : AArch64TargetStreamer(S) {} + AArch64TargetWinCOFFStreamer(llvm::MCStreamer &S, const MCSubtargetInfo *STI) + : AArch64TargetStreamer(S, STI) {} // The unwind codes on ARM64 Windows are documented at // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling Index: llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp =================================================================== --- llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp +++ llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp @@ -11,8 +11,12 @@ //===----------------------------------------------------------------------===// #include "AArch64TargetStreamer.h" +#include "AArch64Subtarget.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/ConstantPools.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCSection.h" +#include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSubtargetInfo.h" using namespace llvm; @@ -21,7 +25,13 @@ // AArch64TargetStreamer Implemenation // AArch64TargetStreamer::AArch64TargetStreamer(MCStreamer &S) - : MCTargetStreamer(S), ConstantPools(new AssemblerConstantPools()) {} + : MCTargetStreamer(S), ConstantPools(new AssemblerConstantPools()), + STI(nullptr) {} + +AArch64TargetStreamer::AArch64TargetStreamer(MCStreamer &S, + const MCSubtargetInfo *STI) + : MCTargetStreamer(S), ConstantPools(new AssemblerConstantPools()), + STI(STI) {} AArch64TargetStreamer::~AArch64TargetStreamer() = default; @@ -37,8 +47,54 @@ ConstantPools->emitForCurrentSection(Streamer); } -// finish() - write out any non-empty assembler constant pools. -void AArch64TargetStreamer::finish() { ConstantPools->emitAll(Streamer); } +// finish() - write out any non-empty assembler constant pools and +// write out note.gnu.properties if need. +void AArch64TargetStreamer::finish() { + ConstantPools->emitAll(Streamer); + if (!STI) + return; + + if (STI->getFeatureBits()[AArch64::FeatureEmitNoteBTIProperty]) { + emitNoteSection(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + } +} + +void AArch64TargetStreamer::emitNoteSection(unsigned Flags) { + if (Flags == 0) { + return; + } + MCStreamer &OutStreamer = getStreamer(); + MCContext &Context = OutStreamer.getContext(); + // Emit a .note.gnu.property section with the flags. + MCSection *Cur = OutStreamer.getCurrentSectionOnly(); + MCSectionELF *Nt = Context.getELFSection(".note.gnu.property", ELF::SHT_NOTE, + ELF::SHF_ALLOC); + if (Nt->isRegistered()) { + SMLoc Loc; + Context.reportWarning( + Loc, + "The .note.gnu.property is not emitted because it is already present."); + return; + } + + OutStreamer.SwitchSection(Nt); + + // Emit the note header. + OutStreamer.emitValueToAlignment(Align(8).value()); + OutStreamer.emitIntValue(4, 4); // data size for "GNU\0" + OutStreamer.emitIntValue(4 * 4, 4); // Elf_Prop size + OutStreamer.emitIntValue(ELF::NT_GNU_PROPERTY_TYPE_0, 4); + OutStreamer.emitBytes(StringRef("GNU", 4)); // note name + + // Emit the PAC/BTI properties. + OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4); + OutStreamer.emitIntValue(4, 4); // data size + OutStreamer.emitIntValue(Flags, 4); // data + OutStreamer.emitIntValue(0, 4); // pad + + OutStreamer.endSection(Nt); + OutStreamer.SwitchSection(Cur); +} void AArch64TargetStreamer::emitInst(uint32_t Inst) { char Buffer[4]; @@ -60,9 +116,9 @@ createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { const Triple &TT = STI.getTargetTriple(); if (TT.isOSBinFormatELF()) - return new AArch64TargetELFStreamer(S); + return new AArch64TargetELFStreamer(S, &STI); if (TT.isOSBinFormatCOFF()) - return new AArch64TargetWinCOFFStreamer(S); + return new AArch64TargetWinCOFFStreamer(S, &STI); return nullptr; }