xref: /freebsd/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64SLSHardening.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
operator |=(const ThunksSet & Other)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 
get(ThunkKind::ThunkKindId Kind,Register Xn,Register Xm)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 
set(ThunkKind::ThunkKindId Kind,Register Xn,Register Xm)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 
getBitmask(ThunkKind::ThunkKindId Kind,Register Xm)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:
getThunkPrefix__anon8cb708490111::SLSHardeningInserter140*0fca6ea1SDimitry Andric   const char *getThunkPrefix() { return CommonNamePrefix.data(); }
mayUseThunk__anon8cb708490111::SLSHardeningInserter141*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.
getThunkKind(unsigned OriginalOpcode)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 
isBLR(const MachineInstr & MI)195*0fca6ea1SDimitry Andric static bool isBLR(const MachineInstr &MI) {
196*0fca6ea1SDimitry Andric   return getThunkKind(MI.getOpcode()) != nullptr;
197*0fca6ea1SDimitry Andric }
198*0fca6ea1SDimitry Andric 
indexOfXReg(Register Reg)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 
xRegByIndex(unsigned N)217*0fca6ea1SDimitry Andric Register ThunksSet::xRegByIndex(unsigned N) {
218*0fca6ea1SDimitry Andric   return AArch64::GPR64RegClass.getRegister(N);
219*0fca6ea1SDimitry Andric }
2205ffd83dbSDimitry Andric 
insertSpeculationBarrier(const AArch64Subtarget * ST,MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,DebugLoc DL,bool AlwaysUseISBDSB=false)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 
insertThunks(MachineModuleInfo & MMI,MachineFunction & MF,ThunksSet ExistingThunks)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 
hardenReturnsAndBRs(MachineModuleInfo & MMI,MachineBasicBlock & MBB)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).
createThunkName(const ThunkKind & Kind,Register Xn,Register Xm)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>
parseThunkName(StringRef ThunkName)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 
populateThunk(MachineFunction & MF)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 
convertBLRToBL(MachineModuleInfo & MMI,MachineBasicBlock & MBB,MachineBasicBlock::instr_iterator MBBI,ThunksSet & Thunks)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 
hardenBLRs(MachineModuleInfo & MMI,MachineBasicBlock & MBB,ThunksSet & Thunks)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 
AArch64SLSHardening()513*0fca6ea1SDimitry Andric   AArch64SLSHardening() : ThunkInserterPass(ID) {}
5145ffd83dbSDimitry Andric 
getPassName() const515*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 
createAArch64SLSHardeningPass()525*0fca6ea1SDimitry Andric FunctionPass *llvm::createAArch64SLSHardeningPass() {
526*0fca6ea1SDimitry Andric   return new AArch64SLSHardening();
5275ffd83dbSDimitry Andric }
528