Handle more CFI cases and some.

Summary:
  * Update CFI state for larger range of functions to increase coverage.
  * Issue more warnings indicating reasons for skipping functions.
  * Print top called functions in the binary.

(cherry picked from FBD2839734)
This commit is contained in:
Maksim Panchenko
2016-01-16 14:58:22 -08:00
parent d9536e6092
commit 4a44d187c6
2 changed files with 141 additions and 80 deletions

View File

@@ -197,6 +197,10 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation,
if (BBExecCount != BinaryBasicBlock::COUNT_NO_PROFILE) {
OS << " Exec Count : " << BBExecCount << "\n";
}
if (!BBCFIState.empty()) {
unsigned BBIndex = BB - &*BasicBlocks.begin();
OS << " CFI State : " << BBCFIState[BBIndex] << '\n';
}
if (!BB->Predecessors.empty()) {
OS << " Predecessors: ";
auto Sep = "";
@@ -331,13 +335,19 @@ bool BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {
nulls(),
nulls())) {
// Ignore this function. Skip to the next one.
errs() << "FLO-WARNING: unable to disassemble instruction at offset 0x"
<< Twine::utohexstr(Offset) << " (address 0x"
<< Twine::utohexstr(getAddress() + Offset) << ") in function "
<< getName() << '\n';
IsSimple = false;
break;
}
if (MIA->isUnsupported(Instruction)) {
DEBUG(dbgs() << "FLO: unsupported instruction seen. Skipping function "
<< getName() << ".\n");
errs() << "FLO-WARNING: unsupported instruction seen at offset 0x"
<< Twine::utohexstr(Offset) << " (address 0x"
<< Twine::utohexstr(getAddress() + Offset) << ") in function "
<< getName() << '\n';
IsSimple = false;
break;
}
@@ -365,9 +375,9 @@ bool BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {
TargetSymbol = Ctx->getOrCreateSymbol(getName());
} else {
// Possibly an old-style PIC code
DEBUG(dbgs() << "FLO: internal call detected at 0x"
<< Twine::utohexstr(AbsoluteInstrAddr)
<< " in function " << getName() << "\n");
errs() << "FLO: internal call detected at 0x"
<< Twine::utohexstr(AbsoluteInstrAddr)
<< " in function " << getName() << ". Skipping.\n";
IsSimple = false;
}
}
@@ -433,11 +443,17 @@ bool BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {
// Should be an indirect call or an indirect branch. Bail out on the
// latter case.
if (MIA->isIndirectBranch(Instruction)) {
DEBUG(dbgs() << "FLO-WARNING: indirect branch detected at 0x"
<< Twine::utohexstr(AbsoluteInstrAddr)
<< ". Skipping function " << getName() << ".\n");
IsSimple = false;
}
// Indirect call. We only need to fix it if the operand is RIP-relative
if (MIA->hasRIPOperand(Instruction)) {
if (!handleRIPOperand(Instruction, AbsoluteInstrAddr, Size)) {
errs() << "FLO-WARNING: cannot handle RIP operand at 0x"
<< Twine::utohexstr(AbsoluteInstrAddr)
<< ". Skipping function " << getName() << ".\n";
IsSimple = false;
}
}
@@ -445,6 +461,9 @@ bool BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {
} else {
if (MIA->hasRIPOperand(Instruction)) {
if (!handleRIPOperand(Instruction, AbsoluteInstrAddr, Size)) {
errs() << "FLO-WARNING: cannot handle RIP operand at 0x"
<< Twine::utohexstr(AbsoluteInstrAddr)
<< ". Skipping function " << getName() << ".\n";
IsSimple = false;
}
}
@@ -829,22 +848,32 @@ bool BinaryFunction::fixCFIState() {
return true;
assert(FromState < ToState);
std::vector<uint32_t> NewCFIs;
uint32_t NestedLevel = 0;
for (uint32_t CurState = FromState; CurState < ToState; ++CurState) {
MCCFIInstruction *Instr = &FrameInstructions[CurState];
if (Instr->getOperation() == MCCFIInstruction::OpRememberState ||
Instr->getOperation() == MCCFIInstruction::OpRestoreState) {
// TODO: If in replaying the CFI instructions to reach this state we
// have state stack instructions, we could still work out the logic
// to extract only the necessary instructions to reach this state
// without using the state stack. Not sure if it is worth the effort
// because this happens rarely.
errs() << "FLO-WARNING: CFI rewriter expected state " << ToState
<< " but found " << FromState << " instead (@ " << getName()
<< "). Giving up this function.\n";
return false;
}
InsertIt =
addCFIPseudo(InBB, InsertIt, Instr - &*FrameInstructions.begin());
if (Instr->getOperation() == MCCFIInstruction::OpRememberState)
++NestedLevel;
if (!NestedLevel)
NewCFIs.push_back(CurState);
if (Instr->getOperation() == MCCFIInstruction::OpRestoreState)
--NestedLevel;
}
// TODO: If in replaying the CFI instructions to reach this state we
// have state stack instructions, we could still work out the logic
// to extract only the necessary instructions to reach this state
// without using the state stack. Not sure if it is worth the effort
// because this happens rarely.
if (NestedLevel != 0) {
errs() << "FLO-WARNING: CFI rewriter detected nested CFI state while "
<< " replaying CFI instructions for BB " << InBB->getName()
<< " in function " << getName() << '\n';
return false;
}
for(auto CFI : NewCFIs) {
InsertIt = addCFIPseudo(InBB, InsertIt, CFI);
++InsertIt;
}
@@ -863,66 +892,64 @@ bool BinaryFunction::fixCFIState() {
BB->IsCold != BasicBlocksLayout[I - 1]->IsCold)
State = 0;
// Check if state is what this BB expect it to be at its entry point
if (BBCFIState[BBIndex] != State) {
// Need to recover the correct state
if (BBCFIState[BBIndex] < State) {
// In this case, State is currently higher than what this BB expect it
// to be. To solve this, we need to insert a CFI instruction to remember
// the old state at function entry, then another CFI instruction to
// restore it at the entry of this BB and replay CFI instructions to
// reach the desired state.
uint32_t OldState = BBCFIState[BBIndex];
// Remember state at function entry point (our reference state).
BinaryBasicBlock::const_iterator InsertIt = EntryBB->begin();
while (InsertIt != EntryBB->end() && BC.MIA->isCFI(*InsertIt))
++InsertIt;
addCFIPseudo(EntryBB, InsertIt, FrameInstructions.size());
FrameInstructions.emplace_back(
MCCFIInstruction::createRememberState(nullptr));
// Restore state
InsertIt = addCFIPseudo(BB, BB->begin(), FrameInstructions.size());
// We need to recover the correct state if it doesn't match expected
// state at BB entry point.
if (BBCFIState[BBIndex] < State) {
// In this case, State is currently higher than what this BB expect it
// to be. To solve this, we need to insert a CFI instruction to remember
// the old state at function entry, then another CFI instruction to
// restore it at the entry of this BB and replay CFI instructions to
// reach the desired state.
uint32_t OldState = BBCFIState[BBIndex];
// Remember state at function entry point (our reference state).
BinaryBasicBlock::const_iterator InsertIt = EntryBB->begin();
while (InsertIt != EntryBB->end() && BC.MIA->isCFI(*InsertIt))
++InsertIt;
FrameInstructions.emplace_back(
MCCFIInstruction::createRestoreState(nullptr));
if (!replayCFIInstrs(0, OldState, BB, InsertIt))
return false;
// Check if we messed up the stack in this process
int StackOffset = 0;
for (BinaryBasicBlock *CurBB : BasicBlocksLayout) {
if (CurBB == BB)
break;
for (auto &Instr : *CurBB) {
if (MCCFIInstruction *CFI = getCFIFor(Instr)) {
if (CFI->getOperation() == MCCFIInstruction::OpRememberState)
++StackOffset;
if (CFI->getOperation() == MCCFIInstruction::OpRestoreState)
--StackOffset;
}
addCFIPseudo(EntryBB, InsertIt, FrameInstructions.size());
FrameInstructions.emplace_back(
MCCFIInstruction::createRememberState(nullptr));
// Restore state
InsertIt = addCFIPseudo(BB, BB->begin(), FrameInstructions.size());
++InsertIt;
FrameInstructions.emplace_back(
MCCFIInstruction::createRestoreState(nullptr));
if (!replayCFIInstrs(0, OldState, BB, InsertIt))
return false;
// Check if we messed up the stack in this process
int StackOffset = 0;
for (BinaryBasicBlock *CurBB : BasicBlocksLayout) {
if (CurBB == BB)
break;
for (auto &Instr : *CurBB) {
if (MCCFIInstruction *CFI = getCFIFor(Instr)) {
if (CFI->getOperation() == MCCFIInstruction::OpRememberState)
++StackOffset;
if (CFI->getOperation() == MCCFIInstruction::OpRestoreState)
--StackOffset;
}
}
auto Pos = BB->begin();
while (MCCFIInstruction *CFI = getCFIFor(*Pos++)) {
if (CFI->getOperation() == MCCFIInstruction::OpRememberState)
++StackOffset;
if (CFI->getOperation() == MCCFIInstruction::OpRestoreState)
--StackOffset;
}
if (StackOffset != 0) {
errs() << " FLO-WARNING: not possible to remember/recover state"
<< "without corrupting CFI state stack in function "
<< getName() << "\n";
return false;
}
} else {
// If BBCFIState[BBIndex] > State, it means we are behind in the
// state. Just emit all instructions to reach this state at the
// beginning of this BB. If this sequence of instructions involve
// remember state or restore state, bail out.
if (!replayCFIInstrs(State, BBCFIState[BBIndex], BB, BB->begin()))
return false;
}
auto Pos = BB->begin();
while (MCCFIInstruction *CFI = getCFIFor(*Pos++)) {
if (CFI->getOperation() == MCCFIInstruction::OpRememberState)
++StackOffset;
if (CFI->getOperation() == MCCFIInstruction::OpRestoreState)
--StackOffset;
}
if (StackOffset != 0) {
errs() << " FLO-WARNING: not possible to remember/recover state"
<< "without corrupting CFI state stack in function "
<< getName() << "\n";
return false;
}
} else if (BBCFIState[BBIndex] > State) {
// If BBCFIState[BBIndex] > State, it means we are behind in the
// state. Just emit all instructions to reach this state at the
// beginning of this BB. If this sequence of instructions involve
// remember state or restore state, bail out.
if (!replayCFIInstrs(State, BBCFIState[BBIndex], BB, BB->begin()))
return false;
}
State = BBCFIState[BBIndex + 1];

View File

@@ -603,8 +603,8 @@ void RewriteInstance::disassembleFunctions() {
if (SymRefI != FileSymRefs.end()) {
auto MaxSize = SymRefI->first - Function.getAddress();
if (MaxSize < Function.getSize()) {
DEBUG(dbgs() << "FLO: symbol seen in the middle of the function "
<< Function.getName() << ". Skipping.\n");
errs() << "FLO-WARNING: symbol seen in the middle of the function "
<< Function.getName() << ". Skipping.\n";
Function.setSimple(false);
continue;
}
@@ -676,6 +676,38 @@ void RewriteInstance::disassembleFunctions() {
"another function. We will not process this function.\n";
Func.setSimple(false);
}
uint64_t NumSimpleFunctions{0};
std::vector<BinaryFunction *> ProfiledFunctions;
for (auto &BFI : BinaryFunctions) {
if (!BFI.second.isSimple())
continue;
++NumSimpleFunctions;
if (BFI.second.getExecutionCount() != BinaryFunction::COUNT_NO_PROFILE)
ProfiledFunctions.push_back(&BFI.second);
}
errs() << "FLO-INFO: " << ProfiledFunctions.size() << " functions out of "
<< NumSimpleFunctions
<< " simple functions ("
<< format("%.f",
ProfiledFunctions.size() /
(float) NumSimpleFunctions * 100.0)
<< "%) have non-empty execution profile.\n";
if (ProfiledFunctions.size() > 10) {
errs() << "FLO-INFO: top called functions are:\n";
std::sort(ProfiledFunctions.begin(), ProfiledFunctions.end(),
[](BinaryFunction *A, BinaryFunction *B) {
return B->getExecutionCount() < A->getExecutionCount();
}
);
auto SFI = ProfiledFunctions.begin();
for(int i = 0; i < 50 && SFI != ProfiledFunctions.end(); ++SFI, ++i) {
errs() << " " << (*SFI)->getName() << " : "
<< (*SFI)->getExecutionCount() << '\n';
}
}
}
void RewriteInstance::runOptimizationPasses() {
@@ -745,13 +777,15 @@ void RewriteInstance::runOptimizationPasses() {
// Update exception handling information.
Function.updateEHRanges();
if (opts::PrintAll || opts::PrintEHRanges) {
if (opts::PrintAll || opts::PrintEHRanges)
Function.print(errs(), "after updating EH ranges");
}
// Fix the CFI state.
if (!Function.fixCFIState())
if (!Function.fixCFIState()) {
errs() << "FLO-WARNING: unable to fix CFI state for function "
<< Function.getName() << ". Skipping.\n";
Function.setSimple(false);
}
}
}