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, @@ -431,7 +431,157 @@ 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: + bool BtiHeader; // bti instruction needed in PLT Header + bool BtiEntry; // bti instruction needed in PLT Entry + bool PacEntry; // autia1716 instruction needed in PLT Entry +}; +} // namespace + +AArch64BtiPac::AArch64BtiPac() { + BtiHeader = (Config->AndFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + // 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. This can happen when the address + // of the PLT entry for a function is canonicalised due to the address of + // the function in an executable being taken by a shared library. + // FIXME: There is a potential optimization to omit the BTI if we detect + // that the address of the PLT entry isn't taken. + BtiEntry = BtiHeader && !Config->Shared; + PacEntry = (Config->AndFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); + + if (BtiEntry || PacEntry) + PltEntrySize = 24; } + +void AArch64BtiPac::writePltHeader(uint8_t *Buf) const { + const uint8_t BtiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t PltData[] = { + 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 + }; + const uint8_t NopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + uint64_t Got = In.GotPlt->getVA(); + uint64_t Plt = In.Plt->getVA(); + + if (BtiHeader) { + // PltHeader is called indirectly by Plt[N]. Prefix PltData with a BTI C + // instruction. + memcpy(Buf, BtiData, sizeof(BtiData)); + Buf += sizeof(BtiData); + Plt += sizeof(BtiData); + } + memcpy(Buf, PltData, sizeof(PltData)); + + relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, + getAArch64Page(Got + 16) - getAArch64Page(Plt + 8)); + relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16); + relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16); + if (!BtiHeader) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(Buf + sizeof(PltData), NopData, sizeof(NopData)); +} + +void AArch64BtiPac::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + // The PLT entry is of the form: + // [BtiData] AddrInst (PacBr | StdBr) [NopData] + const uint8_t BtiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c + const uint8_t AddrInst[] = { + 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])) + }; + const uint8_t PacBr[] = { + 0x9f, 0x21, 0x03, 0xd5, // autia1716 + 0x20, 0x02, 0x1f, 0xd6 // br x17 + }; + const uint8_t StdBr[] = { + 0x20, 0x02, 0x1f, 0xd6, // br x17 + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + const uint8_t NopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop + + if (BtiEntry) { + memcpy(Buf, BtiData, sizeof(BtiData)); + Buf += sizeof(BtiData); + PltEntryAddr += sizeof(BtiData); + } + + memcpy(Buf, AddrInst, sizeof(AddrInst)); + 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); + + if (PacEntry) + memcpy(Buf + sizeof(AddrInst), PacBr, sizeof(PacBr)); + else + memcpy(Buf + sizeof(AddrInst), StdBr, sizeof(StdBr)); + if (!BtiEntry) + // We didn't add the BTI c instruction so round out size with NOP. + memcpy(Buf + sizeof(AddrInst) + sizeof(StdBr), NopData, sizeof(NopData)); +} + +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/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -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 @@ -337,6 +337,13 @@ if (Config->ZRetpolineplt && Config->RequireCET) error("--require-cet may not be used with -z retpolineplt"); + + if (Config->EMachine != EM_AARCH64) { + if (Config->PacPlt) + error("--pac-plt only supported on AArch64"); + if (Config->ForceBTI) + error("--force-bti only supported on AArch64"); + } } static const char *getReproduceOption(opt::InputArgList &Args) { @@ -816,6 +823,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 +859,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); @@ -1594,20 +1603,32 @@ // with CET. // // This function returns the merged feature flags. If 0, we cannot enable CET. +// This is also the case with AARCH64's BTI and PAC which use the similar +// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism. // // Note that the CET-aware PLT is not implemented yet. We do error // check only. template static uint32_t getAndFeatures() { - if (Config->EMachine != EM_386 && Config->EMachine != EM_X86_64) + 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)->AndFeatures; - if (!Features && Config->RequireCET) + if (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->PacPlt) + Ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; + return Ret; } @@ -1793,6 +1814,11 @@ // contain a hint to tweak linker's and loader's behaviors. Config->AndFeatures = getAndFeatures(); + // The Target instance handles target-specific stuff, such as applying + // relocations or writing a PLT section. It also contains target-dependent + // values such as a default image base address. + Target = getTarget(); + Config->EFlags = Target->calcEFlags(); // MaxPageSize (sometimes called abi page size) is the maximum page size that // the output can be run on. For example if the OS can use 4k or 64k page Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -787,6 +787,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,7 +800,7 @@ uint32_t Type = read32le(Desc.data()); uint32_t Size = read32le(Desc.data() + 4); - if (Type == GNU_PROPERTY_X86_FEATURE_1_AND) { + 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. Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -175,6 +175,9 @@ // is not complete. def require_cet: F<"require-cet">; +def force_bti: F<"force-bti">, + HelpText<"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property">; + defm format: Eq<"format", "Change the input format of the inputs following this option">, MetaVarName<"[default,elf,binary]">; @@ -269,6 +272,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/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 are using. 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,11 +305,15 @@ ".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->AndFeatures); // Feature flags if (Config->Is64) @@ -1335,6 +1340,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); Index: docs/ld.lld.1 =================================================================== --- docs/ld.lld.1 +++ docs/ld.lld.1 @@ -182,6 +182,8 @@ field to the specified value. .It Fl -fini Ns = Ns Ar symbol Specify a finalizer function. +.It Fl -force-bti +Force enable AArch64 BTI instruction in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_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 @@ -382,6 +384,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-bti-pac-cli-error.s =================================================================== --- /dev/null +++ test/ELF/aarch64-bti-pac-cli-error.s @@ -0,0 +1,12 @@ +# REQUIRES: x86 +# RUN: llvm-mc --triple=x86_64-pc-linux --filetype=obj -o %t.o %s +# RUN: not ld.lld --pac-plt --force-bti %t.o -o %t 2>&1 | FileCheck %s +# +## Check that we error if --pac-plt and --force-bti are used when target is not +## aarch64 + +# CHECK: error: --pac-plt only supported on AArch64 +# CHECK-NEXT: error: --force-bti only supported on AArch64 + + .globl start +start: ret Index: test/ELF/aarch64-feature-bti.s =================================================================== --- /dev/null +++ test/ELF/aarch64-feature-bti.s @@ -0,0 +1,218 @@ +# 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 --no-show-raw-insn %tno.so | FileCheck --check-prefix=NOBTI %s +# RUN: llvm-readelf -x .got.plt %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: bl #48 +# NOBTI-NEXT: 10004: ret +# NOBTI: Disassembly of section .plt: +# NOBTI: 0000000000010010 .plt: +# NOBTI-NEXT: 10010: stp x16, x30, [sp, #-16]! +# NOBTI-NEXT: 10014: adrp x16, #131072 +# NOBTI-NEXT: 10018: ldr x17, [x16, #16] +# NOBTI-NEXT: 1001c: add x16, x16, #16 +# NOBTI-NEXT: 10020: br x17 +# NOBTI-NEXT: 10024: nop +# NOBTI-NEXT: 10028: nop +# NOBTI-NEXT: 1002c: nop +# NOBTI: 0000000000010030 func3@plt: +# NOBTI-NEXT: 10030: adrp x16, #131072 +# NOBTI-NEXT: 10034: ldr x17, [x16, #24] +# NOBTI-NEXT: 10038: add x16, x16, #24 +# NOBTI-NEXT: 1003c: 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 the shared object, 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 --no-show-raw-insn %t.so | FileCheck --check-prefix BTISO %s +# RUN: llvm-readelf -x .got.plt %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: bl #48 +# BTISO-NEXT: 10004: ret +# BTISO: Disassembly of section .plt: +# BTISO: 0000000000010010 .plt: +# BTISO-NEXT: 10010: bti c +# BTISO-NEXT: 10014: stp x16, x30, [sp, #-16]! +# BTISO-NEXT: 10018: adrp x16, #131072 +# BTISO-NEXT: 1001c: ldr x17, [x16, #16] +# BTISO-NEXT: 10020: add x16, x16, #16 +# BTISO-NEXT: 10024: br x17 +# BTISO-NEXT: 10028: nop +# BTISO-NEXT: 1002c: nop +# BTISO: 0000000000010030 func3@plt: +# BTISO-NEXT: 10030: adrp x16, #131072 +# BTISO-NEXT: 10034: ldr x17, [x16, #24] +# BTISO-NEXT: 10038: add x16, x16, #24 +# BTISO-NEXT: 1003c: br x17 + +## The .got.plt should be identical between the BTI and no BTI DSO PLT. +# SOGOTPLT: Hex dump of section '.got.plt' +# SOGOTPLT-NEXT: 0x00030000 00000000 00000000 00000000 00000000 +# SOGOTPLT-NEXT: 0x00030010 00000000 00000000 10000100 00000000 + +## Build an executable with all relocatable inputs having the BTI +## .note.gnu.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 --no-show-raw-insn %t.exe | FileCheck --check-prefix=EXECBTI %s + +# EXECBTI: Disassembly of section .text: +# EXECBTI: 0000000000210000 func1: +# EXECBTI-NEXT: 210000: bl #48 +# EXECBTI-NEXT: 210004: ret +# EXECBTI: Disassembly of section .plt: +# EXECBTI: 0000000000210010 .plt: +# EXECBTI-NEXT: 210010: bti c +# EXECBTI-NEXT: 210014: stp x16, x30, [sp, #-16]! +# EXECBTI-NEXT: 210018: adrp x16, #131072 +# EXECBTI-NEXT: 21001c: ldr x17, [x16, #16] +# EXECBTI-NEXT: 210020: add x16, x16, #16 +# EXECBTI-NEXT: 210024: br x17 +# EXECBTI-NEXT: 210028: nop +# EXECBTI-NEXT: 21002c: nop +# EXECBTI: 0000000000210030 func2@plt: +# EXECBTI-NEXT: 210030: bti c +# EXECBTI-NEXT: 210034: adrp x16, #131072 +# EXECBTI-NEXT: 210038: ldr x17, [x16, #24] +# EXECBTI-NEXT: 21003c: add x16, x16, #24 +# EXECBTI-NEXT: 210040: br x17 +# EXECBTI-NEXT: 210044: 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 --no-show-raw-insn %tpie.exe | FileCheck --check-prefix=PIE %s + +# PIE: Disassembly of section .text: +# PIE: 0000000000010000 func1: +# PIE-NEXT: 10000: bl #48 +# PIE-NEXT: 10004: ret +# PIE: Disassembly of section .plt: +# PIE: 0000000000010010 .plt: +# PIE-NEXT: 10010: bti c +# PIE-NEXT: 10014: stp x16, x30, [sp, #-16]! +# PIE-NEXT: 10018: adrp x16, #131072 +# PIE-NEXT: 1001c: ldr x17, [x16, #16] +# PIE-NEXT: 10020: add x16, x16, #16 +# PIE-NEXT: 10024: br x17 +# PIE-NEXT: 10028: nop +# PIE-NEXT: 1002c: nop +# PIE: 0000000000010030 func2@plt: +# PIE-NEXT: 10030: bti c +# PIE-NEXT: 10034: adrp x16, #131072 +# PIE-NEXT: 10038: ldr x17, [x16, #24] +# PIE-NEXT: 1003c: add x16, x16, #24 +# PIE-NEXT: 10040: br x17 +# PIE-NEXT: 10044: 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 --no-show-raw-insn %tnobti.exe | FileCheck --check-prefix=NOEX %s + +# NOEX: Disassembly of section .text: +# NOEX: 0000000000210000 func1: +# NOEX-NEXT: 210000: bl #48 +# NOEX-NEXT: 210004: ret +# NOEX: 0000000000210008 func3: +# NOEX-NEXT: 210008: ret +# NOEX: Disassembly of section .plt: +# NOEX: 0000000000210010 .plt: +# NOEX-NEXT: 210010: stp x16, x30, [sp, #-16]! +# NOEX-NEXT: 210014: adrp x16, #131072 +# NOEX-NEXT: 210018: ldr x17, [x16, #16] +# NOEX-NEXT: 21001c: add x16, x16, #16 +# NOEX-NEXT: 210020: br x17 +# NOEX-NEXT: 210024: nop +# NOEX-NEXT: 210028: nop +# NOEX-NEXT: 21002c: nop +# NOEX: 0000000000210030 func2@plt: +# NOEX-NEXT: 210030: adrp x16, #131072 +# NOEX-NEXT: 210034: ldr x17, [x16, #24] +# NOEX-NEXT: 210038: add x16, x16, #24 +# NOEX-NEXT: 21003c: 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 --no-show-raw-insn %tforcebti.exe | FileCheck --check-prefix=FORCE %s + +# FORCE: Disassembly of section .text: +# FORCE: 0000000000210000 func1: +# FORCE-NEXT: 210000: bl #48 +# FORCE-NEXT: 210004: ret +# FORCE: 0000000000210008 func3: +# FORCE-NEXT: 210008: ret +# FORCE: Disassembly of section .plt: +# FORCE: 0000000000210010 .plt: +# FORCE-NEXT: 210010: bti c +# FORCE-NEXT: 210014: stp x16, x30, [sp, #-16]! +# FORCE-NEXT: 210018: adrp x16, #131072 +# FORCE-NEXT: 21001c: ldr x17, [x16, #16] +# FORCE-NEXT: 210020: add x16, x16, #16 +# FORCE-NEXT: 210024: br x17 +# FORCE-NEXT: 210028: nop +# FORCE-NEXT: 21002c: nop +# FORCE: 0000000000210030 func2@plt: +# FORCE-NEXT: 210030: bti c +# FORCE-NEXT: 210034: adrp x16, #131072 +# FORCE-NEXT: 210038: ldr x17, [x16, #24] +# FORCE-NEXT: 21003c: add x16, x16, #24 +# FORCE-NEXT: 210040: br x17 +# FORCE-NEXT: 210044: 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,142 @@ +# 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 --no-show-raw-insn %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: bl #48 +# BTIPACSO-NEXT: 10004: ret +# BTIPACSO: 0000000000010008 func3: +# BTIPACSO-NEXT: 10008: ret +# BTIPACSO: Disassembly of section .plt: +# BTIPACSO: 0000000000010010 .plt: +# BTIPACSO-NEXT: 10010: bti c +# BTIPACSO-NEXT: 10014: stp x16, x30, [sp, #-16]! +# BTIPACSO-NEXT: 10018: adrp x16, #131072 +# BTIPACSO-NEXT: 1001c: ldr x17, [x16, #16] +# BTIPACSO-NEXT: 10020: add x16, x16, #16 +# BTIPACSO-NEXT: 10024: br x17 +# BTIPACSO-NEXT: 10028: nop +# BTIPACSO-NEXT: 1002c: nop +# BTIPACSO: 0000000000010030 func3@plt: +# BTIPACSO-NEXT: 10030: adrp x16, #131072 +# BTIPACSO-NEXT: 10034: ldr x17, [x16, #24] +# BTIPACSO-NEXT: 10038: add x16, x16, #24 +# BTIPACSO-NEXT: 1003c: autia1716 +# BTIPACSO-NEXT: 10040: br x17 +# BTIPACSO-NEXT: 10044: 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 --no-show-raw-insn %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: bl #48 +# BTIPACEX-NEXT: 210004: ret +# BTIPACEX-NEXT: 210008: ret +# BTIPACEX: 000000000021000c func3: +# BTIPACEX-NEXT: 21000c: ret +# BTIPACEX: Disassembly of section .plt: +# BTIPACEX: 0000000000210010 .plt: +# BTIPACEX-NEXT: 210010: bti c +# BTIPACEX-NEXT: 210014: stp x16, x30, [sp, #-16]! +# BTIPACEX-NEXT: 210018: adrp x16, #131072 +# BTIPACEX-NEXT: 21001c: ldr x17, [x16, #16] +# BTIPACEX-NEXT: 210020: add x16, x16, #16 +# BTIPACEX-NEXT: 210024: br x17 +# BTIPACEX-NEXT: 210028: nop +# BTIPACEX-NEXT: 21002c: nop +# BTIPACEX: 0000000000210030 func2@plt: +# BTIPACEX-NEXT: 210030: bti c +# BTIPACEX-NEXT: 210034: adrp x16, #131072 +# BTIPACEX-NEXT: 210038: ldr x17, [x16, #24] +# BTIPACEX-NEXT: 21003c: add x16, x16, #24 +# BTIPACEX-NEXT: 210040: autia1716 +# BTIPACEX-NEXT: 210044: 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 --no-show-raw-insn %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: bl #48 +# EX-NEXT: 210004: ret +# EX-NEXT: 210008: ret +# EX: 000000000021000c func3: +# EX-NEXT: 21000c: ret +# EX: Disassembly of section .plt: +# EX: 0000000000210010 .plt: +# EX-NEXT: 210010: stp x16, x30, [sp, #-16]! +# EX-NEXT: 210014: adrp x16, #131072 +# EX-NEXT: 210018: ldr x17, [x16, #16] +# EX-NEXT: 21001c: add x16, x16, #16 +# EX-NEXT: 210020: br x17 +# EX-NEXT: 210024: nop +# EX-NEXT: 210028: nop +# EX-NEXT: 21002c: nop +# EX: 0000000000210030 func2@plt: +# EX: 210030: adrp x16, #131072 +# EX-NEXT: 210034: ldr x17, [x16, #24] +# EX-NEXT: 210038: add x16, x16, #24 +# EX-NEXT: 21003c: 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. +## The --pac-plt doesn't warn as it is not required for correctness. + +# 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 --no-show-raw-insn %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,129 @@ +# 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 + +## We do not add PAC 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=+v8.3a --no-show-raw-insn %tno.so | FileCheck --check-prefix=NOPAC %s +# RUN: llvm-readelf -x .got.plt %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: bl #48 +# NOPAC-NEXT: 10004: ret +# NOPAC: Disassembly of section .plt: +# NOPAC: 0000000000010010 .plt: +# NOPAC-NEXT: 10010: stp x16, x30, [sp, #-16]! +# NOPAC-NEXT: 10014: adrp x16, #131072 +# NOPAC-NEXT: 10018: ldr x17, [x16, #16] +# NOPAC-NEXT: 1001c: add x16, x16, #16 +# NOPAC-NEXT: 10020: br x17 +# NOPAC-NEXT: 10024: nop +# NOPAC-NEXT: 10028: nop +# NOPAC-NEXT: 1002c: nop +# NOPAC: 0000000000010030 func3@plt: +# NOPAC-NEXT: 10030: adrp x16, #131072 +# NOPAC-NEXT: 10034: ldr x17, [x16, #24] +# NOPAC-NEXT: 10038: add x16, x16, #24 +# NOPAC-NEXT: 1003c: 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 --no-show-raw-insn %t.so | FileCheck --check-prefix PACSO %s +# RUN: llvm-readelf -x .got.plt %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: bl #48 +# PACSO-NEXT: 10004: ret +# PACSO: Disassembly of section .plt: +# PACSO: 0000000000010010 .plt: +# PACSO-NEXT: 10010: stp x16, x30, [sp, #-16]! +# PACSO-NEXT: 10014: adrp x16, #131072 +# PACSO-NEXT: 10018: ldr x17, [x16, #16] +# PACSO-NEXT: 1001c: add x16, x16, #16 +# PACSO-NEXT: 10020: br x17 +# PACSO-NEXT: 10024: nop +# PACSO-NEXT: 10028: nop +# PACSO-NEXT: 1002c: nop +# PACSO: 0000000000010030 func3@plt: +# PACSO-NEXT: 10030: adrp x16, #131072 +# PACSO-NEXT: 10034: ldr x17, [x16, #24] +# PACSO-NEXT: 10038: add x16, x16, #24 +# PACSO-NEXT: 1003c: autia1716 +# PACSO-NEXT: 10040: br x17 +# PACSO-NEXT: 10044: nop + +# The .got.plt should be identical between the PAC and no PAC DSO PLT. +# SOGOTPLT: Hex dump of section '.got.plt': +# SOGOTPLT-NEXT: 0x00030000 00000000 00000000 00000000 00000000 +# SOGOTPLT-NEXT: 0x00030010 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 --no-show-raw-insn %tpacplt.exe | FileCheck --check-prefix PACPLT %s + +# PACPLT: Disassembly of section .text: +# PACPLT: 0000000000210000 func1: +# PACPLT-NEXT: 210000: bl #48 +# PACPLT-NEXT: 210004: ret +# PACPLT: 0000000000210008 func3: +# PACPLT-NEXT: 210008: ret +# PACPLT: Disassembly of section .plt: +# PACPLT: 0000000000210010 .plt: +# PACPLT-NEXT: 210010: stp x16, x30, [sp, #-16]! +# PACPLT-NEXT: 210014: adrp x16, #131072 +# PACPLT-NEXT: 210018: ldr x17, [x16, #16] +# PACPLT-NEXT: 21001c: add x16, x16, #16 +# PACPLT-NEXT: 210020: br x17 +# PACPLT-NEXT: 210024: nop +# PACPLT-NEXT: 210028: nop +# PACPLT-NEXT: 21002c: nop +# PACPLT: 0000000000210030 func2@plt: +# PACPLT-NEXT: 210030: adrp x16, #131072 +# PACPLT-NEXT: 210034: ldr x17, [x16, #24] +# PACPLT-NEXT: 210038: add x16, x16, #24 +# PACPLT-NEXT: 21003c: autia1716 +# PACPLT-NEXT: 210040: br x17 +# PACPLT-NEXT: 210044: 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