xref: /openbsd-src/gnu/llvm/llvm/lib/Target/ARM/ARMSLSHardening.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
173471bf0Spatrick //===- ARMSLSHardening.cpp - Harden Straight Line Missspeculation ---------===//
273471bf0Spatrick //
373471bf0Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
473471bf0Spatrick // See https://llvm.org/LICENSE.txt for license information.
573471bf0Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
673471bf0Spatrick //
773471bf0Spatrick //===----------------------------------------------------------------------===//
873471bf0Spatrick //
973471bf0Spatrick // This file contains a pass to insert code to mitigate against side channel
1073471bf0Spatrick // vulnerabilities that may happen under straight line miss-speculation.
1173471bf0Spatrick //
1273471bf0Spatrick //===----------------------------------------------------------------------===//
1373471bf0Spatrick 
1473471bf0Spatrick #include "ARM.h"
1573471bf0Spatrick #include "ARMInstrInfo.h"
1673471bf0Spatrick #include "ARMSubtarget.h"
1773471bf0Spatrick #include "llvm/CodeGen/IndirectThunks.h"
1873471bf0Spatrick #include "llvm/CodeGen/MachineBasicBlock.h"
1973471bf0Spatrick #include "llvm/CodeGen/MachineFunction.h"
2073471bf0Spatrick #include "llvm/CodeGen/MachineFunctionPass.h"
2173471bf0Spatrick #include "llvm/CodeGen/MachineInstr.h"
2273471bf0Spatrick #include "llvm/CodeGen/MachineInstrBuilder.h"
2373471bf0Spatrick #include "llvm/CodeGen/MachineOperand.h"
2473471bf0Spatrick #include "llvm/IR/DebugLoc.h"
2573471bf0Spatrick #include <cassert>
2673471bf0Spatrick 
2773471bf0Spatrick using namespace llvm;
2873471bf0Spatrick 
2973471bf0Spatrick #define DEBUG_TYPE "arm-sls-hardening"
3073471bf0Spatrick 
3173471bf0Spatrick #define ARM_SLS_HARDENING_NAME "ARM sls hardening pass"
3273471bf0Spatrick 
3373471bf0Spatrick namespace {
3473471bf0Spatrick 
3573471bf0Spatrick class ARMSLSHardening : public MachineFunctionPass {
3673471bf0Spatrick public:
3773471bf0Spatrick   const TargetInstrInfo *TII;
3873471bf0Spatrick   const ARMSubtarget *ST;
3973471bf0Spatrick 
4073471bf0Spatrick   static char ID;
4173471bf0Spatrick 
ARMSLSHardening()4273471bf0Spatrick   ARMSLSHardening() : MachineFunctionPass(ID) {
4373471bf0Spatrick     initializeARMSLSHardeningPass(*PassRegistry::getPassRegistry());
4473471bf0Spatrick   }
4573471bf0Spatrick 
4673471bf0Spatrick   bool runOnMachineFunction(MachineFunction &Fn) override;
4773471bf0Spatrick 
getPassName() const4873471bf0Spatrick   StringRef getPassName() const override { return ARM_SLS_HARDENING_NAME; }
4973471bf0Spatrick 
getAnalysisUsage(AnalysisUsage & AU) const5073471bf0Spatrick   void getAnalysisUsage(AnalysisUsage &AU) const override {
5173471bf0Spatrick     AU.setPreservesCFG();
5273471bf0Spatrick     MachineFunctionPass::getAnalysisUsage(AU);
5373471bf0Spatrick   }
5473471bf0Spatrick 
5573471bf0Spatrick private:
5673471bf0Spatrick   bool hardenReturnsAndBRs(MachineBasicBlock &MBB) const;
5773471bf0Spatrick   bool hardenIndirectCalls(MachineBasicBlock &MBB) const;
5873471bf0Spatrick   MachineBasicBlock &
5973471bf0Spatrick   ConvertIndirectCallToIndirectJump(MachineBasicBlock &MBB,
6073471bf0Spatrick                                     MachineBasicBlock::iterator) const;
6173471bf0Spatrick };
6273471bf0Spatrick 
6373471bf0Spatrick } // end anonymous namespace
6473471bf0Spatrick 
6573471bf0Spatrick char ARMSLSHardening::ID = 0;
6673471bf0Spatrick 
6773471bf0Spatrick INITIALIZE_PASS(ARMSLSHardening, "arm-sls-hardening",
6873471bf0Spatrick                 ARM_SLS_HARDENING_NAME, false, false)
6973471bf0Spatrick 
insertSpeculationBarrier(const ARMSubtarget * ST,MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI,DebugLoc DL,bool AlwaysUseISBDSB=false)7073471bf0Spatrick static void insertSpeculationBarrier(const ARMSubtarget *ST,
7173471bf0Spatrick                                      MachineBasicBlock &MBB,
7273471bf0Spatrick                                      MachineBasicBlock::iterator MBBI,
7373471bf0Spatrick                                      DebugLoc DL,
7473471bf0Spatrick                                      bool AlwaysUseISBDSB = false) {
7573471bf0Spatrick   assert(MBBI != MBB.begin() &&
7673471bf0Spatrick          "Must not insert SpeculationBarrierEndBB as only instruction in MBB.");
7773471bf0Spatrick   assert(std::prev(MBBI)->isBarrier() &&
7873471bf0Spatrick          "SpeculationBarrierEndBB must only follow unconditional control flow "
7973471bf0Spatrick          "instructions.");
8073471bf0Spatrick   assert(std::prev(MBBI)->isTerminator() &&
8173471bf0Spatrick          "SpeculationBarrierEndBB must only follow terminators.");
8273471bf0Spatrick   const TargetInstrInfo *TII = ST->getInstrInfo();
8373471bf0Spatrick   assert(ST->hasDataBarrier() || ST->hasSB());
8473471bf0Spatrick   bool ProduceSB = ST->hasSB() && !AlwaysUseISBDSB;
8573471bf0Spatrick   unsigned BarrierOpc =
8673471bf0Spatrick       ProduceSB ? (ST->isThumb() ? ARM::t2SpeculationBarrierSBEndBB
8773471bf0Spatrick                                  : ARM::SpeculationBarrierSBEndBB)
8873471bf0Spatrick                 : (ST->isThumb() ? ARM::t2SpeculationBarrierISBDSBEndBB
8973471bf0Spatrick                                  : ARM::SpeculationBarrierISBDSBEndBB);
9073471bf0Spatrick   if (MBBI == MBB.end() || !isSpeculationBarrierEndBBOpcode(MBBI->getOpcode()))
9173471bf0Spatrick     BuildMI(MBB, MBBI, DL, TII->get(BarrierOpc));
9273471bf0Spatrick }
9373471bf0Spatrick 
runOnMachineFunction(MachineFunction & MF)9473471bf0Spatrick bool ARMSLSHardening::runOnMachineFunction(MachineFunction &MF) {
9573471bf0Spatrick   ST = &MF.getSubtarget<ARMSubtarget>();
9673471bf0Spatrick   TII = MF.getSubtarget().getInstrInfo();
9773471bf0Spatrick 
9873471bf0Spatrick   bool Modified = false;
9973471bf0Spatrick   for (auto &MBB : MF) {
10073471bf0Spatrick     Modified |= hardenReturnsAndBRs(MBB);
10173471bf0Spatrick     Modified |= hardenIndirectCalls(MBB);
10273471bf0Spatrick   }
10373471bf0Spatrick 
10473471bf0Spatrick   return Modified;
10573471bf0Spatrick }
10673471bf0Spatrick 
hardenReturnsAndBRs(MachineBasicBlock & MBB) const10773471bf0Spatrick bool ARMSLSHardening::hardenReturnsAndBRs(MachineBasicBlock &MBB) const {
10873471bf0Spatrick   if (!ST->hardenSlsRetBr())
10973471bf0Spatrick     return false;
11073471bf0Spatrick   assert(!ST->isThumb1Only());
11173471bf0Spatrick   bool Modified = false;
11273471bf0Spatrick   MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(), E = MBB.end();
11373471bf0Spatrick   MachineBasicBlock::iterator NextMBBI;
11473471bf0Spatrick   for (; MBBI != E; MBBI = NextMBBI) {
11573471bf0Spatrick     MachineInstr &MI = *MBBI;
11673471bf0Spatrick     NextMBBI = std::next(MBBI);
11773471bf0Spatrick     if (isIndirectControlFlowNotComingBack(MI)) {
11873471bf0Spatrick       assert(MI.isTerminator());
11973471bf0Spatrick       assert(!TII->isPredicated(MI));
12073471bf0Spatrick       insertSpeculationBarrier(ST, MBB, std::next(MBBI), MI.getDebugLoc());
12173471bf0Spatrick       Modified = true;
12273471bf0Spatrick     }
12373471bf0Spatrick   }
12473471bf0Spatrick   return Modified;
12573471bf0Spatrick }
12673471bf0Spatrick 
12773471bf0Spatrick static const char SLSBLRNamePrefix[] = "__llvm_slsblr_thunk_";
12873471bf0Spatrick 
12973471bf0Spatrick static const struct ThunkNameRegMode {
13073471bf0Spatrick   const char* Name;
13173471bf0Spatrick   Register Reg;
13273471bf0Spatrick   bool isThumb;
13373471bf0Spatrick } SLSBLRThunks[] = {
13473471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r0", ARM::R0, false},
13573471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r1", ARM::R1, false},
13673471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r2", ARM::R2, false},
13773471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r3", ARM::R3, false},
13873471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r4", ARM::R4, false},
13973471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r5", ARM::R5, false},
14073471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r6", ARM::R6, false},
14173471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r7", ARM::R7, false},
14273471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r8", ARM::R8, false},
14373471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r9", ARM::R9, false},
14473471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r10", ARM::R10, false},
14573471bf0Spatrick     {"__llvm_slsblr_thunk_arm_r11", ARM::R11, false},
14673471bf0Spatrick     {"__llvm_slsblr_thunk_arm_sp", ARM::SP, false},
14773471bf0Spatrick     {"__llvm_slsblr_thunk_arm_pc", ARM::PC, false},
14873471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r0", ARM::R0, true},
14973471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r1", ARM::R1, true},
15073471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r2", ARM::R2, true},
15173471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r3", ARM::R3, true},
15273471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r4", ARM::R4, true},
15373471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r5", ARM::R5, true},
15473471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r6", ARM::R6, true},
15573471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r7", ARM::R7, true},
15673471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r8", ARM::R8, true},
15773471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r9", ARM::R9, true},
15873471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r10", ARM::R10, true},
15973471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_r11", ARM::R11, true},
16073471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_sp", ARM::SP, true},
16173471bf0Spatrick     {"__llvm_slsblr_thunk_thumb_pc", ARM::PC, true},
16273471bf0Spatrick };
16373471bf0Spatrick 
164*d415bd75Srobert // An enum for tracking whether Arm and Thumb thunks have been inserted into the
165*d415bd75Srobert // current module so far.
166*d415bd75Srobert enum ArmInsertedThunks { ArmThunk = 1, ThumbThunk = 2 };
167*d415bd75Srobert 
operator |=(ArmInsertedThunks & X,ArmInsertedThunks Y)168*d415bd75Srobert inline ArmInsertedThunks &operator|=(ArmInsertedThunks &X,
169*d415bd75Srobert                                      ArmInsertedThunks Y) {
170*d415bd75Srobert   return X = static_cast<ArmInsertedThunks>(X | Y);
171*d415bd75Srobert }
172*d415bd75Srobert 
17373471bf0Spatrick namespace {
174*d415bd75Srobert struct SLSBLRThunkInserter
175*d415bd75Srobert     : ThunkInserter<SLSBLRThunkInserter, ArmInsertedThunks> {
getThunkPrefix__anona645888e0211::SLSBLRThunkInserter17673471bf0Spatrick   const char *getThunkPrefix() { return SLSBLRNamePrefix; }
mayUseThunk__anona645888e0211::SLSBLRThunkInserter177*d415bd75Srobert   bool mayUseThunk(const MachineFunction &MF,
178*d415bd75Srobert                    ArmInsertedThunks InsertedThunks) {
179*d415bd75Srobert     if ((InsertedThunks & ArmThunk &&
180*d415bd75Srobert          !MF.getSubtarget<ARMSubtarget>().isThumb()) ||
181*d415bd75Srobert         (InsertedThunks & ThumbThunk &&
182*d415bd75Srobert          MF.getSubtarget<ARMSubtarget>().isThumb()))
183*d415bd75Srobert       return false;
18473471bf0Spatrick     ComdatThunks &= !MF.getSubtarget<ARMSubtarget>().hardenSlsNoComdat();
18573471bf0Spatrick     // FIXME: This could also check if there are any indirect calls in the
18673471bf0Spatrick     // function to more accurately reflect if a thunk will be needed.
18773471bf0Spatrick     return MF.getSubtarget<ARMSubtarget>().hardenSlsBlr();
18873471bf0Spatrick   }
189*d415bd75Srobert   ArmInsertedThunks insertThunks(MachineModuleInfo &MMI, MachineFunction &MF);
19073471bf0Spatrick   void populateThunk(MachineFunction &MF);
19173471bf0Spatrick 
19273471bf0Spatrick private:
19373471bf0Spatrick   bool ComdatThunks = true;
19473471bf0Spatrick };
19573471bf0Spatrick } // namespace
19673471bf0Spatrick 
insertThunks(MachineModuleInfo & MMI,MachineFunction & MF)197*d415bd75Srobert ArmInsertedThunks SLSBLRThunkInserter::insertThunks(MachineModuleInfo &MMI,
198*d415bd75Srobert                                                     MachineFunction &MF) {
19973471bf0Spatrick   // FIXME: It probably would be possible to filter which thunks to produce
20073471bf0Spatrick   // based on which registers are actually used in indirect calls in this
20173471bf0Spatrick   // function. But would that be a worthwhile optimization?
202*d415bd75Srobert   const ARMSubtarget *ST = &MF.getSubtarget<ARMSubtarget>();
20373471bf0Spatrick   for (auto T : SLSBLRThunks)
204*d415bd75Srobert     if (ST->isThumb() == T.isThumb)
20573471bf0Spatrick       createThunkFunction(MMI, T.Name, ComdatThunks);
206*d415bd75Srobert   return ST->isThumb() ? ThumbThunk : ArmThunk;
20773471bf0Spatrick }
20873471bf0Spatrick 
populateThunk(MachineFunction & MF)20973471bf0Spatrick void SLSBLRThunkInserter::populateThunk(MachineFunction &MF) {
21073471bf0Spatrick   // FIXME: How to better communicate Register number, rather than through
21173471bf0Spatrick   // name and lookup table?
21273471bf0Spatrick   assert(MF.getName().startswith(getThunkPrefix()));
21373471bf0Spatrick   auto ThunkIt = llvm::find_if(
21473471bf0Spatrick       SLSBLRThunks, [&MF](auto T) { return T.Name == MF.getName(); });
21573471bf0Spatrick   assert(ThunkIt != std::end(SLSBLRThunks));
21673471bf0Spatrick   Register ThunkReg = ThunkIt->Reg;
21773471bf0Spatrick   bool isThumb = ThunkIt->isThumb;
21873471bf0Spatrick 
21973471bf0Spatrick   const TargetInstrInfo *TII = MF.getSubtarget<ARMSubtarget>().getInstrInfo();
22073471bf0Spatrick   MachineBasicBlock *Entry = &MF.front();
22173471bf0Spatrick   Entry->clear();
22273471bf0Spatrick 
22373471bf0Spatrick   //  These thunks need to consist of the following instructions:
22473471bf0Spatrick   //  __llvm_slsblr_thunk_(arm/thumb)_rN:
22573471bf0Spatrick   //      bx  rN
22673471bf0Spatrick   //      barrierInsts
22773471bf0Spatrick   Entry->addLiveIn(ThunkReg);
22873471bf0Spatrick   if (isThumb)
22973471bf0Spatrick     BuildMI(Entry, DebugLoc(), TII->get(ARM::tBX))
23073471bf0Spatrick         .addReg(ThunkReg)
23173471bf0Spatrick         .add(predOps(ARMCC::AL));
23273471bf0Spatrick   else
23373471bf0Spatrick     BuildMI(Entry, DebugLoc(), TII->get(ARM::BX))
23473471bf0Spatrick         .addReg(ThunkReg);
23573471bf0Spatrick 
23673471bf0Spatrick   // Make sure the thunks do not make use of the SB extension in case there is
23773471bf0Spatrick   // a function somewhere that will call to it that for some reason disabled
23873471bf0Spatrick   // the SB extension locally on that function, even though it's enabled for
23973471bf0Spatrick   // the module otherwise. Therefore set AlwaysUseISBSDB to true.
24073471bf0Spatrick   insertSpeculationBarrier(&MF.getSubtarget<ARMSubtarget>(), *Entry,
24173471bf0Spatrick                            Entry->end(), DebugLoc(), true /*AlwaysUseISBDSB*/);
24273471bf0Spatrick }
24373471bf0Spatrick 
ConvertIndirectCallToIndirectJump(MachineBasicBlock & MBB,MachineBasicBlock::iterator MBBI) const24473471bf0Spatrick MachineBasicBlock &ARMSLSHardening::ConvertIndirectCallToIndirectJump(
24573471bf0Spatrick     MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const {
24673471bf0Spatrick   // Transform an indirect call to an indirect jump as follows:
24773471bf0Spatrick   // Before:
24873471bf0Spatrick   //   |-----------------------------|
24973471bf0Spatrick   //   |      ...                    |
25073471bf0Spatrick   //   |  instI                      |
25173471bf0Spatrick   //   |  BLX rN                     |
25273471bf0Spatrick   //   |  instJ                      |
25373471bf0Spatrick   //   |      ...                    |
25473471bf0Spatrick   //   |-----------------------------|
25573471bf0Spatrick   //
25673471bf0Spatrick   // After:
25773471bf0Spatrick   //   |----------   -------------------------|
25873471bf0Spatrick   //   |      ...                             |
25973471bf0Spatrick   //   |  instI                               |
26073471bf0Spatrick   //   |  *call* __llvm_slsblr_thunk_mode_xN  |
26173471bf0Spatrick   //   |  instJ                               |
26273471bf0Spatrick   //   |      ...                             |
26373471bf0Spatrick   //   |--------------------------------------|
26473471bf0Spatrick   //
26573471bf0Spatrick   //   __llvm_slsblr_thunk_mode_xN:
26673471bf0Spatrick   //   |-----------------------------|
26773471bf0Spatrick   //   |  BX rN                      |
26873471bf0Spatrick   //   |  barrierInsts               |
26973471bf0Spatrick   //   |-----------------------------|
27073471bf0Spatrick   //
27173471bf0Spatrick   // The __llvm_slsblr_thunk_mode_xN thunks are created by the
27273471bf0Spatrick   // SLSBLRThunkInserter.
27373471bf0Spatrick   // This function merely needs to transform an indirect call to a direct call
27473471bf0Spatrick   // to __llvm_slsblr_thunk_xN.
27573471bf0Spatrick   MachineInstr &IndirectCall = *MBBI;
27673471bf0Spatrick   assert(isIndirectCall(IndirectCall) && !IndirectCall.isReturn());
27773471bf0Spatrick   int RegOpIdxOnIndirectCall = -1;
27873471bf0Spatrick   bool isThumb;
27973471bf0Spatrick   switch (IndirectCall.getOpcode()) {
28073471bf0Spatrick   case ARM::BLX:   // !isThumb2
28173471bf0Spatrick   case ARM::BLX_noip:   // !isThumb2
28273471bf0Spatrick     isThumb = false;
28373471bf0Spatrick     RegOpIdxOnIndirectCall = 0;
28473471bf0Spatrick     break;
28573471bf0Spatrick   case ARM::tBLXr:      // isThumb2
28673471bf0Spatrick   case ARM::tBLXr_noip: // isThumb2
28773471bf0Spatrick     isThumb = true;
28873471bf0Spatrick     RegOpIdxOnIndirectCall = 2;
28973471bf0Spatrick     break;
29073471bf0Spatrick   default:
29173471bf0Spatrick     llvm_unreachable("unhandled Indirect Call");
29273471bf0Spatrick   }
29373471bf0Spatrick 
29473471bf0Spatrick   Register Reg = IndirectCall.getOperand(RegOpIdxOnIndirectCall).getReg();
29573471bf0Spatrick   // Since linkers are allowed to clobber R12 on function calls, the above
29673471bf0Spatrick   // mitigation only works if the original indirect call instruction was not
29773471bf0Spatrick   // using R12. Code generation before must make sure that no indirect call
29873471bf0Spatrick   // using R12 was produced if the mitigation is enabled.
29973471bf0Spatrick   // Also, the transformation is incorrect if the indirect call uses LR, so
30073471bf0Spatrick   // also have to avoid that.
30173471bf0Spatrick   assert(Reg != ARM::R12 && Reg != ARM::LR);
30273471bf0Spatrick   bool RegIsKilled = IndirectCall.getOperand(RegOpIdxOnIndirectCall).isKill();
30373471bf0Spatrick 
30473471bf0Spatrick   DebugLoc DL = IndirectCall.getDebugLoc();
30573471bf0Spatrick 
30673471bf0Spatrick   MachineFunction &MF = *MBBI->getMF();
30773471bf0Spatrick   auto ThunkIt = llvm::find_if(SLSBLRThunks, [Reg, isThumb](auto T) {
30873471bf0Spatrick     return T.Reg == Reg && T.isThumb == isThumb;
30973471bf0Spatrick   });
31073471bf0Spatrick   assert(ThunkIt != std::end(SLSBLRThunks));
31173471bf0Spatrick   Module *M = MF.getFunction().getParent();
31273471bf0Spatrick   const GlobalValue *GV = cast<GlobalValue>(M->getNamedValue(ThunkIt->Name));
31373471bf0Spatrick 
31473471bf0Spatrick   MachineInstr *BL =
31573471bf0Spatrick       isThumb ? BuildMI(MBB, MBBI, DL, TII->get(ARM::tBL))
31673471bf0Spatrick                     .addImm(IndirectCall.getOperand(0).getImm())
31773471bf0Spatrick                     .addReg(IndirectCall.getOperand(1).getReg())
31873471bf0Spatrick                     .addGlobalAddress(GV)
31973471bf0Spatrick               : BuildMI(MBB, MBBI, DL, TII->get(ARM::BL)).addGlobalAddress(GV);
32073471bf0Spatrick 
32173471bf0Spatrick   // Now copy the implicit operands from IndirectCall to BL and copy other
32273471bf0Spatrick   // necessary info.
32373471bf0Spatrick   // However, both IndirectCall and BL instructions implictly use SP and
32473471bf0Spatrick   // implicitly define LR. Blindly copying implicit operands would result in SP
32573471bf0Spatrick   // and LR operands to be present multiple times. While this may not be too
32673471bf0Spatrick   // much of an issue, let's avoid that for cleanliness, by removing those
32773471bf0Spatrick   // implicit operands from the BL created above before we copy over all
32873471bf0Spatrick   // implicit operands from the IndirectCall.
32973471bf0Spatrick   int ImpLROpIdx = -1;
33073471bf0Spatrick   int ImpSPOpIdx = -1;
33173471bf0Spatrick   for (unsigned OpIdx = BL->getNumExplicitOperands();
33273471bf0Spatrick        OpIdx < BL->getNumOperands(); OpIdx++) {
33373471bf0Spatrick     MachineOperand Op = BL->getOperand(OpIdx);
33473471bf0Spatrick     if (!Op.isReg())
33573471bf0Spatrick       continue;
33673471bf0Spatrick     if (Op.getReg() == ARM::LR && Op.isDef())
33773471bf0Spatrick       ImpLROpIdx = OpIdx;
33873471bf0Spatrick     if (Op.getReg() == ARM::SP && !Op.isDef())
33973471bf0Spatrick       ImpSPOpIdx = OpIdx;
34073471bf0Spatrick   }
34173471bf0Spatrick   assert(ImpLROpIdx != -1);
34273471bf0Spatrick   assert(ImpSPOpIdx != -1);
34373471bf0Spatrick   int FirstOpIdxToRemove = std::max(ImpLROpIdx, ImpSPOpIdx);
34473471bf0Spatrick   int SecondOpIdxToRemove = std::min(ImpLROpIdx, ImpSPOpIdx);
345*d415bd75Srobert   BL->removeOperand(FirstOpIdxToRemove);
346*d415bd75Srobert   BL->removeOperand(SecondOpIdxToRemove);
34773471bf0Spatrick   // Now copy over the implicit operands from the original IndirectCall
34873471bf0Spatrick   BL->copyImplicitOps(MF, IndirectCall);
34973471bf0Spatrick   MF.moveCallSiteInfo(&IndirectCall, BL);
35073471bf0Spatrick   // Also add the register called in the IndirectCall as being used in the
35173471bf0Spatrick   // called thunk.
35273471bf0Spatrick   BL->addOperand(MachineOperand::CreateReg(Reg, false /*isDef*/, true /*isImp*/,
35373471bf0Spatrick                                            RegIsKilled /*isKill*/));
35473471bf0Spatrick   // Remove IndirectCallinstruction
35573471bf0Spatrick   MBB.erase(MBBI);
35673471bf0Spatrick   return MBB;
35773471bf0Spatrick }
35873471bf0Spatrick 
hardenIndirectCalls(MachineBasicBlock & MBB) const35973471bf0Spatrick bool ARMSLSHardening::hardenIndirectCalls(MachineBasicBlock &MBB) const {
36073471bf0Spatrick   if (!ST->hardenSlsBlr())
36173471bf0Spatrick     return false;
36273471bf0Spatrick   bool Modified = false;
36373471bf0Spatrick   MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
36473471bf0Spatrick   MachineBasicBlock::iterator NextMBBI;
36573471bf0Spatrick   for (; MBBI != E; MBBI = NextMBBI) {
36673471bf0Spatrick     MachineInstr &MI = *MBBI;
36773471bf0Spatrick     NextMBBI = std::next(MBBI);
36873471bf0Spatrick     // Tail calls are both indirect calls and "returns".
36973471bf0Spatrick     // They are also indirect jumps, so should be handled by sls-harden-retbr,
37073471bf0Spatrick     // rather than sls-harden-blr.
37173471bf0Spatrick     if (isIndirectCall(MI) && !MI.isReturn()) {
37273471bf0Spatrick       ConvertIndirectCallToIndirectJump(MBB, MBBI);
37373471bf0Spatrick       Modified = true;
37473471bf0Spatrick     }
37573471bf0Spatrick   }
37673471bf0Spatrick   return Modified;
37773471bf0Spatrick }
37873471bf0Spatrick 
37973471bf0Spatrick 
38073471bf0Spatrick 
createARMSLSHardeningPass()38173471bf0Spatrick FunctionPass *llvm::createARMSLSHardeningPass() {
38273471bf0Spatrick   return new ARMSLSHardening();
38373471bf0Spatrick }
38473471bf0Spatrick 
38573471bf0Spatrick namespace {
38673471bf0Spatrick class ARMIndirectThunks : public MachineFunctionPass {
38773471bf0Spatrick public:
38873471bf0Spatrick   static char ID;
38973471bf0Spatrick 
ARMIndirectThunks()39073471bf0Spatrick   ARMIndirectThunks() : MachineFunctionPass(ID) {}
39173471bf0Spatrick 
getPassName() const39273471bf0Spatrick   StringRef getPassName() const override { return "ARM Indirect Thunks"; }
39373471bf0Spatrick 
39473471bf0Spatrick   bool doInitialization(Module &M) override;
39573471bf0Spatrick   bool runOnMachineFunction(MachineFunction &MF) override;
39673471bf0Spatrick 
getAnalysisUsage(AnalysisUsage & AU) const39773471bf0Spatrick   void getAnalysisUsage(AnalysisUsage &AU) const override {
39873471bf0Spatrick     MachineFunctionPass::getAnalysisUsage(AU);
39973471bf0Spatrick     AU.addRequired<MachineModuleInfoWrapperPass>();
40073471bf0Spatrick     AU.addPreserved<MachineModuleInfoWrapperPass>();
40173471bf0Spatrick   }
40273471bf0Spatrick 
40373471bf0Spatrick private:
40473471bf0Spatrick   std::tuple<SLSBLRThunkInserter> TIs;
40573471bf0Spatrick 
40673471bf0Spatrick   // FIXME: When LLVM moves to C++17, these can become folds
40773471bf0Spatrick   template <typename... ThunkInserterT>
initTIs(Module & M,std::tuple<ThunkInserterT...> & ThunkInserters)40873471bf0Spatrick   static void initTIs(Module &M,
40973471bf0Spatrick                       std::tuple<ThunkInserterT...> &ThunkInserters) {
41073471bf0Spatrick     (void)std::initializer_list<int>{
41173471bf0Spatrick         (std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...};
41273471bf0Spatrick   }
41373471bf0Spatrick   template <typename... ThunkInserterT>
runTIs(MachineModuleInfo & MMI,MachineFunction & MF,std::tuple<ThunkInserterT...> & ThunkInserters)41473471bf0Spatrick   static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF,
41573471bf0Spatrick                      std::tuple<ThunkInserterT...> &ThunkInserters) {
41673471bf0Spatrick     bool Modified = false;
41773471bf0Spatrick     (void)std::initializer_list<int>{
41873471bf0Spatrick         Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...};
41973471bf0Spatrick     return Modified;
42073471bf0Spatrick   }
42173471bf0Spatrick };
42273471bf0Spatrick 
42373471bf0Spatrick } // end anonymous namespace
42473471bf0Spatrick 
42573471bf0Spatrick char ARMIndirectThunks::ID = 0;
42673471bf0Spatrick 
createARMIndirectThunks()42773471bf0Spatrick FunctionPass *llvm::createARMIndirectThunks() {
42873471bf0Spatrick   return new ARMIndirectThunks();
42973471bf0Spatrick }
43073471bf0Spatrick 
doInitialization(Module & M)43173471bf0Spatrick bool ARMIndirectThunks::doInitialization(Module &M) {
43273471bf0Spatrick   initTIs(M, TIs);
43373471bf0Spatrick   return false;
43473471bf0Spatrick }
43573471bf0Spatrick 
runOnMachineFunction(MachineFunction & MF)43673471bf0Spatrick bool ARMIndirectThunks::runOnMachineFunction(MachineFunction &MF) {
43773471bf0Spatrick   LLVM_DEBUG(dbgs() << getPassName() << '\n');
43873471bf0Spatrick   auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
43973471bf0Spatrick   return runTIs(MMI, MF, TIs);
44073471bf0Spatrick }
441