Skip to content

Commit b1d5810

Browse files
scottconstabletopperc
authored andcommitted
[X86] Refactor X86IndirectThunks.cpp to Accommodate Mitigations other than Retpoline
Introduce a ThunkInserter CRTP base class from which new thunk types can inherit, e.g., thunks to mitigate https://software.intel.com/security-software-guidance/software-guidance/load-value-injection. Differential Revision: https://reviews.llvm.org/D76811
1 parent 0cfdce2 commit b1d5810

File tree

1 file changed

+157
-125
lines changed

1 file changed

+157
-125
lines changed

llvm/lib/Target/X86/X86IndirectThunks.cpp

+157-125
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,35 @@ static const char EDXRetpolineName[] = "__llvm_retpoline_edx";
5151
static const char EDIRetpolineName[] = "__llvm_retpoline_edi";
5252

5353
namespace {
54+
template <typename Derived> class ThunkInserter {
55+
Derived &getDerived() { return *static_cast<Derived *>(this); }
56+
57+
protected:
58+
bool InsertedThunks;
59+
void doInitialization(Module &M) {}
60+
void createThunkFunction(MachineModuleInfo &MMI, StringRef Name);
61+
62+
public:
63+
void init(Module &M) {
64+
InsertedThunks = false;
65+
getDerived().doInitialization(M);
66+
}
67+
// return `true` if `MMI` or `MF` was modified
68+
bool run(MachineModuleInfo &MMI, MachineFunction &MF);
69+
};
70+
71+
struct RetpolineThunkInserter : ThunkInserter<RetpolineThunkInserter> {
72+
const char *getThunkPrefix() { return RetpolineNamePrefix; }
73+
bool mayUseThunk(const MachineFunction &MF) {
74+
const auto &STI = MF.getSubtarget<X86Subtarget>();
75+
return (STI.useRetpolineIndirectCalls() ||
76+
STI.useRetpolineIndirectBranches()) &&
77+
!STI.useRetpolineExternalThunk();
78+
}
79+
void insertThunks(MachineModuleInfo &MMI);
80+
void populateThunk(MachineFunction &MF);
81+
};
82+
5483
class X86IndirectThunks : public MachineFunctionPass {
5584
public:
5685
static char ID;
@@ -60,7 +89,7 @@ class X86IndirectThunks : public MachineFunctionPass {
6089
StringRef getPassName() const override { return "X86 Indirect Thunks"; }
6190

6291
bool doInitialization(Module &M) override;
63-
bool runOnMachineFunction(MachineFunction &F) override;
92+
bool runOnMachineFunction(MachineFunction &MF) override;
6493

6594
void getAnalysisUsage(AnalysisUsage &AU) const override {
6695
MachineFunctionPass::getAnalysisUsage(AU);
@@ -69,78 +98,39 @@ class X86IndirectThunks : public MachineFunctionPass {
6998
}
7099

71100
private:
72-
MachineModuleInfo *MMI = nullptr;
73-
const TargetMachine *TM = nullptr;
74-
bool Is64Bit = false;
75-
const X86Subtarget *STI = nullptr;
76-
const X86InstrInfo *TII = nullptr;
77-
78-
bool InsertedThunks = false;
79-
80-
void createThunkFunction(Module &M, StringRef Name);
81-
void insertRegReturnAddrClobber(MachineBasicBlock &MBB, Register Reg);
82-
void populateThunk(MachineFunction &MF, Register Reg);
101+
std::tuple<RetpolineThunkInserter> TIs;
102+
103+
// FIXME: When LLVM moves to C++17, these can become folds
104+
template <typename... ThunkInserterT>
105+
static void initTIs(Module &M,
106+
std::tuple<ThunkInserterT...> &ThunkInserters) {
107+
(void)std::initializer_list<int>{
108+
(std::get<ThunkInserterT>(ThunkInserters).init(M), 0)...};
109+
}
110+
template <typename... ThunkInserterT>
111+
static bool runTIs(MachineModuleInfo &MMI, MachineFunction &MF,
112+
std::tuple<ThunkInserterT...> &ThunkInserters) {
113+
bool Modified = false;
114+
(void)std::initializer_list<int>{
115+
Modified |= std::get<ThunkInserterT>(ThunkInserters).run(MMI, MF)...};
116+
return Modified;
117+
}
83118
};
84119

85120
} // end anonymous namespace
86121

87-
FunctionPass *llvm::createX86IndirectThunksPass() {
88-
return new X86IndirectThunks();
122+
void RetpolineThunkInserter::insertThunks(MachineModuleInfo &MMI) {
123+
if (MMI.getTarget().getTargetTriple().getArch() == Triple::x86_64)
124+
createThunkFunction(MMI, R11RetpolineName);
125+
else
126+
for (StringRef Name : {EAXRetpolineName, ECXRetpolineName, EDXRetpolineName,
127+
EDIRetpolineName})
128+
createThunkFunction(MMI, Name);
89129
}
90130

91-
char X86IndirectThunks::ID = 0;
92-
93-
bool X86IndirectThunks::doInitialization(Module &M) {
94-
InsertedThunks = false;
95-
return false;
96-
}
97-
98-
bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
99-
LLVM_DEBUG(dbgs() << getPassName() << '\n');
100-
101-
TM = &MF.getTarget();;
102-
STI = &MF.getSubtarget<X86Subtarget>();
103-
TII = STI->getInstrInfo();
104-
Is64Bit = TM->getTargetTriple().getArch() == Triple::x86_64;
105-
106-
MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
107-
Module &M = const_cast<Module &>(*MMI->getModule());
108-
109-
// If this function is not a thunk, check to see if we need to insert
110-
// a thunk.
111-
if (!MF.getName().startswith(RetpolineNamePrefix)) {
112-
// If we've already inserted a thunk, nothing else to do.
113-
if (InsertedThunks)
114-
return false;
115-
116-
// Only add a thunk if one of the functions has the retpoline feature
117-
// enabled in its subtarget, and doesn't enable external thunks.
118-
// FIXME: Conditionalize on indirect calls so we don't emit a thunk when
119-
// nothing will end up calling it.
120-
// FIXME: It's a little silly to look at every function just to enumerate
121-
// the subtargets, but eventually we'll want to look at them for indirect
122-
// calls, so maybe this is OK.
123-
if ((!STI->useRetpolineIndirectCalls() &&
124-
!STI->useRetpolineIndirectBranches()) ||
125-
STI->useRetpolineExternalThunk())
126-
return false;
127-
128-
// Otherwise, we need to insert the thunk.
129-
// WARNING: This is not really a well behaving thing to do in a function
130-
// pass. We extract the module and insert a new function (and machine
131-
// function) directly into the module.
132-
if (Is64Bit)
133-
createThunkFunction(M, R11RetpolineName);
134-
else
135-
for (StringRef Name :
136-
{EAXRetpolineName, ECXRetpolineName, EDXRetpolineName,
137-
EDIRetpolineName})
138-
createThunkFunction(M, Name);
139-
InsertedThunks = true;
140-
return true;
141-
}
142-
143-
// If this *is* a thunk function, we need to populate it with the correct MI.
131+
void RetpolineThunkInserter::populateThunk(MachineFunction &MF) {
132+
bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64;
133+
Register ThunkReg;
144134
if (Is64Bit) {
145135
assert(MF.getName() == "__llvm_retpoline_r11" &&
146136
"Should only have an r11 thunk on 64-bit targets");
@@ -155,7 +145,7 @@ bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
155145
// .Lr11_call_target:
156146
// movq %r11, (%rsp)
157147
// retq
158-
populateThunk(MF, X86::R11);
148+
ThunkReg = X86::R11;
159149
} else {
160150
// For 32-bit targets we need to emit a collection of thunks for various
161151
// possible scratch registers as well as a fallback that uses EDI, which is
@@ -185,67 +175,18 @@ bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
185175
// movl %edi, (%esp)
186176
// retl
187177
if (MF.getName() == EAXRetpolineName)
188-
populateThunk(MF, X86::EAX);
178+
ThunkReg = X86::EAX;
189179
else if (MF.getName() == ECXRetpolineName)
190-
populateThunk(MF, X86::ECX);
180+
ThunkReg = X86::ECX;
191181
else if (MF.getName() == EDXRetpolineName)
192-
populateThunk(MF, X86::EDX);
182+
ThunkReg = X86::EDX;
193183
else if (MF.getName() == EDIRetpolineName)
194-
populateThunk(MF, X86::EDI);
184+
ThunkReg = X86::EDI;
195185
else
196186
llvm_unreachable("Invalid thunk name on x86-32!");
197187
}
198188

199-
return true;
200-
}
201-
202-
void X86IndirectThunks::createThunkFunction(Module &M, StringRef Name) {
203-
assert(Name.startswith(RetpolineNamePrefix) &&
204-
"Created a thunk with an unexpected prefix!");
205-
206-
LLVMContext &Ctx = M.getContext();
207-
auto Type = FunctionType::get(Type::getVoidTy(Ctx), false);
208-
Function *F =
209-
Function::Create(Type, GlobalValue::LinkOnceODRLinkage, Name, &M);
210-
F->setVisibility(GlobalValue::HiddenVisibility);
211-
F->setComdat(M.getOrInsertComdat(Name));
212-
213-
// Add Attributes so that we don't create a frame, unwind information, or
214-
// inline.
215-
AttrBuilder B;
216-
B.addAttribute(llvm::Attribute::NoUnwind);
217-
B.addAttribute(llvm::Attribute::Naked);
218-
F->addAttributes(llvm::AttributeList::FunctionIndex, B);
219-
220-
// Populate our function a bit so that we can verify.
221-
BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F);
222-
IRBuilder<> Builder(Entry);
223-
224-
Builder.CreateRetVoid();
225-
226-
// MachineFunctions/MachineBasicBlocks aren't created automatically for the
227-
// IR-level constructs we already made. Create them and insert them into the
228-
// module.
229-
MachineFunction &MF = MMI->getOrCreateMachineFunction(*F);
230-
MachineBasicBlock *EntryMBB = MF.CreateMachineBasicBlock(Entry);
231-
232-
// Insert EntryMBB into MF. It's not in the module until we do this.
233-
MF.insert(MF.end(), EntryMBB);
234-
}
235-
236-
void X86IndirectThunks::insertRegReturnAddrClobber(MachineBasicBlock &MBB,
237-
Register Reg) {
238-
const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr;
239-
const Register SPReg = Is64Bit ? X86::RSP : X86::ESP;
240-
addRegOffset(BuildMI(&MBB, DebugLoc(), TII->get(MovOpc)), SPReg, false, 0)
241-
.addReg(Reg);
242-
}
243-
244-
void X86IndirectThunks::populateThunk(MachineFunction &MF,
245-
Register Reg) {
246-
// Set MF properties. We never use vregs...
247-
MF.getProperties().set(MachineFunctionProperties::Property::NoVRegs);
248-
189+
const TargetInstrInfo *TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
249190
// Grab the entry MBB and erase any other blocks. O0 codegen appears to
250191
// generate two bbs for the entry block.
251192
MachineBasicBlock *Entry = &MF.front();
@@ -264,7 +205,7 @@ void X86IndirectThunks::populateThunk(MachineFunction &MF,
264205
const unsigned CallOpc = Is64Bit ? X86::CALL64pcrel32 : X86::CALLpcrel32;
265206
const unsigned RetOpc = Is64Bit ? X86::RETQ : X86::RETL;
266207

267-
Entry->addLiveIn(Reg);
208+
Entry->addLiveIn(ThunkReg);
268209
BuildMI(Entry, DebugLoc(), TII->get(CallOpc)).addSym(TargetSym);
269210

270211
// The MIR verifier thinks that the CALL in the entry block will fall through
@@ -286,10 +227,101 @@ void X86IndirectThunks::populateThunk(MachineFunction &MF,
286227
CaptureSpec->setHasAddressTaken();
287228
CaptureSpec->addSuccessor(CaptureSpec);
288229

289-
CallTarget->addLiveIn(Reg);
230+
CallTarget->addLiveIn(ThunkReg);
290231
CallTarget->setHasAddressTaken();
291232
CallTarget->setAlignment(Align(16));
292-
insertRegReturnAddrClobber(*CallTarget, Reg);
233+
234+
// Insert return address clobber
235+
const unsigned MovOpc = Is64Bit ? X86::MOV64mr : X86::MOV32mr;
236+
const Register SPReg = Is64Bit ? X86::RSP : X86::ESP;
237+
addRegOffset(BuildMI(CallTarget, DebugLoc(), TII->get(MovOpc)), SPReg, false,
238+
0)
239+
.addReg(ThunkReg);
240+
293241
CallTarget->back().setPreInstrSymbol(MF, TargetSym);
294242
BuildMI(CallTarget, DebugLoc(), TII->get(RetOpc));
295243
}
244+
245+
template <typename Derived>
246+
void ThunkInserter<Derived>::createThunkFunction(MachineModuleInfo &MMI,
247+
StringRef Name) {
248+
assert(Name.startswith(getDerived().getThunkPrefix()) &&
249+
"Created a thunk with an unexpected prefix!");
250+
251+
Module &M = const_cast<Module &>(*MMI.getModule());
252+
LLVMContext &Ctx = M.getContext();
253+
auto Type = FunctionType::get(Type::getVoidTy(Ctx), false);
254+
Function *F =
255+
Function::Create(Type, GlobalValue::LinkOnceODRLinkage, Name, &M);
256+
F->setVisibility(GlobalValue::HiddenVisibility);
257+
F->setComdat(M.getOrInsertComdat(Name));
258+
259+
// Add Attributes so that we don't create a frame, unwind information, or
260+
// inline.
261+
AttrBuilder B;
262+
B.addAttribute(llvm::Attribute::NoUnwind);
263+
B.addAttribute(llvm::Attribute::Naked);
264+
F->addAttributes(llvm::AttributeList::FunctionIndex, B);
265+
266+
// Populate our function a bit so that we can verify.
267+
BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F);
268+
IRBuilder<> Builder(Entry);
269+
270+
Builder.CreateRetVoid();
271+
272+
// MachineFunctions/MachineBasicBlocks aren't created automatically for the
273+
// IR-level constructs we already made. Create them and insert them into the
274+
// module.
275+
MachineFunction &MF = MMI.getOrCreateMachineFunction(*F);
276+
MachineBasicBlock *EntryMBB = MF.CreateMachineBasicBlock(Entry);
277+
278+
// Insert EntryMBB into MF. It's not in the module until we do this.
279+
MF.insert(MF.end(), EntryMBB);
280+
// Set MF properties. We never use vregs...
281+
MF.getProperties().set(MachineFunctionProperties::Property::NoVRegs);
282+
}
283+
284+
template <typename Derived>
285+
bool ThunkInserter<Derived>::run(MachineModuleInfo &MMI, MachineFunction &MF) {
286+
// If MF is not a thunk, check to see if we need to insert a thunk.
287+
if (!MF.getName().startswith(getDerived().getThunkPrefix())) {
288+
// If we've already inserted a thunk, nothing else to do.
289+
if (InsertedThunks)
290+
return false;
291+
292+
// Only add a thunk if one of the functions has the corresponding feature
293+
// enabled in its subtarget, and doesn't enable external thunks.
294+
// FIXME: Conditionalize on indirect calls so we don't emit a thunk when
295+
// nothing will end up calling it.
296+
// FIXME: It's a little silly to look at every function just to enumerate
297+
// the subtargets, but eventually we'll want to look at them for indirect
298+
// calls, so maybe this is OK.
299+
if (!getDerived().mayUseThunk(MF))
300+
return false;
301+
302+
getDerived().insertThunks(MMI);
303+
InsertedThunks = true;
304+
return true;
305+
}
306+
307+
// If this *is* a thunk function, we need to populate it with the correct MI.
308+
getDerived().populateThunk(MF);
309+
return true;
310+
}
311+
312+
FunctionPass *llvm::createX86IndirectThunksPass() {
313+
return new X86IndirectThunks();
314+
}
315+
316+
char X86IndirectThunks::ID = 0;
317+
318+
bool X86IndirectThunks::doInitialization(Module &M) {
319+
initTIs(M, TIs);
320+
return false;
321+
}
322+
323+
bool X86IndirectThunks::runOnMachineFunction(MachineFunction &MF) {
324+
LLVM_DEBUG(dbgs() << getPassName() << '\n');
325+
auto &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
326+
return runTIs(MMI, MF, TIs);
327+
}

0 commit comments

Comments
 (0)