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 315f757f3fSDimitry Andric AArch64PointerAuth() : MachineFunctionPass(ID) {} 325f757f3fSDimitry Andric 335f757f3fSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 345f757f3fSDimitry Andric 355f757f3fSDimitry 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. 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 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. 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 985f757f3fSDimitry 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 1645f757f3fSDimitry 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. 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 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 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 3385f757f3fSDimitry 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 3900fca6ea1SDimitry 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 4080fca6ea1SDimitry 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 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