Index: ELF/Arch/AArch64.cpp =================================================================== --- ELF/Arch/AArch64.cpp +++ ELF/Arch/AArch64.cpp @@ -28,7 +28,7 @@ } namespace { -class AArch64 final : public TargetInfo { +class AArch64 : public TargetInfo { public: AArch64(); RelExpr getRelExpr(RelType Type, const Symbol &S, @@ -180,6 +180,7 @@ void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { + // Standard PLT entry const uint8_t Inst[] = { 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] @@ -433,7 +434,194 @@ llvm_unreachable("invalid relocation for TLS IE to LE relaxation"); } -TargetInfo *elf::getAArch64TargetInfo() { - static AArch64 Target; - return &Target; +// AArch64 may use security features in variant PLT sequences. These are: +// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target +// Indicator (BTI) introduced in armv8.5-a. The additional instructions used +// in the variant Plt sequences are encoded in the Hint space so they can be +// deployed on older architectures, which treat the instructions as a nop. +// PAC and BTI can be combined leading to the following combinations: +// writePltHeader +// writePltHeaderBti (no PAC Header needed) +// writePlt +// writePltBti (BTI only) +// writePltPac (PAC only) +// writePltBtiPac (BTI and PAC) +// +// When PAC is enabled the dynamic loader encrypts the address that it places +// in the .got.plt using the pacia1716 instruction which encrypts the value in +// x17 using the modifier in x16. The static linker places autia1716 before the +// indirect branch to x17 to authenticate the address in x17 with the modifier +// in x16. This makes it more difficult for an attacker to modify the value in +// the .got.plt. +// +// When BTI is enabled all indirect branches must land on a bti instruction. +// The static linker must place a bti instruction at the start of any PLT entry +// that may be the target of an indirect branch. As the PLT entries call the +// lazy resolver indirectly this must have a bti instruction at start. In +// general a bti instruction is not needed for a PLT entry as indirect calls +// are resolved to the function address and not the PLT entry for the function. +// There are a small number of cases where the PLT address can escape, such as +// taking the address of a function or ifunc via a non got-generating +// relocation, and a shared library refers to that symbol. +// +// We use the bti c variant of the instruction which permits indirect branches +// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI +// guarantees that all indirect branches from code requiring BTI protection +// will go via x16/x17 + +namespace { +class AArch64BtiPac final : public AArch64 { +public: + AArch64BtiPac(); + void writePltHeader(uint8_t *Buf) const override; + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, + int32_t Index, unsigned RelOff) const override; + +private: + void writePltHeaderBti(uint8_t *Buf) const; + void writePltBti(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const; + void writePltPac(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const; + void writePltBtiPac(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const; + enum class PltType { Standard = 0, Bti = 1, Pac = 2, BtiPac = 3 }; + PltType EntryType; +}; +} // namespace + +AArch64BtiPac::AArch64BtiPac() { + // A BTI (Branch Target Indicator) Plt Entry is only required if the + // address of the PLT entry can be taken by the program, which permits an + // indirect jump to the PLT entry. + bool BtiNeeded = !Config->Shared && + (Config->AndFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + bool PacNeeded = (Config->AndFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); + + if (BtiNeeded && PacNeeded) + EntryType = PltType::BtiPac; + else if (BtiNeeded) + EntryType = PltType::Bti; + else if (PacNeeded) + EntryType = PltType::Pac; + else + EntryType = PltType::Standard; + + if (EntryType != PltType::Standard) + PltEntrySize = 24; +} + +void AArch64BtiPac::writePltHeaderBti(uint8_t *Buf) const { + const uint8_t PltData[] = { + 0x5f, 0x24, 0x03, 0xd5, // bti c + 0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2])) + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + memcpy(Buf, PltData, sizeof(PltData)); + + uint64_t Got = In.GotPlt->getVA(); + uint64_t Plt = In.Plt->getVA(); + relocateOne(Buf + 8, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(Got + 16) - getAArch64Page(Plt + 8)); + relocateOne(Buf + 12, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16); + relocateOne(Buf + 16, R_AARCH64_ADD_ABS_LO12_NC, Got + 16); +} + +void AArch64BtiPac::writePltHeader(uint8_t *Buf) const { + if (Config->AndFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) + writePltHeaderBti(Buf); + else + AArch64::writePltHeader(Buf); +} + +void AArch64BtiPac::writePltBti(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + const uint8_t Inst[] = { + 0x5f, 0x24, 0x03, 0xd5, // bti c + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr+4)); + relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); + relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); +} + +void AArch64BtiPac::writePltPac(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + const uint8_t Inst[] = { + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) + 0x9f, 0x21, 0x03, 0xd5, // autiax16x17 + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr)); + relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); + relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); } + +void AArch64BtiPac::writePltBtiPac(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + const uint8_t Inst[] = { + 0x5f, 0x24, 0x03, 0xd5, // bti c + 0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n])) + 0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))] + 0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n])) + 0x9f, 0x21, 0x03, 0xd5, // autiax16x17 + 0x20, 0x02, 0x1f, 0xd6 // br x17 + }; + memcpy(Buf, Inst, sizeof(Inst)); + + relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr)); + relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); + relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); +} + +void AArch64BtiPac::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + switch (EntryType) { + case PltType::Standard: + AArch64::writePlt(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff); + break; + case PltType::Bti: + writePltBti(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff); + break; + case PltType::Pac: + writePltPac(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff); + break; + case PltType::BtiPac: + writePltBtiPac(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff); + break; + } +} + +static TargetInfo *getTargetInfo() { + if (Config->AndFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI | + GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) { + static AArch64BtiPac T; + return &T; + } + static AArch64 T; + return &T; +} + +TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } Index: ELF/Arch/X86.cpp =================================================================== --- ELF/Arch/X86.cpp +++ ELF/Arch/X86.cpp @@ -616,7 +616,7 @@ return &T; } - if (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT) { + if (Config->AndFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) { static IntelCET T; return &T; } Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -750,7 +750,7 @@ return &T; } - if (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT) { + if (Config->AndFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) { static IntelCET T; return &T; } Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -82,7 +82,7 @@ // Most fields are initialized by the driver. struct Configuration { uint8_t OSABI = 0; - uint32_t X86Features = 0; + uint32_t AndFeatures = 0; llvm::CachePruningPolicy ThinLTOCachePolicy; llvm::StringMap SectionStartMap; llvm::StringRef Chroot; @@ -147,6 +147,7 @@ bool ExecuteOnly; bool ExportDynamic; bool FixCortexA53Errata843419; + bool ForceBTI; bool FormatBinary = false; bool RequireCET; bool GcSections; @@ -168,6 +169,7 @@ bool OFormatBinary; bool Omagic; bool OptRemarksWithHotness; + bool PacPlt; bool PicThunk; bool Pie; bool PrintGcSections; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -816,6 +816,7 @@ Config->FilterList = args::getStrings(Args, OPT_filter); Config->Fini = Args.getLastArgValue(OPT_fini, "_fini"); Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419); + Config->ForceBTI = Args.hasArg(OPT_force_bti); Config->RequireCET = Args.hasArg(OPT_require_cet); Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); @@ -851,6 +852,7 @@ Config->Optimize = args::getInteger(Args, OPT_O, 1); Config->OrphanHandling = getOrphanHandling(Args); Config->OutputFile = Args.getLastArgValue(OPT_o); + Config->PacPlt = Args.hasArg(OPT_pac_plt); Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false); Config->PrintIcfSections = Args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); @@ -1596,17 +1598,31 @@ // with CET. // // This function returns the merged feature flags. If 0, we cannot enable CET. -template static uint32_t getX86Features() { - if (Config->EMachine != EM_386 && Config->EMachine != EM_X86_64) +// This is also the case with AARCH64's BTI and PAC which use the a similar +// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism. +template static uint32_t getAndFeatures() { + if (Config->EMachine != EM_386 && Config->EMachine != EM_X86_64 && + Config->EMachine != EM_AARCH64) return 0; uint32_t Ret = -1; for (InputFile *F : ObjectFiles) { - uint32_t Features = cast>(F)->X86Features; - if (!Features && Config->RequireCET) + uint32_t Features = cast>(F)->AndFeatures; + if (Config->EMachine == EM_AARCH64 && Config->ForceBTI && + !(Features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { + warn(toString(F) + ": --force-bti: file does not have BTI property"); + Features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI; + } + else if (!Features && Config->RequireCET) error(toString(F) + ": --require-cet: file is not compatible with CET"); Ret &= Features; } + + // Force enable pointer authentication Plt, we don't warn in this case as + // this does not require support in the object for correctness. + if (Config->EMachine == EM_AARCH64 && Config->PacPlt) + Ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; + return Ret; } @@ -1788,7 +1804,9 @@ (S->Name.startswith(".debug") || S->Name.startswith(".zdebug")); }); - Config->X86Features = getX86Features(); + // The .note.gnu.property features can influence the type of Target returned + // by getTarget(); + Config->AndFeatures = getAndFeatures(); // The Target instance handles target-specific stuff, such as applying // relocations or writing a PLT section. It also contains target-dependent Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -228,7 +228,7 @@ // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations. uint32_t MipsGp0 = 0; - uint32_t X86Features = 0; + uint32_t AndFeatures = 0; // Name of source file obtained from STT_FILE symbol value, // or empty string if there is no such symbol in object file Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -768,10 +768,11 @@ // certain type. It seems a bit too much to just store a 32-bit value, perhaps // the ABI is unnecessarily complicated. template -static uint32_t readX86Features(ObjFile *Obj, ArrayRef Data) { +static uint32_t readAndFeatures(ObjFile *Obj, ArrayRef Data) { using Elf_Nhdr = typename ELFT::Nhdr; using Elf_Note = typename ELFT::Note; + uint32_t FeaturesSet = 0; while (!Data.empty()) { // Read one NOTE record. if (Data.size() < sizeof(Elf_Nhdr)) @@ -787,6 +788,10 @@ continue; } + uint32_t FeatureAndType = Config->EMachine == EM_AARCH64 + ? GNU_PROPERTY_AARCH64_FEATURE_1_AND + : GNU_PROPERTY_X86_FEATURE_1_AND; + // Read a body of a NOTE record, which consists of type-length-value fields. ArrayRef Desc = Note.getDesc(); while (!Desc.empty()) { @@ -796,9 +801,11 @@ uint32_t Type = read32le(Desc.data()); uint32_t Size = read32le(Desc.data() + 4); - if (Type == GNU_PROPERTY_X86_FEATURE_1_AND) { - // We found the field. - return read32le(Desc.data() + 8); + if (Type == FeatureAndType) { + // We found a FEATURE_1_AND field. There may be more than one of these + // in a .note.gnu.propery section, for a relocatable object we + // accumulate the bits set. + FeaturesSet |= read32le(Desc.data() + 8); } // On 64-bit, a payload may be followed by a 4-byte padding to make its @@ -809,12 +816,11 @@ Desc = Desc.slice(Size + 8); // +8 for Type and Size } - // Go to next NOTE record if a note section didn't contain - // X86_FEATURES_1_AND description. + // Go to next NOTE record to look for more FEATURE_1_AND descriptions. Data = Data.slice(Nhdr->getSize()); } - return 0; + return FeaturesSet; } template @@ -974,7 +980,7 @@ // file's .note.gnu.property section. if (Name == ".note.gnu.property") { ArrayRef Contents = check(this->getObj().getSectionContents(&Sec)); - this->X86Features = readX86Features(this, Contents); + this->AndFeatures = readAndFeatures(this, Contents); return &InputSection::Discarded; } Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -174,6 +174,9 @@ def require_cet: F<"require-cet">, HelpText<"Force enable x86 Control-Flow Enforcement Technology">; +def force_bti: F<"force-bti">, + HelpText<"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have FEATURE_1_BTI property">; + defm format: Eq<"format", "Change the input format of the inputs following this option">, MetaVarName<"[default,elf,binary]">; @@ -268,6 +271,9 @@ Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">, MetaVarName<"[none,android,relr,android+relr]">; +def pac_plt: F<"pac-plt">, + HelpText<"AArch64 only, use pointer authentication in PLT">; + defm use_android_relr_tags: B<"use-android-relr-tags", "Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*", "Use SHT_RELR / DT_RELR* tags (default)">; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -1036,7 +1036,7 @@ if (!Sym.isDefined()) replaceWithDefined( Sym, In.Plt, - Target->PltHeaderSize + Target->PltEntrySize * Sym.PltIndex, 0); + In.Plt->HeaderSize + Target->PltEntrySize * Sym.PltIndex, 0); Sym.NeedsPltAddr = true; Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); return; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -290,8 +290,9 @@ // This class represents a linker-synthesized .note.gnu.property section. // -// In x86, object files may contain feature flags indicating the features that -// they have used. The flags are stored in a .note.gnu.property section. +// In x86 and AArch64, object files may contain feature flags indicating the +// features that they have used. The flags are stored in a .note.gnu.property +// section. // // lld reads the sections from input files and merges them by computing AND of // the flags. The result is written as a new .note.gnu.property section. @@ -304,13 +305,17 @@ ".note.gnu.property") {} void GnuPropertySection::writeTo(uint8_t *Buf) { + uint32_t FeatureAndType = Config->EMachine == EM_AARCH64 + ? GNU_PROPERTY_AARCH64_FEATURE_1_AND + : GNU_PROPERTY_X86_FEATURE_1_AND; + write32(Buf, 4); // Name size write32(Buf + 4, Config->Is64 ? 16 : 12); // Content size write32(Buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type memcpy(Buf + 12, "GNU", 4); // Name string - write32(Buf + 16, GNU_PROPERTY_X86_FEATURE_1_AND); // Feature type + write32(Buf + 16, FeatureAndType); // Feature type write32(Buf + 20, 4); // Feature size - write32(Buf + 24, Config->X86Features); // Feature flags + write32(Buf + 24, Config->AndFeatures); // Feature flags if (Config->Is64) write32(Buf + 28, 0); // Padding } @@ -1338,6 +1343,13 @@ addInt(DT_PLTREL, Config->IsRela ? DT_RELA : DT_REL); } + if (Config->EMachine == EM_AARCH64) { + if (Config->AndFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) + addInt(DT_AARCH64_BTI_PLT, 0); + if (Config->AndFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC) + addInt(DT_AARCH64_PAC_PLT, 0); + } + addInSec(DT_SYMTAB, In.DynSymTab); addInt(DT_SYMENT, sizeof(Elf_Sym)); addInSec(DT_STRTAB, In.DynStrTab); @@ -2284,7 +2296,8 @@ PltSection::PltSection(bool IsIplt) : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ""), IsIplt(IsIplt) { - bool IsX86Ibt = (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT); + bool IsX86Ibt = (Config->EMachine != EM_AARCH64 && + Config->AndFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT); if (Config->EMachine == EM_PPC64) Name = ".glink"; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -431,7 +431,8 @@ false /*Sort*/); Add(In.RelaIplt); - if (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT) { + if (Config->EMachine != EM_AARCH64 && + (Config->AndFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)) { In.IBTPlt = make(); Add(In.IBTPlt); } @@ -441,7 +442,7 @@ In.Iplt = make(true); Add(In.Iplt); - if (Config->X86Features) + if (Config->AndFeatures) Add(make()); // .note.GNU-stack is always added when we are creating a re-linkable Index: docs/ld.lld.1 =================================================================== --- docs/ld.lld.1 +++ docs/ld.lld.1 @@ -185,6 +185,8 @@ .It Fl -require-cet Intel Control-Flow Enforcement Technology is enforced. An error is reported if there is an input object file not compatible with CET. +.It Fl --force-bti +Force enable AArch64 BTI in PLT, warn if Input ELF file does not have FEATURE_1_BTI property. .It Fl -format Ns = Ns Ar input-format , Fl b Ar input-format Specify the format of the inputs following this option. .Ar input-format @@ -385,6 +387,8 @@ .Fl -use-android-relr-tags is specified, use SHT_ANDROID_RELR instead of SHT_RELR. .Pp +.It Fl --pac-plt +AArch64 only, use pointer authentication in PLT. .It Fl -pic-veneer Always generate position independent thunks. .It Fl -pie , Fl -pic-executable Index: test/ELF/Inputs/aarch64-addrifunc.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-addrifunc.s @@ -0,0 +1,8 @@ + .text + .globl myfunc + .globl func1 + .type func1, %function +func1: + adrp x8, :got: myfunc + ldr x8, [x8, :got_lo12: myfunc] + ret Index: test/ELF/Inputs/aarch64-bti1.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-bti1.s @@ -0,0 +1,19 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 1 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + .globl func3 + .type func3, @function + bl func3 + ret Index: test/ELF/Inputs/aarch64-btipac1.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-btipac1.s @@ -0,0 +1,19 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 3 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC and BTI +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + .globl func3 + .type func3, @function + bl func3 + ret Index: test/ELF/Inputs/aarch64-func2.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-func2.s @@ -0,0 +1,8 @@ +.text +.globl func2 +.type func2,@function +func2: + .globl func3 + .type func3, @function + bl func3 + ret Index: test/ELF/Inputs/aarch64-func3-bti.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-func3-bti.s @@ -0,0 +1,16 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 1 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI +.long 0 + +.text +.globl func3 +.type func3,@function +func3: + ret Index: test/ELF/Inputs/aarch64-func3-btipac.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-func3-btipac.s @@ -0,0 +1,16 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 3 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC +.long 0 + +.text +.globl func3 +.type func3,@function +func3: + ret Index: test/ELF/Inputs/aarch64-func3-pac.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-func3-pac.s @@ -0,0 +1,16 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 2 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC +.long 0 + +.text +.globl func3 +.type func3,@function +func3: + ret Index: test/ELF/Inputs/aarch64-func3.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-func3.s @@ -0,0 +1,5 @@ +.text +.globl func3 +.type func3,@function +func3: + ret Index: test/ELF/Inputs/aarch64-nobti.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-nobti.s @@ -0,0 +1,8 @@ +.text +.globl func2 +.type func2,@function +func2: + .globl func3 + .type func3, @function + bl func3 + ret Index: test/ELF/Inputs/aarch64-nopac.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-nopac.s @@ -0,0 +1,8 @@ +.text +.globl func2 +.type func2,@function +func2: + .globl func3 + .type func3, @function + bl func3 + ret Index: test/ELF/Inputs/aarch64-pac1.s =================================================================== --- /dev/null +++ test/ELF/Inputs/aarch64-pac1.s @@ -0,0 +1,19 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 2 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + .globl func3 + .type func3, @function + bl func3 + ret Index: test/ELF/aarch64-feature-bti.s =================================================================== --- /dev/null +++ test/ELF/aarch64-feature-bti.s @@ -0,0 +1,217 @@ +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-bti1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3-bti.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func2.s -o %tno.o + +# We do not add BTI support when the inputs don't have the .note.gnu.property field. + +# RUN: ld.lld %tno.o %t3.o --shared -o %tno.so +# RUN: llvm-objdump -d -mattr=+bti %tno.so | FileCheck --check-prefix=NOBTI %s +# RUN: llvm-objdump -s %tno.so | FileCheck --check-prefix SOGOTPLT %s +# RUN: llvm-readelf --dynamic-table %tno.so | FileCheck --check-prefix NOBTIDYN %s + +# NOBTIDYN-NOT: 0x0000000070000001 (AARCH64_BTI_PLT) +# NOBTIDYN-NOT: 0x0000000070000003 (AARCH64_PAC_PLT) + +# NOBTI: 0000000000010000 func2: +# NOBTI-NEXT: 10000: 0c 00 00 94 bl #48 +# NOBTI-NEXT: 10004: c0 03 5f d6 ret +# NOBTI: Disassembly of section .plt: +# NOBTI: 0000000000010010 .plt: +# NOBTI-NEXT: 10010: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# NOBTI-NEXT: 10014: 10 01 00 90 adrp x16, #131072 +# NOBTI-NEXT: 10018: 11 0a 40 f9 ldr x17, [x16, #16] +# NOBTI-NEXT: 1001c: 10 42 00 91 add x16, x16, #16 +# NOBTI-NEXT: 10020: 20 02 1f d6 br x17 +# NOBTI-NEXT: 10024: 1f 20 03 d5 nop +# NOBTI-NEXT: 10028: 1f 20 03 d5 nop +# NOBTI-NEXT: 1002c: 1f 20 03 d5 nop +# NOBTI: 0000000000010030 func3@plt: +# NOBTI-NEXT: 10030: 10 01 00 90 adrp x16, #131072 +# NOBTI-NEXT: 10034: 11 0e 40 f9 ldr x17, [x16, #24] +# NOBTI-NEXT: 10038: 10 62 00 91 add x16, x16, #24 +# NOBTI-NEXT: 1003c: 20 02 1f d6 br x17 + +# Expect a bti c at the start of plt[0], the plt entries do not need bti c as +# their address doesn't escape, so they can't be indirectly called. Expect no +# other difference. + +# RUN: ld.lld %t1.o %t3.o --shared -o %t.so +# RUN: llvm-readelf -n %t.so | FileCheck --check-prefix BTIPROP %s +# RUN: llvm-objdump -d -mattr=+bti %t.so | FileCheck --check-prefix BTISO %s +# RUN: llvm-objdump -s %t.so | FileCheck --check-prefix SOGOTPLT %s +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix BTIDYN %s + +# BTIPROP: Properties: aarch64 feature: BTI + +# BTIDYN: 0x0000000070000001 (AARCH64_BTI_PLT) +# BTIDYN-NOT: 0x0000000070000003 (AARCH64_PAC_PLT) + +# BTISO: 0000000000010000 func2: +# BTISO-NEXT: 10000: 0c 00 00 94 bl #48 +# BTISO-NEXT: 10004: c0 03 5f d6 ret +# BTISO: Disassembly of section .plt: +# BTISO: 0000000000010010 .plt: +# BTISO-NEXT: 10010: 5f 24 03 d5 bti c +# BTISO-NEXT: 10014: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# BTISO-NEXT: 10018: 10 01 00 90 adrp x16, #131072 +# BTISO-NEXT: 1001c: 11 0a 40 f9 ldr x17, [x16, #16] +# BTISO-NEXT: 10020: 10 42 00 91 add x16, x16, #16 +# BTISO-NEXT: 10024: 20 02 1f d6 br x17 +# BTISO-NEXT: 10028: 1f 20 03 d5 nop +# BTISO-NEXT: 1002c: 1f 20 03 d5 nop +# BTISO: 0000000000010030 func3@plt: +# BTISO-NEXT: 10030: 10 01 00 90 adrp x16, #131072 +# BTISO-NEXT: 10034: 11 0e 40 f9 ldr x17, [x16, #24] +# BTISO-NEXT: 10038: 10 62 00 91 add x16, x16, #24 +# BTISO-NEXT: 1003c: 20 02 1f d6 br x17 + +# The .got.plt should be identical between the BTI and no BTI DSO PLT. +# SOGOTPLT: Contents of section .got.plt: +# SOGOTPLT-NEXT: 30000 00000000 00000000 00000000 00000000 +# SOGOTPLT-NEXT: 30010 00000000 00000000 10000100 00000000 + +# Build an executable with all relocatable inputs having the BTI .note.property +# We expect a bti c in front of all PLT entries as the address of a PLT entry +# can escape an executable. + +# RUN: ld.lld %t2.o --shared -o %t2.so + +# RUN: ld.lld %t.o %t.so %t2.so -o %t.exe +# RUN: llvm-readelf --dynamic-table -n %t.exe | FileCheck --check-prefix=BTIPROP %s +# RUN: llvm-objdump -d -mattr=+bti %t.exe | FileCheck --check-prefix=EXECBTI %s + +# EXECBTI: Disassembly of section .text: +# EXECBTI: 0000000000210000 func1: +# EXECBTI-NEXT: 210000: 0c 00 00 94 bl #48 +# EXECBTI-NEXT: 210004: c0 03 5f d6 ret +# EXECBTI: Disassembly of section .plt: +# EXECBTI: 0000000000210010 .plt: +# EXECBTI-NEXT: 210010: 5f 24 03 d5 bti c +# EXECBTI-NEXT: 210014: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# EXECBTI-NEXT: 210018: 10 01 00 90 adrp x16, #131072 +# EXECBTI-NEXT: 21001c: 11 0a 40 f9 ldr x17, [x16, #16] +# EXECBTI-NEXT: 210020: 10 42 00 91 add x16, x16, #16 +# EXECBTI-NEXT: 210024: 20 02 1f d6 br x17 +# EXECBTI-NEXT: 210028: 1f 20 03 d5 nop +# EXECBTI-NEXT: 21002c: 1f 20 03 d5 nop +# EXECBTI: 0000000000210030 func2@plt: +# EXECBTI-NEXT: 210030: 5f 24 03 d5 bti c +# EXECBTI-NEXT: 210034: 10 01 00 90 adrp x16, #131072 +# EXECBTI-NEXT: 210038: 11 0e 40 f9 ldr x17, [x16, #24] +# EXECBTI-NEXT: 21003c: 10 62 00 91 add x16, x16, #24 +# EXECBTI-NEXT: 210040: 20 02 1f d6 br x17 +# EXECBTI-NEXT: 210044: 1f 20 03 d5 nop + +# We expect the same for PIE, as the address of an ifunc can escape +# RUN: ld.lld --pie %t.o %t.so %t2.so -o %tpie.exe +# RUN: llvm-readelf -n %tpie.exe | FileCheck --check-prefix=BTIPROP %s +# RUN: llvm-readelf --dynamic-table -n %tpie.exe | FileCheck --check-prefix=BTIPROP %s +# RUN: llvm-objdump -d -mattr=+bti %tpie.exe | FileCheck --check-prefix=PIE %s + +# PIE: Disassembly of section .text: +# PIE: 0000000000010000 func1: +# PIE-NEXT: 10000: 0c 00 00 94 bl #48 +# PIE-NEXT: 10004: c0 03 5f d6 ret +# PIE: Disassembly of section .plt: +# PIE: 0000000000010010 .plt: +# PIE-NEXT: 10010: 5f 24 03 d5 bti c +# PIE-NEXT: 10014: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# PIE-NEXT: 10018: 10 01 00 90 adrp x16, #131072 +# PIE-NEXT: 1001c: 11 0a 40 f9 ldr x17, [x16, #16] +# PIE-NEXT: 10020: 10 42 00 91 add x16, x16, #16 +# PIE-NEXT: 10024: 20 02 1f d6 br x17 +# PIE-NEXT: 10028: 1f 20 03 d5 nop +# PIE-NEXT: 1002c: 1f 20 03 d5 nop +# PIE: 0000000000010030 func2@plt: +# PIE-NEXT: 10030: 5f 24 03 d5 bti c +# PIE-NEXT: 10034: 10 01 00 90 adrp x16, #131072 +# PIE-NEXT: 10038: 11 0e 40 f9 ldr x17, [x16, #24] +# PIE-NEXT: 1003c: 10 62 00 91 add x16, x16, #24 +# PIE-NEXT: 10040: 20 02 1f d6 br x17 +# PIE-NEXT: 10044: 1f 20 03 d5 nop + +# Build and executable with not all relocatable inputs having the BTI +# .note.property, expect no bti c and no .note.gnu.property entry + +# RUN: ld.lld %t.o %t2.o %t.so -o %tnobti.exe +# RUN: llvm-readelf --dynamic-table %tnobti.exe | FileCheck --check-prefix NOBTIDYN %s +# RUN: llvm-objdump -d -mattr=+bti %tnobti.exe | FileCheck --check-prefix=NOEX %s + +# NOEX: Disassembly of section .text: +# NOEX: 0000000000210000 func1: +# NOEX-NEXT: 210000: 0c 00 00 94 bl #48 +# NOEX-NEXT: 210004: c0 03 5f d6 ret +# NOEX: 0000000000210008 func3: +# NOEX-NEXT: 210008: c0 03 5f d6 ret +# NOEX: Disassembly of section .plt: +# NOEX: 0000000000210010 .plt: +# NOEX-NEXT: 210010: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# NOEX-NEXT: 210014: 10 01 00 90 adrp x16, #131072 +# NOEX-NEXT: 210018: 11 0a 40 f9 ldr x17, [x16, #16] +# NOEX-NEXT: 21001c: 10 42 00 91 add x16, x16, #16 +# NOEX-NEXT: 210020: 20 02 1f d6 br x17 +# NOEX-NEXT: 210024: 1f 20 03 d5 nop +# NOEX-NEXT: 210028: 1f 20 03 d5 nop +# NOEX-NEXT: 21002c: 1f 20 03 d5 nop +# NOEX: 0000000000210030 func2@plt: +# NOEX-NEXT: 210030: 10 01 00 90 adrp x16, #131072 +# NOEX-NEXT: 210034: 11 0e 40 f9 ldr x17, [x16, #24] +# NOEX-NEXT: 210038: 10 62 00 91 add x16, x16, #24 +# NOEX-NEXT: 21003c: 20 02 1f d6 br x17 + +# Force BTI entries with the --force-bti command line option. Expect a warning +# from the file without the .note.gnu.property. + +# RUN: ld.lld %t.o %t2.o --force-bti %t.so -o %tforcebti.exe 2>&1 | FileCheck --check-prefix=FORCE-WARN %s + +# FORCE-WARN: aarch64-feature-bti.s.tmp2.o: --force-bti: file does not have BTI property + + +# RUN: llvm-readelf -n %tforcebti.exe | FileCheck --check-prefix=BTIPROP %s +# RUN: llvm-readelf --dynamic-table %tforcebti.exe | FileCheck --check-prefix BTIDYN %s +# RUN: llvm-objdump -d -mattr=+bti %tforcebti.exe | FileCheck --check-prefix=FORCE %s + +# FORCE: Disassembly of section .text: +# FORCE: 0000000000210000 func1: +# FORCE-NEXT: 210000: 0c 00 00 94 bl #48 +# FORCE-NEXT: 210004: c0 03 5f d6 ret +# FORCE: 0000000000210008 func3: +# FORCE-NEXT: 210008: c0 03 5f d6 ret +# FORCE: Disassembly of section .plt: +# FORCE: 0000000000210010 .plt: +# FORCE-NEXT: 210010: 5f 24 03 d5 bti c +# FORCE-NEXT: 210014: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# FORCE-NEXT: 210018: 10 01 00 90 adrp x16, #131072 +# FORCE-NEXT: 21001c: 11 0a 40 f9 ldr x17, [x16, #16] +# FORCE-NEXT: 210020: 10 42 00 91 add x16, x16, #16 +# FORCE-NEXT: 210024: 20 02 1f d6 br x17 +# FORCE-NEXT: 210028: 1f 20 03 d5 nop +# FORCE-NEXT: 21002c: 1f 20 03 d5 nop +# FORCE: 0000000000210030 func2@plt: +# FORCE-NEXT: 210030: 5f 24 03 d5 bti c +# FORCE-NEXT: 210034: 10 01 00 90 adrp x16, #131072 +# FORCE-NEXT: 210038: 11 0e 40 f9 ldr x17, [x16, #24] +# FORCE-NEXT: 21003c: 10 62 00 91 add x16, x16, #24 +# FORCE-NEXT: 210040: 20 02 1f d6 br x17 +# FORCE-NEXT: 210044: 1f 20 03 d5 nop + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 1 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI +.long 0 + +.text +.globl _start +.type func1,%function +func1: + bl func2 + ret Index: test/ELF/aarch64-feature-btipac.s =================================================================== --- /dev/null +++ test/ELF/aarch64-feature-btipac.s @@ -0,0 +1,141 @@ +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-btipac1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3-btipac.s -o %t3btipac.o + +# Build shared library with all inputs having BTI and PAC, expect PLT +# entries supporting both PAC and BTI. For a shared library this means: +# PLT[0] has bti c at start +# PLT[n] has autia1716 before br x17 + +# RUN: ld.lld %t1.o %t3btipac.o --shared -o %t.so +# RUN: llvm-readelf -n %t.so | FileCheck --check-prefix BTIPACPROP %s +# RUN: llvm-objdump -d -mattr=+v8.5a %t.so | FileCheck --check-prefix BTIPACSO %s +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix BTIPACDYN %s + +# BTIPACSO: Disassembly of section .text: +# BTIPACSO: 0000000000010000 func2: +# BTIPACSO-NEXT: 10000: 0c 00 00 94 bl #48 +# BTIPACSO-NEXT: 10004: c0 03 5f d6 ret +# BTIPACSO: 0000000000010008 func3: +# BTIPACSO-NEXT: 10008: c0 03 5f d6 ret +# BTIPACSO: Disassembly of section .plt: +# BTIPACSO: 0000000000010010 .plt: +# BTIPACSO-NEXT: 10010: 5f 24 03 d5 bti c +# BTIPACSO-NEXT: 10014: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# BTIPACSO-NEXT: 10018: 10 01 00 90 adrp x16, #131072 +# BTIPACSO-NEXT: 1001c: 11 0a 40 f9 ldr x17, [x16, #16] +# BTIPACSO-NEXT: 10020: 10 42 00 91 add x16, x16, #16 +# BTIPACSO-NEXT: 10024: 20 02 1f d6 br x17 +# BTIPACSO-NEXT: 10028: 1f 20 03 d5 nop +# BTIPACSO-NEXT: 1002c: 1f 20 03 d5 nop +# BTIPACSO: 0000000000010030 func3@plt: +# BTIPACSO-NEXT: 10030: 10 01 00 90 adrp x16, #131072 +# BTIPACSO-NEXT: 10034: 11 0e 40 f9 ldr x17, [x16, #24] +# BTIPACSO-NEXT: 10038: 10 62 00 91 add x16, x16, #24 +# BTIPACSO-NEXT: 1003c: 9f 21 03 d5 autia1716 +# BTIPACSO-NEXT: 10040: 20 02 1f d6 br x17 +# BTIPACSO-NEXT: 10044: 1f 20 03 d5 nop + +# BTIPACPROP: Properties: aarch64 feature: BTI, PAC + +# BTIPACDYN: 0x0000000070000001 (AARCH64_BTI_PLT) +# BTIPACDYN: 0x0000000070000003 (AARCH64_PAC_PLT) + +# Make an executable with both BTI and PAC properties. Expect: +# PLT[0] bti c as first instruction +# PLT[n] bti n as first instruction, autia1716 before br x17 + +# RUN: ld.lld %t.o %t3btipac.o %t.so -o %t.exe +# RUN: llvm-readelf -n %t.exe | FileCheck --check-prefix=BTIPACPROP %s +# RUN: llvm-objdump -d -mattr=+v8.5a %t.exe | FileCheck --check-prefix BTIPACEX %s +# RUN: llvm-readelf --dynamic-table %t.exe | FileCheck --check-prefix BTIPACDYN %s + +# BTIPACEX: Disassembly of section .text: +# BTIPACEX: 0000000000210000 func1: +# BTIPACEX-NEXT: 210000: 0c 00 00 94 bl #48 +# BTIPACEX-NEXT: 210004: c0 03 5f d6 ret +# BTIPACEX-NEXT: 210008: c0 03 5f d6 ret +# BTIPACEX: 000000000021000c func3: +# BTIPACEX-NEXT: 21000c: c0 03 5f d6 ret +# BTIPACEX: Disassembly of section .plt: +# BTIPACEX: 0000000000210010 .plt: +# BTIPACEX-NEXT: 210010: 5f 24 03 d5 bti c +# BTIPACEX-NEXT: 210014: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# BTIPACEX-NEXT: 210018: 10 01 00 90 adrp x16, #131072 +# BTIPACEX-NEXT: 21001c: 11 0a 40 f9 ldr x17, [x16, #16] +# BTIPACEX-NEXT: 210020: 10 42 00 91 add x16, x16, #16 +# BTIPACEX-NEXT: 210024: 20 02 1f d6 br x17 +# BTIPACEX-NEXT: 210028: 1f 20 03 d5 nop +# BTIPACEX-NEXT: 21002c: 1f 20 03 d5 nop +# BTIPACEX: 0000000000210030 func2@plt: +# BTIPACEX-NEXT: 210030: 5f 24 03 d5 bti c +# BTIPACEX-NEXT: 210034: 10 01 00 90 adrp x16, #131072 +# BTIPACEX-NEXT: 210038: 11 0e 40 f9 ldr x17, [x16, #24] +# BTIPACEX-NEXT: 21003c: 10 62 00 91 add x16, x16, #24 +# BTIPACEX-NEXT: 210040: 9f 21 03 d5 autia1716 +# BTIPACEX-NEXT: 210044: 20 02 1f d6 br x17 + +# Check that combinations of BTI+PAC with 0 properties results in standard PLT + +# RUN: ld.lld %t.o %t3.o %t.so -o %t.exe +# RUN: llvm-objdump -d -mattr=+v8.5a %t.exe | FileCheck --check-prefix EX %s +# RUN: llvm-readelf --dynamic-table %t.exe | FileCheck --check-prefix=NODYN %s + +# EX: Disassembly of section .text: +# EX: 0000000000210000 func1: +# EX-NEXT: 210000: 0c 00 00 94 bl #48 +# EX-NEXT: 210004: c0 03 5f d6 ret +# EX-NEXT: 210008: c0 03 5f d6 ret +# EX: 000000000021000c func3: +# EX-NEXT: 21000c: c0 03 5f d6 ret +# EX: Disassembly of section .plt: +# EX: 0000000000210010 .plt: +# EX-NEXT: 210010: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# EX-NEXT: 210014: 10 01 00 90 adrp x16, #131072 +# EX-NEXT: 210018: 11 0a 40 f9 ldr x17, [x16, #16] +# EX-NEXT: 21001c: 10 42 00 91 add x16, x16, #16 +# EX-NEXT: 210020: 20 02 1f d6 br x17 +# EX-NEXT: 210024: 1f 20 03 d5 nop +# EX-NEXT: 210028: 1f 20 03 d5 nop +# EX-NEXT: 21002c: 1f 20 03 d5 nop +# EX: 0000000000210030 func2@plt: +# EX: 210030: 10 01 00 90 adrp x16, #131072 +# EX-NEXT: 210034: 11 0e 40 f9 ldr x17, [x16, #24] +# EX-NEXT: 210038: 10 62 00 91 add x16, x16, #24 +# EX-NEXT: 21003c: 20 02 1f d6 br x17 + +# NODYN-NOT: 0x0000000070000001 (AARCH64_BTI_PLT) +# NODYN-NOT: 0x0000000070000003 (AARCH64_PAC_PLT) + +# Check that combination of --pac-plt and --force-bti warns for the file that +# doesn't contain the BTI property, but generates PAC and BTI PLT sequences. + +# RUN: ld.lld %t.o %t3.o %t.so --pac-plt --force-bti -o %t.exe 2>&1 | FileCheck --check-prefix=FORCE-WARN %s + +# FORCE-WARN: aarch64-feature-btipac.s.tmp3.o: --force-bti: file does not have BTI property + +# RUN: llvm-readelf -n %t.exe | FileCheck --check-prefix=BTIPACPROP %s +# RUN: llvm-objdump -d -mattr=+v8.5a %t.exe | FileCheck --check-prefix BTIPACEX %s +# RUN: llvm-readelf --dynamic-table %t.exe | FileCheck --check-prefix BTIPACDYN %s +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 3 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI and PAC +.long 0 + +.text +.globl _start +.type func1,%function +func1: + bl func2 + ret +.globl func3 +.type func3,%function + ret Index: test/ELF/aarch64-feature-pac.s =================================================================== --- /dev/null +++ test/ELF/aarch64-feature-pac.s @@ -0,0 +1,126 @@ +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-pac1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3-pac.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func2.s -o %tno.o + +# RUN ld.lld %tno.o %t3.o --shared -o %tno.so +# RUN llvm-objdump -d -mattr=+v8.3a %tno.so | FileCheck --check-prefix=NOBTI %s +# RUN llvm-objdump -s %tno.so | FileCheck --check-prefix SOGOTPLT %s +# RUN llvm-readelf --dynamic-table %tno.so | FileCheck --check-prefix NOPACDYN %s + +# NOPAC: 0000000000010000 func2: +# NOPAC-NEXT: 10000: 0c 00 00 94 bl #48 +# NOPAC-NEXT: 10004: c0 03 5f d6 ret +# NOPAC: Disassembly of section .plt: +# NOPAC: 0000000000010010 .plt: +# NOPAC-NEXT: 10010: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# NOPAC-NEXT: 10014: 10 01 00 90 adrp x16, #131072 +# NOPAC-NEXT: 10018: 11 0a 40 f9 ldr x17, [x16, #16] +# NOPAC-NEXT: 1001c: 10 42 00 91 add x16, x16, #16 +# NOPAC-NEXT: 10020: 20 02 1f d6 br x17 +# NOPAC-NEXT: 10024: 1f 20 03 d5 nop +# NOPAC-NEXT: 10028: 1f 20 03 d5 nop +# NOPAC-NEXT: 1002c: 1f 20 03 d5 nop +# NOPAC: 0000000000010030 func3@plt: +# NOPAC-NEXT: 10030: 10 01 00 90 adrp x16, #131072 +# NOPAC-NEXT: 10034: 11 0e 40 f9 ldr x17, [x16, #24] +# NOPAC-NEXT: 10038: 10 62 00 91 add x16, x16, #24 +# NOPAC-NEXT: 1003c: 20 02 1f d6 br x17 + +# NOPACDYN-NOT: 0x0000000070000001 (AARCH64_BTI_PLT) +# NOPACDYN-NOT: 0x0000000070000003 (AARCH64_PAC_PLT) + +# RUN: ld.lld %t1.o %t3.o --shared -o %t.so +# RUN: llvm-readelf -n %t.so | FileCheck --check-prefix PACPROP %s +# RUN: llvm-objdump -d -mattr=+v8.3a %t.so | FileCheck --check-prefix PACSO %s +# RUN: llvm-objdump -s %t.so | FileCheck --check-prefix SOGOTPLT %s +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix PACDYN %s + +# PAC has no effect on PLT[0], for PLT[N] autia1716 is used to authenticate +# the address in x17 (context in x16) before branching to it. The dynamic +# loader is responsible for calling pacia1716 on the entry. +# PACSO: 0000000000010000 func2: +# PACSO-NEXT: 10000: 0c 00 00 94 bl #48 +# PACSO-NEXT: 10004: c0 03 5f d6 ret +# PACSO: Disassembly of section .plt: +# PACSO: 0000000000010010 .plt: +# PACSO-NEXT: 10010: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# PACSO-NEXT: 10014: 10 01 00 90 adrp x16, #131072 +# PACSO-NEXT: 10018: 11 0a 40 f9 ldr x17, [x16, #16] +# PACSO-NEXT: 1001c: 10 42 00 91 add x16, x16, #16 +# PACSO-NEXT: 10020: 20 02 1f d6 br x17 +# PACSO-NEXT: 10024: 1f 20 03 d5 nop +# PACSO-NEXT: 10028: 1f 20 03 d5 nop +# PACSO-NEXT: 1002c: 1f 20 03 d5 nop +# PACSO: 0000000000010030 func3@plt: +# PACSO-NEXT: 10030: 10 01 00 90 adrp x16, #131072 +# PACSO-NEXT: 10034: 11 0e 40 f9 ldr x17, [x16, #24] +# PACSO-NEXT: 10038: 10 62 00 91 add x16, x16, #24 +# PACSO-NEXT: 1003c: 9f 21 03 d5 autia1716 +# PACSO-NEXT: 10040: 20 02 1f d6 br x17 +# PACSO-NEXT: 10044: 1f 20 03 d5 nop + +# The .got.plt should be identical between the PAC and no PAC DSO PLT. +# SOGOTPLT: Contents of section .got.plt: +# SOGOTPLT-NEXT: 30000 00000000 00000000 00000000 00000000 +# SOGOTPLT-NEXT: 30010 00000000 00000000 10000100 00000000 + +# PACPROP: Properties: aarch64 feature: PAC + +# PACDYN-NOT: 0x0000000070000001 (AARCH64_BTI_PLT) +# PACDYN: 0x0000000070000003 (AARCH64_PAC_PLT) + +# Turn on PAC entries with the --pac-plt command line option. There are no +# warnings in this case as the choice to use PAC in PLT entries is orthogonal +# to the choice of using PAC in relocatable objects. The presence of the PAC +# .note.gnu.property is an indication of preference by the relocatable object. + +# RUN: ld.lld %t.o %t2.o --pac-plt %t.so -o %tpacplt.exe +# RUN: llvm-readelf -n %tpacplt.exe | FileCheck --check-prefix=PACPROP %s +# RUN: llvm-readelf --dynamic-table %tpacplt.exe | FileCheck --check-prefix PACDYN %s +# RUN: llvm-objdump -d -mattr=+v8.3a %tpacplt.exe | FileCheck --check-prefix PACPLT %s + +# PACPLT: Disassembly of section .text: +# PACPLT: 0000000000210000 func1: +# PACPLT-NEXT: 210000: 0c 00 00 94 bl #48 +# PACPLT-NEXT: 210004: c0 03 5f d6 ret +# PACPLT: 0000000000210008 func3: +# PACPLT-NEXT: 210008: c0 03 5f d6 ret +# PACPLT: Disassembly of section .plt: +# PACPLT: 0000000000210010 .plt: +# PACPLT-NEXT: 210010: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# PACPLT-NEXT: 210014: 10 01 00 90 adrp x16, #131072 +# PACPLT-NEXT: 210018: 11 0a 40 f9 ldr x17, [x16, #16] +# PACPLT-NEXT: 21001c: 10 42 00 91 add x16, x16, #16 +# PACPLT-NEXT: 210020: 20 02 1f d6 br x17 +# PACPLT-NEXT: 210024: 1f 20 03 d5 nop +# PACPLT-NEXT: 210028: 1f 20 03 d5 nop +# PACPLT-NEXT: 21002c: 1f 20 03 d5 nop +# PACPLT: 0000000000210030 func2@plt: +# PACPLT-NEXT: 210030: 10 01 00 90 adrp x16, #131072 +# PACPLT-NEXT: 210034: 11 0e 40 f9 ldr x17, [x16, #24] +# PACPLT-NEXT: 210038: 10 62 00 91 add x16, x16, #24 +# PACPLT-NEXT: 21003c: 9f 21 03 d5 autia1716 +# PACPLT-NEXT: 210040: 20 02 1f d6 br x17 +# PACPLT-NEXT: 210044: 1f 20 03 d5 nop + + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 2 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC +.long 0 + +.text +.globl _start +.type func1,%function +func1: + bl func2 + ret Index: test/ELF/aarch64-ifunc-bti.s =================================================================== --- /dev/null +++ test/ELF/aarch64-ifunc-bti.s @@ -0,0 +1,65 @@ +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %p/Inputs/aarch64-addrifunc.s -o %lib.o + +# RUN: ld.lld --shared %lib.o -o %lib.so +# RUN: ld.lld --pie %lib.so %t.o -o %t +# RUN: llvm-objdump -d -mattr=+bti -triple=aarch64-linux-gnu %t | FileCheck %s + +# When the address of an ifunc is taken using a non-got reference which clang +# can do, LLD exports a canonical PLT entry that may have its address taken so +# we must use bti c. + +# CHECK: Disassembly of section .plt: +# CHECK: 0000000000010020 .plt: +# CHECK-NEXT: 10020: 5f 24 03 d5 bti c +# CHECK-NEXT: 10024: f0 7b bf a9 stp x16, x30, [sp, #-16]! +# CHECK-NEXT: 10028: 10 01 00 90 adrp x16, #131072 +# CHECK-NEXT: 1002c: 11 0a 40 f9 ldr x17, [x16, #16] +# CHECK-NEXT: 10030: 10 42 00 91 add x16, x16, #16 +# CHECK-NEXT: 10034: 20 02 1f d6 br x17 +# CHECK-NEXT: 10038: 1f 20 03 d5 nop +# CHECK-NEXT: 1003c: 1f 20 03 d5 nop +# CHECK: 0000000000010040 func1@plt: +# CHECK-NEXT: 10040: 5f 24 03 d5 bti c +# CHECK-NEXT: 10044: 10 01 00 90 adrp x16, #131072 +# CHECK-NEXT: 10048: 11 0e 40 f9 ldr x17, [x16, #24] +# CHECK-NEXT: 1004c: 10 62 00 91 add x16, x16, #24 +# CHECK-NEXT: 10050: 20 02 1f d6 br x17 +# CHECK-NEXT: 10054: 1f 20 03 d5 nop +# CHECK-NEXT: ... +# CHECK: 0000000000010060 myfunc: +# CHECK-NEXT: 10060: 5f 24 03 d5 bti c +# CHECK-NEXT: 10064: 10 01 00 90 adrp x16, #131072 +# CHECK-NEXT: 10068: 11 12 40 f9 ldr x17, [x16, #32] +# CHECK-NEXT: 1006c: 10 82 00 91 add x16, x16, #32 +# CHECK-NEXT: 10070: 20 02 1f d6 br x17 +# CHECK-NEXT: 10074: 1f 20 03 d5 nop + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 1 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI +.long 0 + +.text +.globl myfunc +.type myfunc,@gnu_indirect_function +myfunc: + ret + +.globl func1 + +.text +.globl _start +.type _start, %function +_start: + bl func1 + adrp x8, myfunc + add x8, x8, :lo12:myfunc + ret Index: test/ELF/aarch64-property-relocatable.s =================================================================== --- /dev/null +++ test/ELF/aarch64-property-relocatable.s @@ -0,0 +1,36 @@ +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t.o +# RUN: ld.lld -r %t.o -o %t2.o +# RUN: llvm-readelf -n %t2.o | FileCheck -match-full-lines %s + +# Test that .note.gnu.property is passed through -r, and that we can handle +# more than one FEATURE_AND in the same object file. This is logically the +# same as if the features were combined in a single FEATURE_AND as the rule +# states that the bit in the output pr_data field if it is set in all +.text +ret + +.section ".note.gnu.property", "a" +.p2align 3 +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 1 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI +.long 0 + +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 2 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC +.long 0 + +# CHECK: Owner Data size Description +# CHECK-NEXT: GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0 (property note) +# CHECK-NEXT: Properties: aarch64 feature: BTI, PAC Index: test/ELF/i386-cet.s =================================================================== --- test/ELF/i386-cet.s +++ test/ELF/i386-cet.s @@ -58,14 +58,13 @@ .section ".note.gnu.property", "a" .long 4 -.long 0x10 +.long 0xc .long 0x5 .asciz "GNU" .long 0xc0000002 .long 4 .long 3 -.long 0 .text .globl func1 Index: test/ELF/x86-property-relocatable.s =================================================================== --- /dev/null +++ test/ELF/x86-property-relocatable.s @@ -0,0 +1,36 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld -r %t.o -o %t2.o +# RUN: llvm-readelf -n %t2.o | FileCheck -match-full-lines %s + +# Test that .note.gnu.property is passed through -r, and that we can handle +# more than one FEATURE_AND in the same object file. This is logically the +# same as if the features were combined in a single FEATURE_AND as the rule +# states that the bit in the output pr_data field if it is set in all +.text +ret + +.section ".note.gnu.property", "a" +.p2align 3 +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000002 // GNU_PROPERTY_X86_FEATURE_1_AND +.long 4 +.long 1 // GNU_PROPERTY_X86_FEATURE_1_IBT +.long 0 + +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" +.long 0xc0000002 // GNU_PROPERTY_X86_FEATURE_1_AND +.long 4 +.long 2 // GNU_PROPERTY_X86_FEATURE_1_SHSTK +.long 0 + +# CHECK: Owner Data size Description +# CHECK-NEXT: GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0 (property note) +# CHECK-NEXT: Properties: x86 feature: IBT, SHSTK