1 //===- bolt/Passes/ValidateInternalCalls.cpp ------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the ValidateInternalCalls class.
11 //===----------------------------------------------------------------------===//
13 #include "bolt/Passes/ValidateInternalCalls.h"
14 #include "bolt/Core/BinaryBasicBlock.h"
15 #include "bolt/Passes/DataflowInfoManager.h"
16 #include "bolt/Passes/FrameAnalysis.h"
17 #include "llvm/ADT/SmallVector.h"
18 #include "llvm/MC/MCInstPrinter.h"
22 #define DEBUG_TYPE "bolt-internalcalls"
29 // Helper used to extract the target basic block used in an internal call.
30 // Return nullptr if this is not an internal call target.
31 BinaryBasicBlock
*getInternalCallTarget(BinaryFunction
&Function
,
33 const BinaryContext
&BC
= Function
.getBinaryContext();
34 if (!BC
.MIB
->isCall(Inst
) || MCPlus::getNumPrimeOperands(Inst
) != 1 ||
35 !Inst
.getOperand(0).isExpr())
38 return Function
.getBasicBlockForLabel(BC
.MIB
->getTargetSymbol(Inst
));
41 // A special StackPointerTracking that considers internal calls
42 class StackPointerTrackingForInternalCalls
43 : public StackPointerTrackingBase
<StackPointerTrackingForInternalCalls
> {
44 friend class DataflowAnalysis
<StackPointerTrackingForInternalCalls
,
47 std::optional
<unsigned> AnnotationIndex
;
50 // We change the starting state to only consider the first block as an
51 // entry point, otherwise the analysis won't converge (there will be two valid
52 // stack offsets, one for an external call and another for an internal call).
53 std::pair
<int, int> getStartingStateAtBB(const BinaryBasicBlock
&BB
) {
54 if (&BB
== &*Func
.begin())
55 return std::make_pair(-8, getEmpty());
56 return std::make_pair(getEmpty(), getEmpty());
59 // Here we decrement SP for internal calls too, in addition to the regular
60 // StackPointerTracking processing.
61 std::pair
<int, int> computeNext(const MCInst
&Point
,
62 const std::pair
<int, int> &Cur
) {
63 std::pair
<int, int> Res
= StackPointerTrackingBase
<
64 StackPointerTrackingForInternalCalls
>::computeNext(Point
, Cur
);
65 if (Res
.first
== StackPointerTracking::SUPERPOSITION
||
66 Res
.first
== StackPointerTracking::EMPTY
)
69 if (BC
.MIB
->isReturn(Point
)) {
74 BinaryBasicBlock
*Target
= getInternalCallTarget(Func
, Point
);
82 StringRef
getAnnotationName() const {
83 return StringRef("StackPointerTrackingForInternalCalls");
87 StackPointerTrackingForInternalCalls(BinaryFunction
&BF
)
88 : StackPointerTrackingBase
<StackPointerTrackingForInternalCalls
>(BF
) {}
91 StackPointerTrackingBase
<StackPointerTrackingForInternalCalls
>::run();
95 } // end anonymous namespace
97 void ValidateInternalCalls::fixCFGForPIC(BinaryFunction
&Function
) const {
98 std::queue
<BinaryBasicBlock
*> Work
;
99 for (BinaryBasicBlock
&BB
: Function
)
102 while (!Work
.empty()) {
103 BinaryBasicBlock
&BB
= *Work
.front();
106 // Search for the next internal call.
107 const BinaryBasicBlock::iterator InternalCall
=
108 llvm::find_if(BB
, [&](const MCInst
&Inst
) {
109 return getInternalCallTarget(Function
, Inst
) != nullptr;
112 // No internal call? Done with this block.
113 if (InternalCall
== BB
.end())
116 BinaryBasicBlock
*Target
= getInternalCallTarget(Function
, *InternalCall
);
117 InstructionListType MovedInsts
= BB
.splitInstructions(&*InternalCall
);
118 if (!MovedInsts
.empty()) {
119 // Split this block at the call instruction.
120 std::unique_ptr
<BinaryBasicBlock
> NewBB
= Function
.createBasicBlock();
121 NewBB
->addInstructions(MovedInsts
.begin(), MovedInsts
.end());
122 BB
.moveAllSuccessorsTo(NewBB
.get());
124 Work
.emplace(NewBB
.get());
125 std::vector
<std::unique_ptr
<BinaryBasicBlock
>> NewBBs
;
126 NewBBs
.emplace_back(std::move(NewBB
));
127 Function
.insertBasicBlocks(&BB
, std::move(NewBBs
));
130 BB
.removeAllSuccessors();
131 BB
.addSuccessor(Target
, BB
.getExecutionCount(), 0ULL);
135 bool ValidateInternalCalls::fixCFGForIC(BinaryFunction
&Function
) const {
136 const BinaryContext
&BC
= Function
.getBinaryContext();
138 StackPointerTrackingForInternalCalls
SPTIC(Function
);
141 // Track instructions reaching a given point of the CFG to answer
142 // "There is a path from entry to point A that contains instruction B"
143 ReachingInsns
<false> RI(Function
);
146 // We use the InsnToBB map that DataflowInfoManager provides us
147 DataflowInfoManager
Info(Function
, nullptr, nullptr);
149 bool Updated
= false;
151 auto processReturns
= [&](BinaryBasicBlock
&BB
, MCInst
&Return
) {
152 // Check all reaching internal calls
153 for (auto I
= RI
.expr_begin(Return
), E
= RI
.expr_end(); I
!= E
; ++I
) {
154 MCInst
&ReachingInst
= **I
;
155 if (!getInternalCallTarget(Function
, ReachingInst
) ||
156 BC
.MIB
->hasAnnotation(ReachingInst
, getProcessedICTag()))
159 // Stack pointer matching
160 int SPAtCall
= SPTIC
.getStateAt(ReachingInst
)->first
;
161 int SPAtRet
= SPTIC
.getStateAt(Return
)->first
;
162 if (SPAtCall
!= StackPointerTracking::SUPERPOSITION
&&
163 SPAtRet
!= StackPointerTracking::SUPERPOSITION
&&
164 SPAtCall
!= SPAtRet
- 8)
169 // Mark this call as processed, so we don't try to analyze it as a
170 // PIC-computation internal call.
171 BC
.MIB
->addAnnotation(ReachingInst
, getProcessedICTag(), 0U);
173 // Connect this block with the returning block of the caller
174 BinaryBasicBlock
*CallerBlock
= Info
.getInsnToBBMap()[&ReachingInst
];
175 BinaryBasicBlock
*ReturnDestBlock
=
176 Function
.getLayout().getBasicBlockAfter(CallerBlock
);
177 BB
.addSuccessor(ReturnDestBlock
, BB
.getExecutionCount(), 0);
181 // This will connect blocks terminated with RETs to their respective
182 // internal caller return block. A note here: this is overly conservative
183 // because in nested calls, or unrelated calls, it will create edges
184 // connecting RETs to potentially unrelated internal calls. This is safe
185 // and if this causes a problem to recover the stack offsets properly, we
187 for (BinaryBasicBlock
&BB
: Function
) {
188 for (MCInst
&Inst
: BB
) {
189 if (!BC
.MIB
->isReturn(Inst
))
192 processReturns(BB
, Inst
);
198 bool ValidateInternalCalls::hasTailCallsInRange(
199 BinaryFunction
&Function
) const {
200 const BinaryContext
&BC
= Function
.getBinaryContext();
201 for (BinaryBasicBlock
&BB
: Function
)
202 for (MCInst
&Inst
: BB
)
203 if (BC
.MIB
->isTailCall(Inst
))
208 bool ValidateInternalCalls::analyzeFunction(BinaryFunction
&Function
) const {
209 fixCFGForPIC(Function
);
210 while (fixCFGForIC(Function
)) {
213 BinaryContext
&BC
= Function
.getBinaryContext();
214 RegAnalysis RA
= RegAnalysis(BC
, nullptr, nullptr);
215 RA
.setConservativeStrategy(RegAnalysis::ConservativeStrategy::CLOBBERS_NONE
);
216 bool HasTailCalls
= hasTailCallsInRange(Function
);
218 for (BinaryBasicBlock
&BB
: Function
) {
219 for (MCInst
&Inst
: BB
) {
220 BinaryBasicBlock
*Target
= getInternalCallTarget(Function
, Inst
);
221 if (!Target
|| BC
.MIB
->hasAnnotation(Inst
, getProcessedICTag()))
225 LLVM_DEBUG(dbgs() << Function
226 << " has tail calls and internal calls.\n");
233 int64_t StackOffset
= 0;
234 bool IsIndexed
= false;
235 MCInst
*TargetInst
= ProgramPoint::getFirstPointAt(*Target
).getInst();
236 if (!BC
.MIB
->isStackAccess(*TargetInst
, FIE
.IsLoad
, FIE
.IsStore
,
237 FIE
.IsStoreFromReg
, Reg
, SrcImm
,
238 FIE
.StackPtrReg
, StackOffset
, FIE
.Size
,
239 FIE
.IsSimple
, IsIndexed
)) {
241 dbgs() << "Frame analysis failed - not simple: " << Function
<< "\n";
246 if (!FIE
.IsLoad
|| FIE
.StackPtrReg
!= BC
.MIB
->getStackPointer() ||
249 dbgs() << "Target instruction does not fetch return address - not "
256 // Now track how the return address is used by tracking uses of Reg
257 ReachingDefOrUse
</*Def=*/false> RU
=
258 ReachingDefOrUse
<false>(RA
, Function
, Reg
);
261 int64_t Offset
= static_cast<int64_t>(Target
->getInputOffset());
262 bool UseDetected
= false;
263 for (auto I
= RU
.expr_begin(*RU
.getStateBefore(*TargetInst
)),
267 BitVector UsedRegs
= BitVector(BC
.MRI
->getNumRegs(), false);
268 BC
.MIB
->getTouchedRegs(Use
, UsedRegs
);
273 std::pair
<MCPhysReg
, int64_t> Input1
= std::make_pair(Reg
, 0);
274 std::pair
<MCPhysReg
, int64_t> Input2
= std::make_pair(0, 0);
275 if (!BC
.MIB
->evaluateStackOffsetExpr(Use
, Output
, Input1
, Input2
)) {
276 LLVM_DEBUG(dbgs() << "Evaluate stack offset expr failed.\n");
279 if (Offset
+ Output
< 0 ||
280 Offset
+ Output
> static_cast<int64_t>(Function
.getSize())) {
282 dbgs() << "Detected out-of-range PIC reference in " << Function
283 << "\nReturn address load: ";
284 BC
.InstPrinter
->printInst(TargetInst
, 0, "", *BC
.STI
, dbgs());
286 BC
.InstPrinter
->printInst(&Use
, 0, "", *BC
.STI
, dbgs());
293 dbgs() << "Validated access: ";
294 BC
.InstPrinter
->printInst(&Use
, 0, "", *BC
.STI
, dbgs());
299 LLVM_DEBUG(dbgs() << "No use detected.\n");
307 void ValidateInternalCalls::runOnFunctions(BinaryContext
&BC
) {
311 // Look for functions that need validation. This should be pretty rare.
312 std::set
<BinaryFunction
*> NeedsValidation
;
313 for (auto &BFI
: BC
.getBinaryFunctions()) {
314 BinaryFunction
&Function
= BFI
.second
;
315 for (BinaryBasicBlock
&BB
: Function
) {
316 for (MCInst
&Inst
: BB
) {
317 if (getInternalCallTarget(Function
, Inst
)) {
318 NeedsValidation
.insert(&Function
);
319 Function
.setSimple(false);
326 // Skip validation for non-relocation mode
327 if (!BC
.HasRelocations
)
330 // Since few functions need validation, we can work with our most expensive
331 // algorithms here. Fix the CFG treating internal calls as unconditional
332 // jumps. This optimistically assumes this call is a PIC trick to get the PC
333 // value, so it is not really a call, but a jump. If we find that it's not the
334 // case, we mark this function as non-simple and stop processing it.
335 std::set
<BinaryFunction
*> Invalid
;
336 for (BinaryFunction
*Function
: NeedsValidation
) {
337 LLVM_DEBUG(dbgs() << "Validating " << *Function
<< "\n");
338 if (!analyzeFunction(*Function
))
339 Invalid
.insert(Function
);
340 clearAnnotations(*Function
);
343 if (!Invalid
.empty()) {
344 errs() << "BOLT-WARNING: will skip the following function(s) as unsupported"
345 " internal calls were detected:\n";
346 for (BinaryFunction
*Function
: Invalid
) {
347 errs() << " " << *Function
<< "\n";
348 Function
->setIgnored();