15f757f3fSDimitry Andric //===-- AArch64PointerAuth.cpp -- Harden code using PAuth ------------------==//
25f757f3fSDimitry Andric //
35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65f757f3fSDimitry Andric //
75f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
85f757f3fSDimitry Andric
95f757f3fSDimitry Andric #include "AArch64PointerAuth.h"
105f757f3fSDimitry Andric
115f757f3fSDimitry Andric #include "AArch64.h"
125f757f3fSDimitry Andric #include "AArch64InstrInfo.h"
135f757f3fSDimitry Andric #include "AArch64MachineFunctionInfo.h"
145f757f3fSDimitry Andric #include "AArch64Subtarget.h"
150fca6ea1SDimitry Andric #include "Utils/AArch64BaseInfo.h"
165f757f3fSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
175f757f3fSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
185f757f3fSDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h"
195f757f3fSDimitry Andric
205f757f3fSDimitry Andric using namespace llvm;
215f757f3fSDimitry Andric using namespace llvm::AArch64PAuth;
225f757f3fSDimitry Andric
235f757f3fSDimitry Andric #define AARCH64_POINTER_AUTH_NAME "AArch64 Pointer Authentication"
245f757f3fSDimitry Andric
255f757f3fSDimitry Andric namespace {
265f757f3fSDimitry Andric
275f757f3fSDimitry Andric class AArch64PointerAuth : public MachineFunctionPass {
285f757f3fSDimitry Andric public:
295f757f3fSDimitry Andric static char ID;
305f757f3fSDimitry Andric
AArch64PointerAuth()315f757f3fSDimitry Andric AArch64PointerAuth() : MachineFunctionPass(ID) {}
325f757f3fSDimitry Andric
335f757f3fSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override;
345f757f3fSDimitry Andric
getPassName() const355f757f3fSDimitry Andric StringRef getPassName() const override { return AARCH64_POINTER_AUTH_NAME; }
365f757f3fSDimitry Andric
375f757f3fSDimitry Andric private:
385f757f3fSDimitry Andric /// An immediate operand passed to BRK instruction, if it is ever emitted.
BrkOperandForKey(AArch64PACKey::ID KeyId)390fca6ea1SDimitry Andric static unsigned BrkOperandForKey(AArch64PACKey::ID KeyId) {
400fca6ea1SDimitry Andric const unsigned BrkOperandBase = 0xc470;
410fca6ea1SDimitry Andric return BrkOperandBase + KeyId;
420fca6ea1SDimitry Andric }
435f757f3fSDimitry Andric
445f757f3fSDimitry Andric const AArch64Subtarget *Subtarget = nullptr;
455f757f3fSDimitry Andric const AArch64InstrInfo *TII = nullptr;
465f757f3fSDimitry Andric const AArch64RegisterInfo *TRI = nullptr;
475f757f3fSDimitry Andric
485f757f3fSDimitry Andric void signLR(MachineFunction &MF, MachineBasicBlock::iterator MBBI) const;
495f757f3fSDimitry Andric
505f757f3fSDimitry Andric void authenticateLR(MachineFunction &MF,
515f757f3fSDimitry Andric MachineBasicBlock::iterator MBBI) const;
525f757f3fSDimitry Andric
530fca6ea1SDimitry Andric /// Stores blend(AddrDisc, IntDisc) to the Result register.
540fca6ea1SDimitry Andric void emitBlend(MachineBasicBlock::iterator MBBI, Register Result,
550fca6ea1SDimitry Andric Register AddrDisc, unsigned IntDisc) const;
560fca6ea1SDimitry Andric
570fca6ea1SDimitry Andric /// Expands PAUTH_BLEND pseudo instruction.
580fca6ea1SDimitry Andric void expandPAuthBlend(MachineBasicBlock::iterator MBBI) const;
590fca6ea1SDimitry Andric
605f757f3fSDimitry Andric bool checkAuthenticatedLR(MachineBasicBlock::iterator TI) const;
615f757f3fSDimitry Andric };
625f757f3fSDimitry Andric
635f757f3fSDimitry Andric } // end anonymous namespace
645f757f3fSDimitry Andric
655f757f3fSDimitry Andric INITIALIZE_PASS(AArch64PointerAuth, "aarch64-ptrauth",
665f757f3fSDimitry Andric AARCH64_POINTER_AUTH_NAME, false, false)
675f757f3fSDimitry Andric
createAArch64PointerAuthPass()685f757f3fSDimitry Andric FunctionPass *llvm::createAArch64PointerAuthPass() {
695f757f3fSDimitry Andric return new AArch64PointerAuth();
705f757f3fSDimitry Andric }
715f757f3fSDimitry Andric
725f757f3fSDimitry Andric char AArch64PointerAuth::ID = 0;
735f757f3fSDimitry Andric
74cb14a3feSDimitry Andric // Where PAuthLR support is not known at compile time, it is supported using
75cb14a3feSDimitry Andric // PACM. PACM is in the hint space so has no effect when PAuthLR is not
76cb14a3feSDimitry Andric // supported by the hardware, but will alter the behaviour of PACI*SP, AUTI*SP
77cb14a3feSDimitry Andric // and RETAA/RETAB if the hardware supports PAuthLR.
BuildPACM(const AArch64Subtarget & Subtarget,MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,DebugLoc DL,MachineInstr::MIFlag Flags,MCSymbol * PACSym=nullptr)78cb14a3feSDimitry Andric static void BuildPACM(const AArch64Subtarget &Subtarget, MachineBasicBlock &MBB,
79cb14a3feSDimitry Andric MachineBasicBlock::iterator MBBI, DebugLoc DL,
80cb14a3feSDimitry Andric MachineInstr::MIFlag Flags, MCSymbol *PACSym = nullptr) {
81cb14a3feSDimitry Andric const TargetInstrInfo *TII = Subtarget.getInstrInfo();
82cb14a3feSDimitry Andric auto &MFnI = *MBB.getParent()->getInfo<AArch64FunctionInfo>();
83cb14a3feSDimitry Andric
84cb14a3feSDimitry Andric // ADR X16,<address_of_PACIASP>
85cb14a3feSDimitry Andric if (PACSym) {
86cb14a3feSDimitry Andric assert(Flags == MachineInstr::FrameDestroy);
87cb14a3feSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADR))
88cb14a3feSDimitry Andric .addReg(AArch64::X16, RegState::Define)
89cb14a3feSDimitry Andric .addSym(PACSym);
90cb14a3feSDimitry Andric }
91cb14a3feSDimitry Andric
92cb14a3feSDimitry Andric // Only emit PACM if -mbranch-protection has +pc and the target does not
93cb14a3feSDimitry Andric // have feature +pauth-lr.
94cb14a3feSDimitry Andric if (MFnI.branchProtectionPAuthLR() && !Subtarget.hasPAuthLR())
95cb14a3feSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACM)).setMIFlag(Flags);
96cb14a3feSDimitry Andric }
97cb14a3feSDimitry Andric
signLR(MachineFunction & MF,MachineBasicBlock::iterator MBBI) const985f757f3fSDimitry Andric void AArch64PointerAuth::signLR(MachineFunction &MF,
995f757f3fSDimitry Andric MachineBasicBlock::iterator MBBI) const {
100cb14a3feSDimitry Andric auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
101cb14a3feSDimitry Andric bool UseBKey = MFnI.shouldSignWithBKey();
102cb14a3feSDimitry Andric bool EmitCFI = MFnI.needsDwarfUnwindInfo(MF);
1030fca6ea1SDimitry Andric bool EmitAsyncCFI = MFnI.needsAsyncDwarfUnwindInfo(MF);
1045f757f3fSDimitry Andric bool NeedsWinCFI = MF.hasWinCFI();
1055f757f3fSDimitry Andric
1065f757f3fSDimitry Andric MachineBasicBlock &MBB = *MBBI->getParent();
1075f757f3fSDimitry Andric
1085f757f3fSDimitry Andric // Debug location must be unknown, see AArch64FrameLowering::emitPrologue.
1095f757f3fSDimitry Andric DebugLoc DL;
1105f757f3fSDimitry Andric
1115f757f3fSDimitry Andric if (UseBKey) {
1125f757f3fSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY))
1135f757f3fSDimitry Andric .setMIFlag(MachineInstr::FrameSetup);
1145f757f3fSDimitry Andric }
1155f757f3fSDimitry Andric
116cb14a3feSDimitry Andric // PAuthLR authentication instructions need to know the value of PC at the
117cb14a3feSDimitry Andric // point of signing (PACI*).
118cb14a3feSDimitry Andric if (MFnI.branchProtectionPAuthLR()) {
1190fca6ea1SDimitry Andric MCSymbol *PACSym = MF.getContext().createTempSymbol();
120cb14a3feSDimitry Andric MFnI.setSigningInstrLabel(PACSym);
121cb14a3feSDimitry Andric }
122cb14a3feSDimitry Andric
1235f757f3fSDimitry Andric // No SEH opcode for this one; it doesn't materialize into an
1245f757f3fSDimitry Andric // instruction on Windows.
125cb14a3feSDimitry Andric if (MFnI.branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
1265f757f3fSDimitry Andric BuildMI(MBB, MBBI, DL,
127cb14a3feSDimitry Andric TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSPPC
128cb14a3feSDimitry Andric : AArch64::PACIASPPC))
129cb14a3feSDimitry Andric .setMIFlag(MachineInstr::FrameSetup)
130cb14a3feSDimitry Andric ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
131cb14a3feSDimitry Andric } else {
132cb14a3feSDimitry Andric BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameSetup);
133cb14a3feSDimitry Andric BuildMI(MBB, MBBI, DL,
134cb14a3feSDimitry Andric TII->get(MFnI.shouldSignWithBKey() ? AArch64::PACIBSP
135cb14a3feSDimitry Andric : AArch64::PACIASP))
136cb14a3feSDimitry Andric .setMIFlag(MachineInstr::FrameSetup)
137cb14a3feSDimitry Andric ->setPreInstrSymbol(MF, MFnI.getSigningInstrLabel());
138cb14a3feSDimitry Andric }
1395f757f3fSDimitry Andric
1405f757f3fSDimitry Andric if (EmitCFI) {
1410fca6ea1SDimitry Andric if (!EmitAsyncCFI) {
1420fca6ea1SDimitry Andric // Reduce the size of the generated call frame information for synchronous
1430fca6ea1SDimitry Andric // CFI by bundling the new CFI instruction with others in the prolog, so
1440fca6ea1SDimitry Andric // that no additional DW_CFA_advance_loc is needed.
1450fca6ea1SDimitry Andric for (auto I = MBBI; I != MBB.end(); ++I) {
1460fca6ea1SDimitry Andric if (I->getOpcode() == TargetOpcode::CFI_INSTRUCTION &&
1470fca6ea1SDimitry Andric I->getFlag(MachineInstr::FrameSetup)) {
1480fca6ea1SDimitry Andric MBBI = I;
1490fca6ea1SDimitry Andric break;
1500fca6ea1SDimitry Andric }
1510fca6ea1SDimitry Andric }
1520fca6ea1SDimitry Andric }
1535f757f3fSDimitry Andric unsigned CFIIndex =
1545f757f3fSDimitry Andric MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr));
1555f757f3fSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
1565f757f3fSDimitry Andric .addCFIIndex(CFIIndex)
1575f757f3fSDimitry Andric .setMIFlags(MachineInstr::FrameSetup);
1585f757f3fSDimitry Andric } else if (NeedsWinCFI) {
1595f757f3fSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
1605f757f3fSDimitry Andric .setMIFlag(MachineInstr::FrameSetup);
1615f757f3fSDimitry Andric }
1625f757f3fSDimitry Andric }
1635f757f3fSDimitry Andric
authenticateLR(MachineFunction & MF,MachineBasicBlock::iterator MBBI) const1645f757f3fSDimitry Andric void AArch64PointerAuth::authenticateLR(
1655f757f3fSDimitry Andric MachineFunction &MF, MachineBasicBlock::iterator MBBI) const {
1665f757f3fSDimitry Andric const AArch64FunctionInfo *MFnI = MF.getInfo<AArch64FunctionInfo>();
1675f757f3fSDimitry Andric bool UseBKey = MFnI->shouldSignWithBKey();
1685f757f3fSDimitry Andric bool EmitAsyncCFI = MFnI->needsAsyncDwarfUnwindInfo(MF);
1695f757f3fSDimitry Andric bool NeedsWinCFI = MF.hasWinCFI();
1705f757f3fSDimitry Andric
1715f757f3fSDimitry Andric MachineBasicBlock &MBB = *MBBI->getParent();
1725f757f3fSDimitry Andric DebugLoc DL = MBBI->getDebugLoc();
1735f757f3fSDimitry Andric // MBBI points to a PAUTH_EPILOGUE instruction to be replaced and
1745f757f3fSDimitry Andric // TI points to a terminator instruction that may or may not be combined.
1755f757f3fSDimitry Andric // Note that inserting new instructions "before MBBI" and "before TI" is
1765f757f3fSDimitry Andric // not the same because if ShadowCallStack is enabled, its instructions
1775f757f3fSDimitry Andric // are placed between MBBI and TI.
1785f757f3fSDimitry Andric MachineBasicBlock::iterator TI = MBB.getFirstInstrTerminator();
1795f757f3fSDimitry Andric
1805f757f3fSDimitry Andric // The AUTIASP instruction assembles to a hint instruction before v8.3a so
1815f757f3fSDimitry Andric // this instruction can safely used for any v8a architecture.
1825f757f3fSDimitry Andric // From v8.3a onwards there are optimised authenticate LR and return
1835f757f3fSDimitry Andric // instructions, namely RETA{A,B}, that can be used instead. In this case the
1845f757f3fSDimitry Andric // DW_CFA_AARCH64_negate_ra_state can't be emitted.
1855f757f3fSDimitry Andric bool TerminatorIsCombinable =
1865f757f3fSDimitry Andric TI != MBB.end() && TI->getOpcode() == AArch64::RET;
187cb14a3feSDimitry Andric MCSymbol *PACSym = MFnI->getSigningInstrLabel();
188cb14a3feSDimitry Andric
1895f757f3fSDimitry Andric if (Subtarget->hasPAuth() && TerminatorIsCombinable && !NeedsWinCFI &&
1905f757f3fSDimitry Andric !MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) {
191cb14a3feSDimitry Andric if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
192cb14a3feSDimitry Andric assert(PACSym && "No PAC instruction to refer to");
193cb14a3feSDimitry Andric BuildMI(MBB, TI, DL,
194cb14a3feSDimitry Andric TII->get(UseBKey ? AArch64::RETABSPPCi : AArch64::RETAASPPCi))
195cb14a3feSDimitry Andric .addSym(PACSym)
196cb14a3feSDimitry Andric .copyImplicitOps(*MBBI)
197cb14a3feSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy);
198cb14a3feSDimitry Andric } else {
199cb14a3feSDimitry Andric BuildPACM(*Subtarget, MBB, TI, DL, MachineInstr::FrameDestroy, PACSym);
200cb14a3feSDimitry Andric BuildMI(MBB, TI, DL, TII->get(UseBKey ? AArch64::RETAB : AArch64::RETAA))
201cb14a3feSDimitry Andric .copyImplicitOps(*MBBI)
202cb14a3feSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy);
203cb14a3feSDimitry Andric }
2045f757f3fSDimitry Andric MBB.erase(TI);
2055f757f3fSDimitry Andric } else {
206cb14a3feSDimitry Andric if (MFnI->branchProtectionPAuthLR() && Subtarget->hasPAuthLR()) {
207cb14a3feSDimitry Andric assert(PACSym && "No PAC instruction to refer to");
208cb14a3feSDimitry Andric BuildMI(MBB, MBBI, DL,
209cb14a3feSDimitry Andric TII->get(UseBKey ? AArch64::AUTIBSPPCi : AArch64::AUTIASPPCi))
210cb14a3feSDimitry Andric .addSym(PACSym)
2115f757f3fSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy);
212cb14a3feSDimitry Andric } else {
213cb14a3feSDimitry Andric BuildPACM(*Subtarget, MBB, MBBI, DL, MachineInstr::FrameDestroy, PACSym);
214cb14a3feSDimitry Andric BuildMI(MBB, MBBI, DL,
215cb14a3feSDimitry Andric TII->get(UseBKey ? AArch64::AUTIBSP : AArch64::AUTIASP))
216cb14a3feSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy);
217cb14a3feSDimitry Andric }
2185f757f3fSDimitry Andric
2195f757f3fSDimitry Andric if (EmitAsyncCFI) {
2205f757f3fSDimitry Andric unsigned CFIIndex =
2215f757f3fSDimitry Andric MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr));
2225f757f3fSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
2235f757f3fSDimitry Andric .addCFIIndex(CFIIndex)
2245f757f3fSDimitry Andric .setMIFlags(MachineInstr::FrameDestroy);
2255f757f3fSDimitry Andric }
2265f757f3fSDimitry Andric if (NeedsWinCFI) {
2275f757f3fSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
2285f757f3fSDimitry Andric .setMIFlag(MachineInstr::FrameDestroy);
2295f757f3fSDimitry Andric }
2305f757f3fSDimitry Andric }
2315f757f3fSDimitry Andric }
2325f757f3fSDimitry Andric
2335f757f3fSDimitry Andric namespace {
2345f757f3fSDimitry Andric
2355f757f3fSDimitry Andric // Mark dummy LDR instruction as volatile to prevent removing it as dead code.
createCheckMemOperand(MachineFunction & MF,const AArch64Subtarget & Subtarget)2365f757f3fSDimitry Andric MachineMemOperand *createCheckMemOperand(MachineFunction &MF,
2375f757f3fSDimitry Andric const AArch64Subtarget &Subtarget) {
2385f757f3fSDimitry Andric MachinePointerInfo PointerInfo(Subtarget.getAddressCheckPSV());
2395f757f3fSDimitry Andric auto MOVolatileLoad =
2405f757f3fSDimitry Andric MachineMemOperand::MOLoad | MachineMemOperand::MOVolatile;
2415f757f3fSDimitry Andric
2425f757f3fSDimitry Andric return MF.getMachineMemOperand(PointerInfo, MOVolatileLoad, 4, Align(4));
2435f757f3fSDimitry Andric }
2445f757f3fSDimitry Andric
2455f757f3fSDimitry Andric } // namespace
2465f757f3fSDimitry Andric
checkAuthenticatedRegister(MachineBasicBlock::iterator MBBI,AuthCheckMethod Method,Register AuthenticatedReg,Register TmpReg,bool UseIKey,unsigned BrkImm)2470fca6ea1SDimitry Andric void llvm::AArch64PAuth::checkAuthenticatedRegister(
2485f757f3fSDimitry Andric MachineBasicBlock::iterator MBBI, AuthCheckMethod Method,
2495f757f3fSDimitry Andric Register AuthenticatedReg, Register TmpReg, bool UseIKey, unsigned BrkImm) {
2505f757f3fSDimitry Andric
2515f757f3fSDimitry Andric MachineBasicBlock &MBB = *MBBI->getParent();
2525f757f3fSDimitry Andric MachineFunction &MF = *MBB.getParent();
2535f757f3fSDimitry Andric const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
2545f757f3fSDimitry Andric const AArch64InstrInfo *TII = Subtarget.getInstrInfo();
2555f757f3fSDimitry Andric DebugLoc DL = MBBI->getDebugLoc();
2565f757f3fSDimitry Andric
2570fca6ea1SDimitry Andric // All terminator instructions should be grouped at the end of the machine
2580fca6ea1SDimitry Andric // basic block, with no non-terminator instructions between them. Depending on
2590fca6ea1SDimitry Andric // the method requested, we will insert some regular instructions, maybe
2600fca6ea1SDimitry Andric // followed by a conditional branch instruction, which is a terminator, before
2610fca6ea1SDimitry Andric // MBBI. Thus, MBBI is expected to be the first terminator of its MBB.
2620fca6ea1SDimitry Andric assert(MBBI->isTerminator() && MBBI == MBB.getFirstTerminator() &&
2630fca6ea1SDimitry Andric "MBBI should be the first terminator in MBB");
2640fca6ea1SDimitry Andric
2655f757f3fSDimitry Andric // First, handle the methods not requiring creating extra MBBs.
2665f757f3fSDimitry Andric switch (Method) {
2675f757f3fSDimitry Andric default:
2685f757f3fSDimitry Andric break;
2695f757f3fSDimitry Andric case AuthCheckMethod::None:
2700fca6ea1SDimitry Andric return;
2715f757f3fSDimitry Andric case AuthCheckMethod::DummyLoad:
2725f757f3fSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRWui), getWRegFromXReg(TmpReg))
2730fca6ea1SDimitry Andric .addReg(AuthenticatedReg)
2745f757f3fSDimitry Andric .addImm(0)
2755f757f3fSDimitry Andric .addMemOperand(createCheckMemOperand(MF, Subtarget));
2760fca6ea1SDimitry Andric return;
2775f757f3fSDimitry Andric }
2785f757f3fSDimitry Andric
2795f757f3fSDimitry Andric // Control flow has to be changed, so arrange new MBBs.
2805f757f3fSDimitry Andric
2815f757f3fSDimitry Andric // The block that explicitly generates a break-point exception on failure.
2825f757f3fSDimitry Andric MachineBasicBlock *BreakBlock =
2835f757f3fSDimitry Andric MF.CreateMachineBasicBlock(MBB.getBasicBlock());
2845f757f3fSDimitry Andric MF.push_back(BreakBlock);
2850fca6ea1SDimitry Andric MBB.addSuccessor(BreakBlock);
2865f757f3fSDimitry Andric
2875f757f3fSDimitry Andric BuildMI(BreakBlock, DL, TII->get(AArch64::BRK)).addImm(BrkImm);
2885f757f3fSDimitry Andric
2895f757f3fSDimitry Andric switch (Method) {
2905f757f3fSDimitry Andric case AuthCheckMethod::None:
2915f757f3fSDimitry Andric case AuthCheckMethod::DummyLoad:
2925f757f3fSDimitry Andric llvm_unreachable("Should be handled above");
2935f757f3fSDimitry Andric case AuthCheckMethod::HighBitsNoTBI:
2940fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::EORXrs), TmpReg)
2955f757f3fSDimitry Andric .addReg(AuthenticatedReg)
2965f757f3fSDimitry Andric .addReg(AuthenticatedReg)
2975f757f3fSDimitry Andric .addImm(1);
2980fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::TBNZX))
2995f757f3fSDimitry Andric .addReg(TmpReg)
3005f757f3fSDimitry Andric .addImm(62)
3015f757f3fSDimitry Andric .addMBB(BreakBlock);
3020fca6ea1SDimitry Andric return;
3035f757f3fSDimitry Andric case AuthCheckMethod::XPACHint:
3045f757f3fSDimitry Andric assert(AuthenticatedReg == AArch64::LR &&
3055f757f3fSDimitry Andric "XPACHint mode is only compatible with checking the LR register");
3065f757f3fSDimitry Andric assert(UseIKey && "XPACHint mode is only compatible with I-keys");
3070fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXrs), TmpReg)
3085f757f3fSDimitry Andric .addReg(AArch64::XZR)
3095f757f3fSDimitry Andric .addReg(AArch64::LR)
3105f757f3fSDimitry Andric .addImm(0);
3110fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::XPACLRI));
3120fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR)
3135f757f3fSDimitry Andric .addReg(TmpReg)
3145f757f3fSDimitry Andric .addReg(AArch64::LR)
3155f757f3fSDimitry Andric .addImm(0);
3160fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::Bcc))
3175f757f3fSDimitry Andric .addImm(AArch64CC::NE)
3185f757f3fSDimitry Andric .addMBB(BreakBlock);
3190fca6ea1SDimitry Andric return;
3205f757f3fSDimitry Andric }
3215f757f3fSDimitry Andric llvm_unreachable("Unknown AuthCheckMethod enum");
3225f757f3fSDimitry Andric }
3235f757f3fSDimitry Andric
getCheckerSizeInBytes(AuthCheckMethod Method)3245f757f3fSDimitry Andric unsigned llvm::AArch64PAuth::getCheckerSizeInBytes(AuthCheckMethod Method) {
3255f757f3fSDimitry Andric switch (Method) {
3265f757f3fSDimitry Andric case AuthCheckMethod::None:
3275f757f3fSDimitry Andric return 0;
3285f757f3fSDimitry Andric case AuthCheckMethod::DummyLoad:
3295f757f3fSDimitry Andric return 4;
3305f757f3fSDimitry Andric case AuthCheckMethod::HighBitsNoTBI:
3315f757f3fSDimitry Andric return 12;
3325f757f3fSDimitry Andric case AuthCheckMethod::XPACHint:
3335f757f3fSDimitry Andric return 20;
3345f757f3fSDimitry Andric }
3355f757f3fSDimitry Andric llvm_unreachable("Unknown AuthCheckMethod enum");
3365f757f3fSDimitry Andric }
3375f757f3fSDimitry Andric
checkAuthenticatedLR(MachineBasicBlock::iterator TI) const3385f757f3fSDimitry Andric bool AArch64PointerAuth::checkAuthenticatedLR(
3395f757f3fSDimitry Andric MachineBasicBlock::iterator TI) const {
3400fca6ea1SDimitry Andric const AArch64FunctionInfo *MFnI = TI->getMF()->getInfo<AArch64FunctionInfo>();
3410fca6ea1SDimitry Andric AArch64PACKey::ID KeyId =
3420fca6ea1SDimitry Andric MFnI->shouldSignWithBKey() ? AArch64PACKey::IB : AArch64PACKey::IA;
3430fca6ea1SDimitry Andric
344*36b606aeSDimitry Andric AuthCheckMethod Method =
345*36b606aeSDimitry Andric Subtarget->getAuthenticatedLRCheckMethod(*TI->getMF());
3465f757f3fSDimitry Andric
3475f757f3fSDimitry Andric if (Method == AuthCheckMethod::None)
3485f757f3fSDimitry Andric return false;
3495f757f3fSDimitry Andric
3505f757f3fSDimitry Andric // FIXME If FEAT_FPAC is implemented by the CPU, this check can be skipped.
3515f757f3fSDimitry Andric
3525f757f3fSDimitry Andric assert(!TI->getMF()->hasWinCFI() && "WinCFI is not yet supported");
3535f757f3fSDimitry Andric
3545f757f3fSDimitry Andric // The following code may create a signing oracle:
3555f757f3fSDimitry Andric //
3565f757f3fSDimitry Andric // <authenticate LR>
3575f757f3fSDimitry Andric // TCRETURN ; the callee may sign and spill the LR in its prologue
3585f757f3fSDimitry Andric //
3595f757f3fSDimitry Andric // To avoid generating a signing oracle, check the authenticated value
3605f757f3fSDimitry Andric // before possibly re-signing it in the callee, as follows:
3615f757f3fSDimitry Andric //
3625f757f3fSDimitry Andric // <authenticate LR>
3635f757f3fSDimitry Andric // <check if LR contains a valid address>
3645f757f3fSDimitry Andric // b.<cond> break_block
3655f757f3fSDimitry Andric // ret_block:
3665f757f3fSDimitry Andric // TCRETURN
3675f757f3fSDimitry Andric // break_block:
3685f757f3fSDimitry Andric // brk <BrkOperand>
3695f757f3fSDimitry Andric //
3705f757f3fSDimitry Andric // or just
3715f757f3fSDimitry Andric //
3725f757f3fSDimitry Andric // <authenticate LR>
3735f757f3fSDimitry Andric // ldr tmp, [lr]
3745f757f3fSDimitry Andric // TCRETURN
3755f757f3fSDimitry Andric
3765f757f3fSDimitry Andric // TmpReg is chosen assuming X16 and X17 are dead after TI.
3775f757f3fSDimitry Andric assert(AArch64InstrInfo::isTailCallReturnInst(*TI) &&
3785f757f3fSDimitry Andric "Tail call is expected");
3795f757f3fSDimitry Andric Register TmpReg =
3805f757f3fSDimitry Andric TI->readsRegister(AArch64::X16, TRI) ? AArch64::X17 : AArch64::X16;
3815f757f3fSDimitry Andric assert(!TI->readsRegister(TmpReg, TRI) &&
3825f757f3fSDimitry Andric "More than a single register is used by TCRETURN");
3835f757f3fSDimitry Andric
3845f757f3fSDimitry Andric checkAuthenticatedRegister(TI, Method, AArch64::LR, TmpReg, /*UseIKey=*/true,
3850fca6ea1SDimitry Andric BrkOperandForKey(KeyId));
3865f757f3fSDimitry Andric
3875f757f3fSDimitry Andric return true;
3885f757f3fSDimitry Andric }
3895f757f3fSDimitry Andric
emitBlend(MachineBasicBlock::iterator MBBI,Register Result,Register AddrDisc,unsigned IntDisc) const3900fca6ea1SDimitry Andric void AArch64PointerAuth::emitBlend(MachineBasicBlock::iterator MBBI,
3910fca6ea1SDimitry Andric Register Result, Register AddrDisc,
3920fca6ea1SDimitry Andric unsigned IntDisc) const {
3930fca6ea1SDimitry Andric MachineBasicBlock &MBB = *MBBI->getParent();
3940fca6ea1SDimitry Andric DebugLoc DL = MBBI->getDebugLoc();
3950fca6ea1SDimitry Andric
3960fca6ea1SDimitry Andric if (Result != AddrDisc)
3970fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::ORRXrs), Result)
3980fca6ea1SDimitry Andric .addReg(AArch64::XZR)
3990fca6ea1SDimitry Andric .addReg(AddrDisc)
4000fca6ea1SDimitry Andric .addImm(0);
4010fca6ea1SDimitry Andric
4020fca6ea1SDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVKXi), Result)
4030fca6ea1SDimitry Andric .addReg(Result)
4040fca6ea1SDimitry Andric .addImm(IntDisc)
4050fca6ea1SDimitry Andric .addImm(48);
4060fca6ea1SDimitry Andric }
4070fca6ea1SDimitry Andric
expandPAuthBlend(MachineBasicBlock::iterator MBBI) const4080fca6ea1SDimitry Andric void AArch64PointerAuth::expandPAuthBlend(
4090fca6ea1SDimitry Andric MachineBasicBlock::iterator MBBI) const {
4100fca6ea1SDimitry Andric Register ResultReg = MBBI->getOperand(0).getReg();
4110fca6ea1SDimitry Andric Register AddrDisc = MBBI->getOperand(1).getReg();
4120fca6ea1SDimitry Andric unsigned IntDisc = MBBI->getOperand(2).getImm();
4130fca6ea1SDimitry Andric emitBlend(MBBI, ResultReg, AddrDisc, IntDisc);
4140fca6ea1SDimitry Andric }
4150fca6ea1SDimitry Andric
runOnMachineFunction(MachineFunction & MF)4165f757f3fSDimitry Andric bool AArch64PointerAuth::runOnMachineFunction(MachineFunction &MF) {
4175f757f3fSDimitry Andric const auto *MFnI = MF.getInfo<AArch64FunctionInfo>();
4185f757f3fSDimitry Andric
4195f757f3fSDimitry Andric Subtarget = &MF.getSubtarget<AArch64Subtarget>();
4205f757f3fSDimitry Andric TII = Subtarget->getInstrInfo();
4215f757f3fSDimitry Andric TRI = Subtarget->getRegisterInfo();
4225f757f3fSDimitry Andric
4235f757f3fSDimitry Andric SmallVector<MachineBasicBlock::instr_iterator> PAuthPseudoInstrs;
4245f757f3fSDimitry Andric SmallVector<MachineBasicBlock::instr_iterator> TailCallInstrs;
4255f757f3fSDimitry Andric
4265f757f3fSDimitry Andric bool Modified = false;
4275f757f3fSDimitry Andric bool HasAuthenticationInstrs = false;
4285f757f3fSDimitry Andric
4295f757f3fSDimitry Andric for (auto &MBB : MF) {
4305f757f3fSDimitry Andric // Using instr_iterator to catch unsupported bundled TCRETURN* instructions
4315f757f3fSDimitry Andric // instead of just skipping them.
4325f757f3fSDimitry Andric for (auto &MI : MBB.instrs()) {
4335f757f3fSDimitry Andric switch (MI.getOpcode()) {
4345f757f3fSDimitry Andric default:
4355f757f3fSDimitry Andric // Bundled TCRETURN* instructions (such as created by KCFI)
4365f757f3fSDimitry Andric // are not supported yet, but no support is required if no
4375f757f3fSDimitry Andric // PAUTH_EPILOGUE instructions exist in the same function.
4385f757f3fSDimitry Andric // Skip the BUNDLE instruction itself (actual bundled instructions
4395f757f3fSDimitry Andric // follow it in the instruction list).
4405f757f3fSDimitry Andric if (MI.isBundle())
4415f757f3fSDimitry Andric continue;
4425f757f3fSDimitry Andric if (AArch64InstrInfo::isTailCallReturnInst(MI))
4435f757f3fSDimitry Andric TailCallInstrs.push_back(MI.getIterator());
4445f757f3fSDimitry Andric break;
4455f757f3fSDimitry Andric case AArch64::PAUTH_PROLOGUE:
4465f757f3fSDimitry Andric case AArch64::PAUTH_EPILOGUE:
4470fca6ea1SDimitry Andric case AArch64::PAUTH_BLEND:
4485f757f3fSDimitry Andric assert(!MI.isBundled());
4495f757f3fSDimitry Andric PAuthPseudoInstrs.push_back(MI.getIterator());
4505f757f3fSDimitry Andric break;
4515f757f3fSDimitry Andric }
4525f757f3fSDimitry Andric }
4535f757f3fSDimitry Andric }
4545f757f3fSDimitry Andric
4555f757f3fSDimitry Andric for (auto It : PAuthPseudoInstrs) {
4565f757f3fSDimitry Andric switch (It->getOpcode()) {
4575f757f3fSDimitry Andric case AArch64::PAUTH_PROLOGUE:
4585f757f3fSDimitry Andric signLR(MF, It);
4595f757f3fSDimitry Andric break;
4605f757f3fSDimitry Andric case AArch64::PAUTH_EPILOGUE:
4615f757f3fSDimitry Andric authenticateLR(MF, It);
4625f757f3fSDimitry Andric HasAuthenticationInstrs = true;
4635f757f3fSDimitry Andric break;
4640fca6ea1SDimitry Andric case AArch64::PAUTH_BLEND:
4650fca6ea1SDimitry Andric expandPAuthBlend(It);
4660fca6ea1SDimitry Andric break;
4675f757f3fSDimitry Andric default:
4685f757f3fSDimitry Andric llvm_unreachable("Unhandled opcode");
4695f757f3fSDimitry Andric }
4705f757f3fSDimitry Andric It->eraseFromParent();
4715f757f3fSDimitry Andric Modified = true;
4725f757f3fSDimitry Andric }
4735f757f3fSDimitry Andric
4745f757f3fSDimitry Andric // FIXME Do we need to emit any PAuth-related epilogue code at all
4755f757f3fSDimitry Andric // when SCS is enabled?
4765f757f3fSDimitry Andric if (HasAuthenticationInstrs &&
4775f757f3fSDimitry Andric !MFnI->needsShadowCallStackPrologueEpilogue(MF)) {
4785f757f3fSDimitry Andric for (auto TailCall : TailCallInstrs) {
4795f757f3fSDimitry Andric assert(!TailCall->isBundled() && "Not yet supported");
4805f757f3fSDimitry Andric Modified |= checkAuthenticatedLR(TailCall);
4815f757f3fSDimitry Andric }
4825f757f3fSDimitry Andric }
4835f757f3fSDimitry Andric
4845f757f3fSDimitry Andric return Modified;
4855f757f3fSDimitry Andric }
486