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