15ffd83dbSDimitry Andric //===- AArch64SLSHardening.cpp - Harden Straight Line Missspeculation -----===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric // 95ffd83dbSDimitry Andric // This file contains a pass to insert code to mitigate against side channel 105ffd83dbSDimitry Andric // vulnerabilities that may happen under straight line miss-speculation. 115ffd83dbSDimitry Andric // 125ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 135ffd83dbSDimitry Andric 145ffd83dbSDimitry Andric #include "AArch64InstrInfo.h" 155ffd83dbSDimitry Andric #include "AArch64Subtarget.h" 16*0fca6ea1SDimitry Andric #include "llvm/ADT/StringSwitch.h" 175ffd83dbSDimitry Andric #include "llvm/CodeGen/IndirectThunks.h" 185ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h" 195ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 205ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineInstr.h" 215ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 225ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineOperand.h" 235ffd83dbSDimitry Andric #include "llvm/CodeGen/RegisterScavenging.h" 245ffd83dbSDimitry Andric #include "llvm/IR/DebugLoc.h" 255ffd83dbSDimitry Andric #include "llvm/Pass.h" 26*0fca6ea1SDimitry Andric #include "llvm/Support/ErrorHandling.h" 27*0fca6ea1SDimitry Andric #include "llvm/Support/FormatVariadic.h" 285ffd83dbSDimitry Andric #include "llvm/Target/TargetMachine.h" 295ffd83dbSDimitry Andric #include <cassert> 30*0fca6ea1SDimitry Andric #include <climits> 31*0fca6ea1SDimitry Andric #include <tuple> 325ffd83dbSDimitry Andric 335ffd83dbSDimitry Andric using namespace llvm; 345ffd83dbSDimitry Andric 355ffd83dbSDimitry Andric #define DEBUG_TYPE "aarch64-sls-hardening" 365ffd83dbSDimitry Andric 375ffd83dbSDimitry Andric #define AARCH64_SLS_HARDENING_NAME "AArch64 sls hardening pass" 385ffd83dbSDimitry Andric 39*0fca6ea1SDimitry Andric // Common name prefix of all thunks generated by this pass. 40*0fca6ea1SDimitry Andric // 41*0fca6ea1SDimitry Andric // The generic form is 42*0fca6ea1SDimitry Andric // __llvm_slsblr_thunk_xN for BLR thunks 43*0fca6ea1SDimitry Andric // __llvm_slsblr_thunk_(aaz|abz)_xN for BLRAAZ and BLRABZ thunks 44*0fca6ea1SDimitry Andric // __llvm_slsblr_thunk_(aa|ab)_xN_xM for BLRAA and BLRAB thunks 45*0fca6ea1SDimitry Andric static constexpr StringRef CommonNamePrefix = "__llvm_slsblr_thunk_"; 46*0fca6ea1SDimitry Andric 475ffd83dbSDimitry Andric namespace { 485ffd83dbSDimitry Andric 49*0fca6ea1SDimitry Andric struct ThunkKind { 50*0fca6ea1SDimitry Andric enum ThunkKindId { 51*0fca6ea1SDimitry Andric ThunkBR, 52*0fca6ea1SDimitry Andric ThunkBRAA, 53*0fca6ea1SDimitry Andric ThunkBRAB, 54*0fca6ea1SDimitry Andric ThunkBRAAZ, 55*0fca6ea1SDimitry Andric ThunkBRABZ, 56*0fca6ea1SDimitry Andric }; 57*0fca6ea1SDimitry Andric 58*0fca6ea1SDimitry Andric ThunkKindId Id; 59*0fca6ea1SDimitry Andric StringRef NameInfix; 60*0fca6ea1SDimitry Andric bool HasXmOperand; 61*0fca6ea1SDimitry Andric bool NeedsPAuth; 62*0fca6ea1SDimitry Andric 63*0fca6ea1SDimitry Andric // Opcode to perform indirect jump from inside the thunk. 64*0fca6ea1SDimitry Andric unsigned BROpcode; 65*0fca6ea1SDimitry Andric 66*0fca6ea1SDimitry Andric static const ThunkKind BR; 67*0fca6ea1SDimitry Andric static const ThunkKind BRAA; 68*0fca6ea1SDimitry Andric static const ThunkKind BRAB; 69*0fca6ea1SDimitry Andric static const ThunkKind BRAAZ; 70*0fca6ea1SDimitry Andric static const ThunkKind BRABZ; 71*0fca6ea1SDimitry Andric }; 72*0fca6ea1SDimitry Andric 73*0fca6ea1SDimitry Andric // Set of inserted thunks. 74*0fca6ea1SDimitry Andric class ThunksSet { 755ffd83dbSDimitry Andric public: 76*0fca6ea1SDimitry Andric static constexpr unsigned NumXRegisters = 32; 775ffd83dbSDimitry Andric 78*0fca6ea1SDimitry Andric // Given Xn register, returns n. 79*0fca6ea1SDimitry Andric static unsigned indexOfXReg(Register Xn); 80*0fca6ea1SDimitry Andric // Given n, returns Xn register. 81*0fca6ea1SDimitry Andric static Register xRegByIndex(unsigned N); 825ffd83dbSDimitry Andric 83*0fca6ea1SDimitry Andric ThunksSet &operator|=(const ThunksSet &Other) { 84*0fca6ea1SDimitry Andric BLRThunks |= Other.BLRThunks; 85*0fca6ea1SDimitry Andric BLRAAZThunks |= Other.BLRAAZThunks; 86*0fca6ea1SDimitry Andric BLRABZThunks |= Other.BLRABZThunks; 87*0fca6ea1SDimitry Andric for (unsigned I = 0; I < NumXRegisters; ++I) 88*0fca6ea1SDimitry Andric BLRAAThunks[I] |= Other.BLRAAThunks[I]; 89*0fca6ea1SDimitry Andric for (unsigned I = 0; I < NumXRegisters; ++I) 90*0fca6ea1SDimitry Andric BLRABThunks[I] |= Other.BLRABThunks[I]; 91*0fca6ea1SDimitry Andric 92*0fca6ea1SDimitry Andric return *this; 935ffd83dbSDimitry Andric } 945ffd83dbSDimitry Andric 95*0fca6ea1SDimitry Andric bool get(ThunkKind::ThunkKindId Kind, Register Xn, Register Xm) { 96*0fca6ea1SDimitry Andric reg_bitmask_t XnBit = reg_bitmask_t(1) << indexOfXReg(Xn); 97*0fca6ea1SDimitry Andric return getBitmask(Kind, Xm) & XnBit; 98*0fca6ea1SDimitry Andric } 995ffd83dbSDimitry Andric 100*0fca6ea1SDimitry Andric void set(ThunkKind::ThunkKindId Kind, Register Xn, Register Xm) { 101*0fca6ea1SDimitry Andric reg_bitmask_t XnBit = reg_bitmask_t(1) << indexOfXReg(Xn); 102*0fca6ea1SDimitry Andric getBitmask(Kind, Xm) |= XnBit; 103*0fca6ea1SDimitry Andric } 1045ffd83dbSDimitry Andric 1055ffd83dbSDimitry Andric private: 106*0fca6ea1SDimitry Andric typedef uint32_t reg_bitmask_t; 107*0fca6ea1SDimitry Andric static_assert(NumXRegisters <= sizeof(reg_bitmask_t) * CHAR_BIT, 108*0fca6ea1SDimitry Andric "Bitmask is not wide enough to hold all Xn registers"); 109*0fca6ea1SDimitry Andric 110*0fca6ea1SDimitry Andric // Bitmasks representing operands used, with n-th bit corresponding to Xn 111*0fca6ea1SDimitry Andric // register operand. If the instruction has a second operand (Xm), an array 112*0fca6ea1SDimitry Andric // of bitmasks is used, indexed by m. 113*0fca6ea1SDimitry Andric // Indexes corresponding to the forbidden x16, x17 and x30 registers are 114*0fca6ea1SDimitry Andric // always unset, for simplicity there are no holes. 115*0fca6ea1SDimitry Andric reg_bitmask_t BLRThunks = 0; 116*0fca6ea1SDimitry Andric reg_bitmask_t BLRAAZThunks = 0; 117*0fca6ea1SDimitry Andric reg_bitmask_t BLRABZThunks = 0; 118*0fca6ea1SDimitry Andric reg_bitmask_t BLRAAThunks[NumXRegisters] = {}; 119*0fca6ea1SDimitry Andric reg_bitmask_t BLRABThunks[NumXRegisters] = {}; 120*0fca6ea1SDimitry Andric 121*0fca6ea1SDimitry Andric reg_bitmask_t &getBitmask(ThunkKind::ThunkKindId Kind, Register Xm) { 122*0fca6ea1SDimitry Andric switch (Kind) { 123*0fca6ea1SDimitry Andric case ThunkKind::ThunkBR: 124*0fca6ea1SDimitry Andric return BLRThunks; 125*0fca6ea1SDimitry Andric case ThunkKind::ThunkBRAAZ: 126*0fca6ea1SDimitry Andric return BLRAAZThunks; 127*0fca6ea1SDimitry Andric case ThunkKind::ThunkBRABZ: 128*0fca6ea1SDimitry Andric return BLRABZThunks; 129*0fca6ea1SDimitry Andric case ThunkKind::ThunkBRAA: 130*0fca6ea1SDimitry Andric return BLRAAThunks[indexOfXReg(Xm)]; 131*0fca6ea1SDimitry Andric case ThunkKind::ThunkBRAB: 132*0fca6ea1SDimitry Andric return BLRABThunks[indexOfXReg(Xm)]; 133*0fca6ea1SDimitry Andric } 134*0fca6ea1SDimitry Andric llvm_unreachable("Unknown ThunkKindId enum"); 135*0fca6ea1SDimitry Andric } 136*0fca6ea1SDimitry Andric }; 137*0fca6ea1SDimitry Andric 138*0fca6ea1SDimitry Andric struct SLSHardeningInserter : ThunkInserter<SLSHardeningInserter, ThunksSet> { 139*0fca6ea1SDimitry Andric public: 140*0fca6ea1SDimitry Andric const char *getThunkPrefix() { return CommonNamePrefix.data(); } 141*0fca6ea1SDimitry Andric bool mayUseThunk(const MachineFunction &MF) { 142*0fca6ea1SDimitry Andric ComdatThunks &= !MF.getSubtarget<AArch64Subtarget>().hardenSlsNoComdat(); 143*0fca6ea1SDimitry Andric // We are inserting barriers aside from thunk calls, so 144*0fca6ea1SDimitry Andric // check hardenSlsRetBr() as well. 145*0fca6ea1SDimitry Andric return MF.getSubtarget<AArch64Subtarget>().hardenSlsBlr() || 146*0fca6ea1SDimitry Andric MF.getSubtarget<AArch64Subtarget>().hardenSlsRetBr(); 147*0fca6ea1SDimitry Andric } 148*0fca6ea1SDimitry Andric ThunksSet insertThunks(MachineModuleInfo &MMI, MachineFunction &MF, 149*0fca6ea1SDimitry Andric ThunksSet ExistingThunks); 150*0fca6ea1SDimitry Andric void populateThunk(MachineFunction &MF); 151*0fca6ea1SDimitry Andric 152*0fca6ea1SDimitry Andric private: 153*0fca6ea1SDimitry Andric bool ComdatThunks = true; 154*0fca6ea1SDimitry Andric 155*0fca6ea1SDimitry Andric bool hardenReturnsAndBRs(MachineModuleInfo &MMI, MachineBasicBlock &MBB); 156*0fca6ea1SDimitry Andric bool hardenBLRs(MachineModuleInfo &MMI, MachineBasicBlock &MBB, 157*0fca6ea1SDimitry Andric ThunksSet &Thunks); 158*0fca6ea1SDimitry Andric 159*0fca6ea1SDimitry Andric void convertBLRToBL(MachineModuleInfo &MMI, MachineBasicBlock &MBB, 160*0fca6ea1SDimitry Andric MachineBasicBlock::instr_iterator MBBI, 161*0fca6ea1SDimitry Andric ThunksSet &Thunks); 1625ffd83dbSDimitry Andric }; 1635ffd83dbSDimitry Andric 1645ffd83dbSDimitry Andric } // end anonymous namespace 1655ffd83dbSDimitry Andric 166*0fca6ea1SDimitry Andric const ThunkKind ThunkKind::BR = {ThunkBR, "", /*HasXmOperand=*/false, 167*0fca6ea1SDimitry Andric /*NeedsPAuth=*/false, AArch64::BR}; 168*0fca6ea1SDimitry Andric const ThunkKind ThunkKind::BRAA = {ThunkBRAA, "aa_", /*HasXmOperand=*/true, 169*0fca6ea1SDimitry Andric /*NeedsPAuth=*/true, AArch64::BRAA}; 170*0fca6ea1SDimitry Andric const ThunkKind ThunkKind::BRAB = {ThunkBRAB, "ab_", /*HasXmOperand=*/true, 171*0fca6ea1SDimitry Andric /*NeedsPAuth=*/true, AArch64::BRAB}; 172*0fca6ea1SDimitry Andric const ThunkKind ThunkKind::BRAAZ = {ThunkBRAAZ, "aaz_", /*HasXmOperand=*/false, 173*0fca6ea1SDimitry Andric /*NeedsPAuth=*/true, AArch64::BRAAZ}; 174*0fca6ea1SDimitry Andric const ThunkKind ThunkKind::BRABZ = {ThunkBRABZ, "abz_", /*HasXmOperand=*/false, 175*0fca6ea1SDimitry Andric /*NeedsPAuth=*/true, AArch64::BRABZ}; 1765ffd83dbSDimitry Andric 177*0fca6ea1SDimitry Andric // Returns thunk kind to emit, or nullptr if not a BLR* instruction. 178*0fca6ea1SDimitry Andric static const ThunkKind *getThunkKind(unsigned OriginalOpcode) { 179*0fca6ea1SDimitry Andric switch (OriginalOpcode) { 180*0fca6ea1SDimitry Andric case AArch64::BLR: 181*0fca6ea1SDimitry Andric case AArch64::BLRNoIP: 182*0fca6ea1SDimitry Andric return &ThunkKind::BR; 183*0fca6ea1SDimitry Andric case AArch64::BLRAA: 184*0fca6ea1SDimitry Andric return &ThunkKind::BRAA; 185*0fca6ea1SDimitry Andric case AArch64::BLRAB: 186*0fca6ea1SDimitry Andric return &ThunkKind::BRAB; 187*0fca6ea1SDimitry Andric case AArch64::BLRAAZ: 188*0fca6ea1SDimitry Andric return &ThunkKind::BRAAZ; 189*0fca6ea1SDimitry Andric case AArch64::BLRABZ: 190*0fca6ea1SDimitry Andric return &ThunkKind::BRABZ; 191*0fca6ea1SDimitry Andric } 192*0fca6ea1SDimitry Andric return nullptr; 193*0fca6ea1SDimitry Andric } 194*0fca6ea1SDimitry Andric 195*0fca6ea1SDimitry Andric static bool isBLR(const MachineInstr &MI) { 196*0fca6ea1SDimitry Andric return getThunkKind(MI.getOpcode()) != nullptr; 197*0fca6ea1SDimitry Andric } 198*0fca6ea1SDimitry Andric 199*0fca6ea1SDimitry Andric unsigned ThunksSet::indexOfXReg(Register Reg) { 200*0fca6ea1SDimitry Andric assert(AArch64::GPR64RegClass.contains(Reg)); 201*0fca6ea1SDimitry Andric assert(Reg != AArch64::X16 && Reg != AArch64::X17 && Reg != AArch64::LR); 202*0fca6ea1SDimitry Andric 203*0fca6ea1SDimitry Andric // Most Xn registers have consecutive ids, except for FP and XZR. 204*0fca6ea1SDimitry Andric unsigned Result = (unsigned)Reg - (unsigned)AArch64::X0; 205*0fca6ea1SDimitry Andric if (Reg == AArch64::FP) 206*0fca6ea1SDimitry Andric Result = 29; 207*0fca6ea1SDimitry Andric else if (Reg == AArch64::XZR) 208*0fca6ea1SDimitry Andric Result = 31; 209*0fca6ea1SDimitry Andric 210*0fca6ea1SDimitry Andric assert(Result < NumXRegisters && "Internal register numbering changed"); 211*0fca6ea1SDimitry Andric assert(AArch64::GPR64RegClass.getRegister(Result).id() == Reg && 212*0fca6ea1SDimitry Andric "Internal register numbering changed"); 213*0fca6ea1SDimitry Andric 214*0fca6ea1SDimitry Andric return Result; 215*0fca6ea1SDimitry Andric } 216*0fca6ea1SDimitry Andric 217*0fca6ea1SDimitry Andric Register ThunksSet::xRegByIndex(unsigned N) { 218*0fca6ea1SDimitry Andric return AArch64::GPR64RegClass.getRegister(N); 219*0fca6ea1SDimitry Andric } 2205ffd83dbSDimitry Andric 2215ffd83dbSDimitry Andric static void insertSpeculationBarrier(const AArch64Subtarget *ST, 2225ffd83dbSDimitry Andric MachineBasicBlock &MBB, 2235ffd83dbSDimitry Andric MachineBasicBlock::iterator MBBI, 2245ffd83dbSDimitry Andric DebugLoc DL, 2255ffd83dbSDimitry Andric bool AlwaysUseISBDSB = false) { 2265ffd83dbSDimitry Andric assert(MBBI != MBB.begin() && 2275ffd83dbSDimitry Andric "Must not insert SpeculationBarrierEndBB as only instruction in MBB."); 2285ffd83dbSDimitry Andric assert(std::prev(MBBI)->isBarrier() && 2295ffd83dbSDimitry Andric "SpeculationBarrierEndBB must only follow unconditional control flow " 2305ffd83dbSDimitry Andric "instructions."); 2315ffd83dbSDimitry Andric assert(std::prev(MBBI)->isTerminator() && 2325ffd83dbSDimitry Andric "SpeculationBarrierEndBB must only follow terminators."); 2335ffd83dbSDimitry Andric const TargetInstrInfo *TII = ST->getInstrInfo(); 2345ffd83dbSDimitry Andric unsigned BarrierOpc = ST->hasSB() && !AlwaysUseISBDSB 2355ffd83dbSDimitry Andric ? AArch64::SpeculationBarrierSBEndBB 2365ffd83dbSDimitry Andric : AArch64::SpeculationBarrierISBDSBEndBB; 2375ffd83dbSDimitry Andric if (MBBI == MBB.end() || 2385ffd83dbSDimitry Andric (MBBI->getOpcode() != AArch64::SpeculationBarrierSBEndBB && 2395ffd83dbSDimitry Andric MBBI->getOpcode() != AArch64::SpeculationBarrierISBDSBEndBB)) 2405ffd83dbSDimitry Andric BuildMI(MBB, MBBI, DL, TII->get(BarrierOpc)); 2415ffd83dbSDimitry Andric } 2425ffd83dbSDimitry Andric 243*0fca6ea1SDimitry Andric ThunksSet SLSHardeningInserter::insertThunks(MachineModuleInfo &MMI, 244*0fca6ea1SDimitry Andric MachineFunction &MF, 245*0fca6ea1SDimitry Andric ThunksSet ExistingThunks) { 246*0fca6ea1SDimitry Andric const AArch64Subtarget *ST = &MF.getSubtarget<AArch64Subtarget>(); 2475ffd83dbSDimitry Andric 2485ffd83dbSDimitry Andric for (auto &MBB : MF) { 249*0fca6ea1SDimitry Andric if (ST->hardenSlsRetBr()) 250*0fca6ea1SDimitry Andric hardenReturnsAndBRs(MMI, MBB); 251*0fca6ea1SDimitry Andric if (ST->hardenSlsBlr()) 252*0fca6ea1SDimitry Andric hardenBLRs(MMI, MBB, ExistingThunks); 253*0fca6ea1SDimitry Andric } 254*0fca6ea1SDimitry Andric return ExistingThunks; 2555ffd83dbSDimitry Andric } 2565ffd83dbSDimitry Andric 257*0fca6ea1SDimitry Andric bool SLSHardeningInserter::hardenReturnsAndBRs(MachineModuleInfo &MMI, 258*0fca6ea1SDimitry Andric MachineBasicBlock &MBB) { 259*0fca6ea1SDimitry Andric const AArch64Subtarget *ST = 260*0fca6ea1SDimitry Andric &MBB.getParent()->getSubtarget<AArch64Subtarget>(); 2615ffd83dbSDimitry Andric bool Modified = false; 2625ffd83dbSDimitry Andric MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(), E = MBB.end(); 2635ffd83dbSDimitry Andric MachineBasicBlock::iterator NextMBBI; 2645ffd83dbSDimitry Andric for (; MBBI != E; MBBI = NextMBBI) { 2655ffd83dbSDimitry Andric MachineInstr &MI = *MBBI; 2665ffd83dbSDimitry Andric NextMBBI = std::next(MBBI); 2675ffd83dbSDimitry Andric if (MI.isReturn() || isIndirectBranchOpcode(MI.getOpcode())) { 2685ffd83dbSDimitry Andric assert(MI.isTerminator()); 2695ffd83dbSDimitry Andric insertSpeculationBarrier(ST, MBB, std::next(MBBI), MI.getDebugLoc()); 2705ffd83dbSDimitry Andric Modified = true; 2715ffd83dbSDimitry Andric } 2725ffd83dbSDimitry Andric } 2735ffd83dbSDimitry Andric return Modified; 2745ffd83dbSDimitry Andric } 2755ffd83dbSDimitry Andric 276*0fca6ea1SDimitry Andric // Currently, the longest possible thunk name is 277*0fca6ea1SDimitry Andric // __llvm_slsblr_thunk_aa_xNN_xMM 278*0fca6ea1SDimitry Andric // which is 31 characters (without the '\0' character). 279*0fca6ea1SDimitry Andric static SmallString<32> createThunkName(const ThunkKind &Kind, Register Xn, 280*0fca6ea1SDimitry Andric Register Xm) { 281*0fca6ea1SDimitry Andric unsigned N = ThunksSet::indexOfXReg(Xn); 282*0fca6ea1SDimitry Andric if (!Kind.HasXmOperand) 283*0fca6ea1SDimitry Andric return formatv("{0}{1}x{2}", CommonNamePrefix, Kind.NameInfix, N); 2845ffd83dbSDimitry Andric 285*0fca6ea1SDimitry Andric unsigned M = ThunksSet::indexOfXReg(Xm); 286*0fca6ea1SDimitry Andric return formatv("{0}{1}x{2}_x{3}", CommonNamePrefix, Kind.NameInfix, N, M); 2875ffd83dbSDimitry Andric } 2885ffd83dbSDimitry Andric 289*0fca6ea1SDimitry Andric static std::tuple<const ThunkKind &, Register, Register> 290*0fca6ea1SDimitry Andric parseThunkName(StringRef ThunkName) { 291*0fca6ea1SDimitry Andric assert(ThunkName.starts_with(CommonNamePrefix) && 292*0fca6ea1SDimitry Andric "Should be filtered out by ThunkInserter"); 293*0fca6ea1SDimitry Andric // Thunk name suffix, such as "x1" or "aa_x2_x3". 294*0fca6ea1SDimitry Andric StringRef NameSuffix = ThunkName.drop_front(CommonNamePrefix.size()); 295*0fca6ea1SDimitry Andric 296*0fca6ea1SDimitry Andric // Parse thunk kind based on thunk name infix. 297*0fca6ea1SDimitry Andric const ThunkKind &Kind = *StringSwitch<const ThunkKind *>(NameSuffix) 298*0fca6ea1SDimitry Andric .StartsWith("aa_", &ThunkKind::BRAA) 299*0fca6ea1SDimitry Andric .StartsWith("ab_", &ThunkKind::BRAB) 300*0fca6ea1SDimitry Andric .StartsWith("aaz_", &ThunkKind::BRAAZ) 301*0fca6ea1SDimitry Andric .StartsWith("abz_", &ThunkKind::BRABZ) 302*0fca6ea1SDimitry Andric .Default(&ThunkKind::BR); 303*0fca6ea1SDimitry Andric 304*0fca6ea1SDimitry Andric auto ParseRegName = [](StringRef Name) { 305*0fca6ea1SDimitry Andric unsigned N; 306*0fca6ea1SDimitry Andric 307*0fca6ea1SDimitry Andric assert(Name.starts_with("x") && "xN register name expected"); 308*0fca6ea1SDimitry Andric bool Fail = Name.drop_front(1).getAsInteger(/*Radix=*/10, N); 309*0fca6ea1SDimitry Andric assert(!Fail && N < ThunksSet::NumXRegisters && "Unexpected register"); 310*0fca6ea1SDimitry Andric (void)Fail; 311*0fca6ea1SDimitry Andric 312*0fca6ea1SDimitry Andric return ThunksSet::xRegByIndex(N); 313*0fca6ea1SDimitry Andric }; 314*0fca6ea1SDimitry Andric 315*0fca6ea1SDimitry Andric // For example, "x1" or "x2_x3". 316*0fca6ea1SDimitry Andric StringRef RegsStr = NameSuffix.drop_front(Kind.NameInfix.size()); 317*0fca6ea1SDimitry Andric StringRef XnStr, XmStr; 318*0fca6ea1SDimitry Andric std::tie(XnStr, XmStr) = RegsStr.split('_'); 319*0fca6ea1SDimitry Andric 320*0fca6ea1SDimitry Andric // Parse register operands. 321*0fca6ea1SDimitry Andric Register Xn = ParseRegName(XnStr); 322*0fca6ea1SDimitry Andric Register Xm = Kind.HasXmOperand ? ParseRegName(XmStr) : AArch64::NoRegister; 323*0fca6ea1SDimitry Andric 324*0fca6ea1SDimitry Andric return std::make_tuple(std::ref(Kind), Xn, Xm); 325*0fca6ea1SDimitry Andric } 326*0fca6ea1SDimitry Andric 327*0fca6ea1SDimitry Andric void SLSHardeningInserter::populateThunk(MachineFunction &MF) { 328*0fca6ea1SDimitry Andric assert(MF.getFunction().hasComdat() == ComdatThunks && 329*0fca6ea1SDimitry Andric "ComdatThunks value changed since MF creation"); 330*0fca6ea1SDimitry Andric Register Xn, Xm; 331*0fca6ea1SDimitry Andric auto KindAndRegs = parseThunkName(MF.getName()); 332*0fca6ea1SDimitry Andric const ThunkKind &Kind = std::get<0>(KindAndRegs); 333*0fca6ea1SDimitry Andric std::tie(std::ignore, Xn, Xm) = KindAndRegs; 3345ffd83dbSDimitry Andric 3355ffd83dbSDimitry Andric const TargetInstrInfo *TII = 3365ffd83dbSDimitry Andric MF.getSubtarget<AArch64Subtarget>().getInstrInfo(); 337*0fca6ea1SDimitry Andric 338*0fca6ea1SDimitry Andric // Depending on whether this pass is in the same FunctionPassManager as the 339*0fca6ea1SDimitry Andric // IR->MIR conversion, the thunk may be completely empty, or contain a single 340*0fca6ea1SDimitry Andric // basic block with a single return instruction. Normalise it to contain a 341*0fca6ea1SDimitry Andric // single empty basic block. 342*0fca6ea1SDimitry Andric if (MF.size() == 1) { 343*0fca6ea1SDimitry Andric assert(MF.front().size() == 1); 344*0fca6ea1SDimitry Andric assert(MF.front().front().getOpcode() == AArch64::RET); 345*0fca6ea1SDimitry Andric MF.front().erase(MF.front().begin()); 346*0fca6ea1SDimitry Andric } else { 347*0fca6ea1SDimitry Andric assert(MF.size() == 0); 348*0fca6ea1SDimitry Andric MF.push_back(MF.CreateMachineBasicBlock()); 349*0fca6ea1SDimitry Andric } 350*0fca6ea1SDimitry Andric 3515ffd83dbSDimitry Andric MachineBasicBlock *Entry = &MF.front(); 3525ffd83dbSDimitry Andric Entry->clear(); 3535ffd83dbSDimitry Andric 3545ffd83dbSDimitry Andric // These thunks need to consist of the following instructions: 355*0fca6ea1SDimitry Andric // __llvm_slsblr_thunk_...: 356*0fca6ea1SDimitry Andric // MOV x16, xN ; BR* instructions are not compatible with "BTI c" 357*0fca6ea1SDimitry Andric // ; branch target unless xN is x16 or x17. 358*0fca6ea1SDimitry Andric // BR* ... ; One of: BR x16 359*0fca6ea1SDimitry Andric // ; BRA(A|B) x16, xM 360*0fca6ea1SDimitry Andric // ; BRA(A|B)Z x16 3615ffd83dbSDimitry Andric // barrierInsts 362*0fca6ea1SDimitry Andric Entry->addLiveIn(Xn); 363*0fca6ea1SDimitry Andric // MOV X16, Reg == ORR X16, XZR, Reg, LSL #0 3645ffd83dbSDimitry Andric BuildMI(Entry, DebugLoc(), TII->get(AArch64::ORRXrs), AArch64::X16) 3655ffd83dbSDimitry Andric .addReg(AArch64::XZR) 366*0fca6ea1SDimitry Andric .addReg(Xn) 3675ffd83dbSDimitry Andric .addImm(0); 368*0fca6ea1SDimitry Andric MachineInstrBuilder Builder = 369*0fca6ea1SDimitry Andric BuildMI(Entry, DebugLoc(), TII->get(Kind.BROpcode)).addReg(AArch64::X16); 370*0fca6ea1SDimitry Andric if (Xm != AArch64::NoRegister) { 371*0fca6ea1SDimitry Andric Entry->addLiveIn(Xm); 372*0fca6ea1SDimitry Andric Builder.addReg(Xm); 373*0fca6ea1SDimitry Andric } 374*0fca6ea1SDimitry Andric 3755ffd83dbSDimitry Andric // Make sure the thunks do not make use of the SB extension in case there is 3765ffd83dbSDimitry Andric // a function somewhere that will call to it that for some reason disabled 3775ffd83dbSDimitry Andric // the SB extension locally on that function, even though it's enabled for 3785ffd83dbSDimitry Andric // the module otherwise. Therefore set AlwaysUseISBSDB to true. 3795ffd83dbSDimitry Andric insertSpeculationBarrier(&MF.getSubtarget<AArch64Subtarget>(), *Entry, 3805ffd83dbSDimitry Andric Entry->end(), DebugLoc(), true /*AlwaysUseISBDSB*/); 3815ffd83dbSDimitry Andric } 3825ffd83dbSDimitry Andric 383*0fca6ea1SDimitry Andric void SLSHardeningInserter::convertBLRToBL( 384*0fca6ea1SDimitry Andric MachineModuleInfo &MMI, MachineBasicBlock &MBB, 385*0fca6ea1SDimitry Andric MachineBasicBlock::instr_iterator MBBI, ThunksSet &Thunks) { 386*0fca6ea1SDimitry Andric // Transform a BLR* instruction (one of BLR, BLRAA/BLRAB or BLRAAZ/BLRABZ) to 387*0fca6ea1SDimitry Andric // a BL to the thunk containing BR, BRAA/BRAB or BRAAZ/BRABZ, respectively. 388*0fca6ea1SDimitry Andric // 3895ffd83dbSDimitry Andric // Before: 3905ffd83dbSDimitry Andric // |-----------------------------| 3915ffd83dbSDimitry Andric // | ... | 3925ffd83dbSDimitry Andric // | instI | 393*0fca6ea1SDimitry Andric // | BLR* xN or BLR* xN, xM | 3945ffd83dbSDimitry Andric // | instJ | 3955ffd83dbSDimitry Andric // | ... | 3965ffd83dbSDimitry Andric // |-----------------------------| 3975ffd83dbSDimitry Andric // 3985ffd83dbSDimitry Andric // After: 3995ffd83dbSDimitry Andric // |-----------------------------| 4005ffd83dbSDimitry Andric // | ... | 4015ffd83dbSDimitry Andric // | instI | 402*0fca6ea1SDimitry Andric // | BL __llvm_slsblr_thunk_... | 4035ffd83dbSDimitry Andric // | instJ | 4045ffd83dbSDimitry Andric // | ... | 4055ffd83dbSDimitry Andric // |-----------------------------| 4065ffd83dbSDimitry Andric // 407*0fca6ea1SDimitry Andric // __llvm_slsblr_thunk_...: 4085ffd83dbSDimitry Andric // |-----------------------------| 409*0fca6ea1SDimitry Andric // | MOV x16, xN | 410*0fca6ea1SDimitry Andric // | BR* x16 or BR* x16, xM | 4115ffd83dbSDimitry Andric // | barrierInsts | 4125ffd83dbSDimitry Andric // |-----------------------------| 4135ffd83dbSDimitry Andric // 414*0fca6ea1SDimitry Andric // This function needs to transform BLR* instruction into BL with the correct 415*0fca6ea1SDimitry Andric // thunk name and lazily create the thunk if it does not exist yet. 4165ffd83dbSDimitry Andric // 4175ffd83dbSDimitry Andric // Since linkers are allowed to clobber X16 and X17 on function calls, the 418*0fca6ea1SDimitry Andric // above mitigation only works if the original BLR* instruction had neither 419*0fca6ea1SDimitry Andric // X16 nor X17 as one of its operands. Code generation before must make sure 420*0fca6ea1SDimitry Andric // that no such BLR* instruction was produced if the mitigation is enabled. 4215ffd83dbSDimitry Andric 4225ffd83dbSDimitry Andric MachineInstr &BLR = *MBBI; 4235ffd83dbSDimitry Andric assert(isBLR(BLR)); 424*0fca6ea1SDimitry Andric const ThunkKind &Kind = *getThunkKind(BLR.getOpcode()); 425*0fca6ea1SDimitry Andric 426*0fca6ea1SDimitry Andric unsigned NumRegOperands = Kind.HasXmOperand ? 2 : 1; 427*0fca6ea1SDimitry Andric assert(BLR.getNumExplicitOperands() == NumRegOperands && 428*0fca6ea1SDimitry Andric "Expected one or two register inputs"); 429*0fca6ea1SDimitry Andric Register Xn = BLR.getOperand(0).getReg(); 430*0fca6ea1SDimitry Andric Register Xm = 431*0fca6ea1SDimitry Andric Kind.HasXmOperand ? BLR.getOperand(1).getReg() : AArch64::NoRegister; 432*0fca6ea1SDimitry Andric 4335ffd83dbSDimitry Andric DebugLoc DL = BLR.getDebugLoc(); 4345ffd83dbSDimitry Andric 4355ffd83dbSDimitry Andric MachineFunction &MF = *MBBI->getMF(); 4365ffd83dbSDimitry Andric MCContext &Context = MBB.getParent()->getContext(); 437*0fca6ea1SDimitry Andric const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); 4385ffd83dbSDimitry Andric 439*0fca6ea1SDimitry Andric auto ThunkName = createThunkName(Kind, Xn, Xm); 440*0fca6ea1SDimitry Andric MCSymbol *Sym = Context.getOrCreateSymbol(ThunkName); 441*0fca6ea1SDimitry Andric 442*0fca6ea1SDimitry Andric if (!Thunks.get(Kind.Id, Xn, Xm)) { 443*0fca6ea1SDimitry Andric StringRef TargetAttrs = Kind.NeedsPAuth ? "+pauth" : ""; 444*0fca6ea1SDimitry Andric Thunks.set(Kind.Id, Xn, Xm); 445*0fca6ea1SDimitry Andric createThunkFunction(MMI, ThunkName, ComdatThunks, TargetAttrs); 446*0fca6ea1SDimitry Andric } 447*0fca6ea1SDimitry Andric 448*0fca6ea1SDimitry Andric MachineInstr *BL = BuildMI(MBB, MBBI, DL, TII->get(AArch64::BL)).addSym(Sym); 4495ffd83dbSDimitry Andric 4505ffd83dbSDimitry Andric // Now copy the implicit operands from BLR to BL and copy other necessary 4515ffd83dbSDimitry Andric // info. 4525ffd83dbSDimitry Andric // However, both BLR and BL instructions implictly use SP and implicitly 4535ffd83dbSDimitry Andric // define LR. Blindly copying implicit operands would result in SP and LR 4545ffd83dbSDimitry Andric // operands to be present multiple times. While this may not be too much of 4555ffd83dbSDimitry Andric // an issue, let's avoid that for cleanliness, by removing those implicit 4565ffd83dbSDimitry Andric // operands from the BL created above before we copy over all implicit 4575ffd83dbSDimitry Andric // operands from the BLR. 4585ffd83dbSDimitry Andric int ImpLROpIdx = -1; 4595ffd83dbSDimitry Andric int ImpSPOpIdx = -1; 4605ffd83dbSDimitry Andric for (unsigned OpIdx = BL->getNumExplicitOperands(); 4615ffd83dbSDimitry Andric OpIdx < BL->getNumOperands(); OpIdx++) { 4625ffd83dbSDimitry Andric MachineOperand Op = BL->getOperand(OpIdx); 4635ffd83dbSDimitry Andric if (!Op.isReg()) 4645ffd83dbSDimitry Andric continue; 4655ffd83dbSDimitry Andric if (Op.getReg() == AArch64::LR && Op.isDef()) 4665ffd83dbSDimitry Andric ImpLROpIdx = OpIdx; 4675ffd83dbSDimitry Andric if (Op.getReg() == AArch64::SP && !Op.isDef()) 4685ffd83dbSDimitry Andric ImpSPOpIdx = OpIdx; 4695ffd83dbSDimitry Andric } 4705ffd83dbSDimitry Andric assert(ImpLROpIdx != -1); 4715ffd83dbSDimitry Andric assert(ImpSPOpIdx != -1); 4725ffd83dbSDimitry Andric int FirstOpIdxToRemove = std::max(ImpLROpIdx, ImpSPOpIdx); 4735ffd83dbSDimitry Andric int SecondOpIdxToRemove = std::min(ImpLROpIdx, ImpSPOpIdx); 47481ad6265SDimitry Andric BL->removeOperand(FirstOpIdxToRemove); 47581ad6265SDimitry Andric BL->removeOperand(SecondOpIdxToRemove); 4765ffd83dbSDimitry Andric // Now copy over the implicit operands from the original BLR 4775ffd83dbSDimitry Andric BL->copyImplicitOps(MF, BLR); 4785ffd83dbSDimitry Andric MF.moveCallSiteInfo(&BLR, BL); 479*0fca6ea1SDimitry Andric // Also add the register operands of the original BLR* instruction 480*0fca6ea1SDimitry Andric // as being used in the called thunk. 481*0fca6ea1SDimitry Andric for (unsigned OpIdx = 0; OpIdx < NumRegOperands; ++OpIdx) { 482*0fca6ea1SDimitry Andric MachineOperand &Op = BLR.getOperand(OpIdx); 483*0fca6ea1SDimitry Andric BL->addOperand(MachineOperand::CreateReg(Op.getReg(), /*isDef=*/false, 484*0fca6ea1SDimitry Andric /*isImp=*/true, Op.isKill())); 485*0fca6ea1SDimitry Andric } 4865ffd83dbSDimitry Andric // Remove BLR instruction 4875ffd83dbSDimitry Andric MBB.erase(MBBI); 4885ffd83dbSDimitry Andric } 4895ffd83dbSDimitry Andric 490*0fca6ea1SDimitry Andric bool SLSHardeningInserter::hardenBLRs(MachineModuleInfo &MMI, 491*0fca6ea1SDimitry Andric MachineBasicBlock &MBB, 492*0fca6ea1SDimitry Andric ThunksSet &Thunks) { 4935ffd83dbSDimitry Andric bool Modified = false; 49406c3fb27SDimitry Andric MachineBasicBlock::instr_iterator MBBI = MBB.instr_begin(), 49506c3fb27SDimitry Andric E = MBB.instr_end(); 49606c3fb27SDimitry Andric MachineBasicBlock::instr_iterator NextMBBI; 4975ffd83dbSDimitry Andric for (; MBBI != E; MBBI = NextMBBI) { 4985ffd83dbSDimitry Andric MachineInstr &MI = *MBBI; 4995ffd83dbSDimitry Andric NextMBBI = std::next(MBBI); 5005ffd83dbSDimitry Andric if (isBLR(MI)) { 501*0fca6ea1SDimitry Andric convertBLRToBL(MMI, MBB, MBBI, Thunks); 5025ffd83dbSDimitry Andric Modified = true; 5035ffd83dbSDimitry Andric } 5045ffd83dbSDimitry Andric } 5055ffd83dbSDimitry Andric return Modified; 5065ffd83dbSDimitry Andric } 5075ffd83dbSDimitry Andric 5085ffd83dbSDimitry Andric namespace { 509*0fca6ea1SDimitry Andric class AArch64SLSHardening : public ThunkInserterPass<SLSHardeningInserter> { 5105ffd83dbSDimitry Andric public: 5115ffd83dbSDimitry Andric static char ID; 5125ffd83dbSDimitry Andric 513*0fca6ea1SDimitry Andric AArch64SLSHardening() : ThunkInserterPass(ID) {} 5145ffd83dbSDimitry Andric 515*0fca6ea1SDimitry Andric StringRef getPassName() const override { return AARCH64_SLS_HARDENING_NAME; } 5165ffd83dbSDimitry Andric }; 5175ffd83dbSDimitry Andric 5185ffd83dbSDimitry Andric } // end anonymous namespace 5195ffd83dbSDimitry Andric 520*0fca6ea1SDimitry Andric char AArch64SLSHardening::ID = 0; 5215ffd83dbSDimitry Andric 522*0fca6ea1SDimitry Andric INITIALIZE_PASS(AArch64SLSHardening, "aarch64-sls-hardening", 523*0fca6ea1SDimitry Andric AARCH64_SLS_HARDENING_NAME, false, false) 5245ffd83dbSDimitry Andric 525*0fca6ea1SDimitry Andric FunctionPass *llvm::createAArch64SLSHardeningPass() { 526*0fca6ea1SDimitry Andric return new AArch64SLSHardening(); 5275ffd83dbSDimitry Andric } 528