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/MC/MCInstPrinter.h"
21 #define DEBUG_TYPE "bolt-internalcalls"
28 // Helper used to extract the target basic block used in an internal call.
29 // Return nullptr if this is not an internal call target.
30 BinaryBasicBlock
*getInternalCallTarget(BinaryFunction
&Function
,
32 const BinaryContext
&BC
= Function
.getBinaryContext();
33 if (!BC
.MIB
->isCall(Inst
) || MCPlus::getNumPrimeOperands(Inst
) != 1 ||
34 !Inst
.getOperand(0).isExpr())
37 return Function
.getBasicBlockForLabel(BC
.MIB
->getTargetSymbol(Inst
));
40 // A special StackPointerTracking that considers internal calls
41 class StackPointerTrackingForInternalCalls
42 : public StackPointerTrackingBase
<StackPointerTrackingForInternalCalls
> {
43 friend class DataflowAnalysis
<StackPointerTrackingForInternalCalls
,
46 std::optional
<unsigned> AnnotationIndex
;
49 // We change the starting state to only consider the first block as an
50 // entry point, otherwise the analysis won't converge (there will be two valid
51 // stack offsets, one for an external call and another for an internal call).
52 std::pair
<int, int> getStartingStateAtBB(const BinaryBasicBlock
&BB
) {
53 if (&BB
== &*Func
.begin())
54 return std::make_pair(-8, getEmpty());
55 return std::make_pair(getEmpty(), getEmpty());
58 // Here we decrement SP for internal calls too, in addition to the regular
59 // StackPointerTracking processing.
60 std::pair
<int, int> computeNext(const MCInst
&Point
,
61 const std::pair
<int, int> &Cur
) {
62 std::pair
<int, int> Res
= StackPointerTrackingBase
<
63 StackPointerTrackingForInternalCalls
>::computeNext(Point
, Cur
);
64 if (Res
.first
== StackPointerTracking::SUPERPOSITION
||
65 Res
.first
== StackPointerTracking::EMPTY
)
68 if (BC
.MIB
->isReturn(Point
)) {
73 BinaryBasicBlock
*Target
= getInternalCallTarget(Func
, Point
);
81 StringRef
getAnnotationName() const {
82 return StringRef("StackPointerTrackingForInternalCalls");
86 StackPointerTrackingForInternalCalls(BinaryFunction
&BF
)
87 : StackPointerTrackingBase
<StackPointerTrackingForInternalCalls
>(BF
) {}
90 StackPointerTrackingBase
<StackPointerTrackingForInternalCalls
>::run();
94 } // end anonymous namespace
96 void ValidateInternalCalls::fixCFGForPIC(BinaryFunction
&Function
) const {
97 std::queue
<BinaryBasicBlock
*> Work
;
98 for (BinaryBasicBlock
&BB
: Function
)
101 while (!Work
.empty()) {
102 BinaryBasicBlock
&BB
= *Work
.front();
105 // Search for the next internal call.
106 const BinaryBasicBlock::iterator InternalCall
=
107 llvm::find_if(BB
, [&](const MCInst
&Inst
) {
108 return getInternalCallTarget(Function
, Inst
) != nullptr;
111 // No internal call? Done with this block.
112 if (InternalCall
== BB
.end())
115 BinaryBasicBlock
*Target
= getInternalCallTarget(Function
, *InternalCall
);
116 InstructionListType MovedInsts
= BB
.splitInstructions(&*InternalCall
);
117 if (!MovedInsts
.empty()) {
118 // Split this block at the call instruction.
119 std::unique_ptr
<BinaryBasicBlock
> NewBB
= Function
.createBasicBlock();
120 NewBB
->addInstructions(MovedInsts
.begin(), MovedInsts
.end());
121 BB
.moveAllSuccessorsTo(NewBB
.get());
123 Work
.emplace(NewBB
.get());
124 std::vector
<std::unique_ptr
<BinaryBasicBlock
>> NewBBs
;
125 NewBBs
.emplace_back(std::move(NewBB
));
126 Function
.insertBasicBlocks(&BB
, std::move(NewBBs
));
129 BB
.removeAllSuccessors();
130 BB
.addSuccessor(Target
, BB
.getExecutionCount(), 0ULL);
134 bool ValidateInternalCalls::fixCFGForIC(BinaryFunction
&Function
) const {
135 const BinaryContext
&BC
= Function
.getBinaryContext();
137 StackPointerTrackingForInternalCalls
SPTIC(Function
);
140 // Track instructions reaching a given point of the CFG to answer
141 // "There is a path from entry to point A that contains instruction B"
142 ReachingInsns
<false> RI(Function
);
145 // We use the InsnToBB map that DataflowInfoManager provides us
146 DataflowInfoManager
Info(Function
, nullptr, nullptr);
148 bool Updated
= false;
150 auto processReturns
= [&](BinaryBasicBlock
&BB
, MCInst
&Return
) {
151 // Check all reaching internal calls
152 for (auto I
= RI
.expr_begin(Return
), E
= RI
.expr_end(); I
!= E
; ++I
) {
153 MCInst
&ReachingInst
= **I
;
154 if (!getInternalCallTarget(Function
, ReachingInst
) ||
155 BC
.MIB
->hasAnnotation(ReachingInst
, getProcessedICTag()))
158 // Stack pointer matching
159 int SPAtCall
= SPTIC
.getStateAt(ReachingInst
)->first
;
160 int SPAtRet
= SPTIC
.getStateAt(Return
)->first
;
161 if (SPAtCall
!= StackPointerTracking::SUPERPOSITION
&&
162 SPAtRet
!= StackPointerTracking::SUPERPOSITION
&&
163 SPAtCall
!= SPAtRet
- 8)
168 // Mark this call as processed, so we don't try to analyze it as a
169 // PIC-computation internal call.
170 BC
.MIB
->addAnnotation(ReachingInst
, getProcessedICTag(), 0U);
172 // Connect this block with the returning block of the caller
173 BinaryBasicBlock
*CallerBlock
= Info
.getInsnToBBMap()[&ReachingInst
];
174 BinaryBasicBlock
*ReturnDestBlock
=
175 Function
.getLayout().getBasicBlockAfter(CallerBlock
);
176 BB
.addSuccessor(ReturnDestBlock
, BB
.getExecutionCount(), 0);
180 // This will connect blocks terminated with RETs to their respective
181 // internal caller return block. A note here: this is overly conservative
182 // because in nested calls, or unrelated calls, it will create edges
183 // connecting RETs to potentially unrelated internal calls. This is safe
184 // and if this causes a problem to recover the stack offsets properly, we
186 for (BinaryBasicBlock
&BB
: Function
) {
187 for (MCInst
&Inst
: BB
) {
188 if (!BC
.MIB
->isReturn(Inst
))
191 processReturns(BB
, Inst
);
197 bool ValidateInternalCalls::hasTailCallsInRange(
198 BinaryFunction
&Function
) const {
199 const BinaryContext
&BC
= Function
.getBinaryContext();
200 for (BinaryBasicBlock
&BB
: Function
)
201 for (MCInst
&Inst
: BB
)
202 if (BC
.MIB
->isTailCall(Inst
))
207 bool ValidateInternalCalls::analyzeFunction(BinaryFunction
&Function
) const {
208 fixCFGForPIC(Function
);
209 while (fixCFGForIC(Function
)) {
212 BinaryContext
&BC
= Function
.getBinaryContext();
213 RegAnalysis RA
= RegAnalysis(BC
, nullptr, nullptr);
214 RA
.setConservativeStrategy(RegAnalysis::ConservativeStrategy::CLOBBERS_NONE
);
215 bool HasTailCalls
= hasTailCallsInRange(Function
);
217 for (BinaryBasicBlock
&BB
: Function
) {
218 for (MCInst
&Inst
: BB
) {
219 BinaryBasicBlock
*Target
= getInternalCallTarget(Function
, Inst
);
220 if (!Target
|| BC
.MIB
->hasAnnotation(Inst
, getProcessedICTag()))
224 LLVM_DEBUG(dbgs() << Function
225 << " has tail calls and internal calls.\n");
232 int64_t StackOffset
= 0;
233 bool IsIndexed
= false;
234 MCInst
*TargetInst
= ProgramPoint::getFirstPointAt(*Target
).getInst();
235 if (!BC
.MIB
->isStackAccess(*TargetInst
, FIE
.IsLoad
, FIE
.IsStore
,
236 FIE
.IsStoreFromReg
, Reg
, SrcImm
,
237 FIE
.StackPtrReg
, StackOffset
, FIE
.Size
,
238 FIE
.IsSimple
, IsIndexed
)) {
240 dbgs() << "Frame analysis failed - not simple: " << Function
<< "\n";
245 if (!FIE
.IsLoad
|| FIE
.StackPtrReg
!= BC
.MIB
->getStackPointer() ||
248 dbgs() << "Target instruction does not fetch return address - not "
255 // Now track how the return address is used by tracking uses of Reg
256 ReachingDefOrUse
</*Def=*/false> RU
=
257 ReachingDefOrUse
<false>(RA
, Function
, Reg
);
260 int64_t Offset
= static_cast<int64_t>(Target
->getInputOffset());
261 bool UseDetected
= false;
262 for (auto I
= RU
.expr_begin(*RU
.getStateBefore(*TargetInst
)),
266 BitVector UsedRegs
= BitVector(BC
.MRI
->getNumRegs(), false);
267 BC
.MIB
->getTouchedRegs(Use
, UsedRegs
);
272 std::pair
<MCPhysReg
, int64_t> Input1
= std::make_pair(Reg
, 0);
273 std::pair
<MCPhysReg
, int64_t> Input2
= std::make_pair(0, 0);
274 if (!BC
.MIB
->evaluateStackOffsetExpr(Use
, Output
, Input1
, Input2
)) {
275 LLVM_DEBUG(dbgs() << "Evaluate stack offset expr failed.\n");
278 if (Offset
+ Output
< 0 ||
279 Offset
+ Output
> static_cast<int64_t>(Function
.getSize())) {
281 dbgs() << "Detected out-of-range PIC reference in " << Function
282 << "\nReturn address load: ";
283 BC
.dump(*TargetInst
);
291 dbgs() << "Validated access: ";
296 LLVM_DEBUG(dbgs() << "No use detected.\n");
304 Error
ValidateInternalCalls::runOnFunctions(BinaryContext
&BC
) {
306 return Error::success();
308 // Look for functions that need validation. This should be pretty rare.
309 std::set
<BinaryFunction
*> NeedsValidation
;
310 for (auto &BFI
: BC
.getBinaryFunctions()) {
311 BinaryFunction
&Function
= BFI
.second
;
312 for (BinaryBasicBlock
&BB
: Function
) {
313 for (MCInst
&Inst
: BB
) {
314 if (getInternalCallTarget(Function
, Inst
)) {
315 NeedsValidation
.insert(&Function
);
316 Function
.setSimple(false);
323 // Skip validation for non-relocation mode
324 if (!BC
.HasRelocations
)
325 return Error::success();
327 // Since few functions need validation, we can work with our most expensive
328 // algorithms here. Fix the CFG treating internal calls as unconditional
329 // jumps. This optimistically assumes this call is a PIC trick to get the PC
330 // value, so it is not really a call, but a jump. If we find that it's not the
331 // case, we mark this function as non-simple and stop processing it.
332 std::set
<BinaryFunction
*> Invalid
;
333 for (BinaryFunction
*Function
: NeedsValidation
) {
334 LLVM_DEBUG(dbgs() << "Validating " << *Function
<< "\n");
335 if (!analyzeFunction(*Function
))
336 Invalid
.insert(Function
);
337 clearAnnotations(*Function
);
340 if (!Invalid
.empty()) {
342 << "BOLT-WARNING: will skip the following function(s) as unsupported"
343 " internal calls were detected:\n";
344 for (BinaryFunction
*Function
: Invalid
) {
345 BC
.errs() << " " << *Function
<< "\n";
346 Function
->setIgnored();
349 return Error::success();