- unify isRAStateSigned and isRAStateUnsigned to a common getRAState, - unify setRASigned and setRAUnsigned into setRAState(MCInst, bool), - update users of these to match the new implementations.
168 lines
5.7 KiB
C++
168 lines
5.7 KiB
C++
//===- bolt/Passes/InsertNegateRAStatePass.cpp ----------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the InsertNegateRAStatePass class. It inserts
|
|
// OpNegateRAState CFIs to places where the state of two consecutive
|
|
// instructions are different.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "bolt/Passes/InsertNegateRAStatePass.h"
|
|
#include "bolt/Core/BinaryFunction.h"
|
|
#include "bolt/Core/ParallelUtilities.h"
|
|
#include <cstdlib>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace llvm {
|
|
namespace bolt {
|
|
|
|
static bool PassFailed = false;
|
|
|
|
void InsertNegateRAState::runOnFunction(BinaryFunction &BF) {
|
|
if (PassFailed)
|
|
return;
|
|
|
|
BinaryContext &BC = BF.getBinaryContext();
|
|
|
|
if (BF.getState() == BinaryFunction::State::Empty)
|
|
return;
|
|
|
|
if (BF.getState() != BinaryFunction::State::CFG &&
|
|
BF.getState() != BinaryFunction::State::CFG_Finalized) {
|
|
BC.outs() << "BOLT-INFO: no CFG for " << BF.getPrintName()
|
|
<< " in InsertNegateRAStatePass\n";
|
|
return;
|
|
}
|
|
|
|
inferUnknownStates(BF);
|
|
|
|
for (FunctionFragment &FF : BF.getLayout().fragments()) {
|
|
coverFunctionFragmentStart(BF, FF);
|
|
bool FirstIter = true;
|
|
bool PrevRAState = false;
|
|
// As this pass runs after function splitting, we should only check
|
|
// consecutive instructions inside FunctionFragments.
|
|
for (BinaryBasicBlock *BB : FF) {
|
|
for (auto It = BB->begin(); It != BB->end(); ++It) {
|
|
MCInst &Inst = *It;
|
|
if (BC.MIB->isCFI(Inst))
|
|
continue;
|
|
auto RAState = BC.MIB->getRAState(Inst);
|
|
if (!RAState) {
|
|
BC.errs() << "BOLT-ERROR: unknown RAState after inferUnknownStates "
|
|
<< " in function " << BF.getPrintName() << "\n";
|
|
PassFailed = true;
|
|
return;
|
|
}
|
|
if (!FirstIter) {
|
|
// Consecutive instructions with different RAState means we need to
|
|
// add a OpNegateRAState.
|
|
if (*RAState != PrevRAState)
|
|
It = BF.addCFIInstruction(
|
|
BB, It, MCCFIInstruction::createNegateRAState(nullptr));
|
|
} else {
|
|
FirstIter = false;
|
|
}
|
|
PrevRAState = *RAState;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InsertNegateRAState::coverFunctionFragmentStart(BinaryFunction &BF,
|
|
FunctionFragment &FF) {
|
|
BinaryContext &BC = BF.getBinaryContext();
|
|
if (FF.empty())
|
|
return;
|
|
// Find the first BB in the FF which has Instructions.
|
|
// BOLT can generate empty BBs at function splitting which are only used as
|
|
// target labels. We should add the negate-ra-state CFI to the first
|
|
// non-empty BB.
|
|
auto *FirstNonEmpty =
|
|
std::find_if(FF.begin(), FF.end(), [](BinaryBasicBlock *BB) {
|
|
// getFirstNonPseudo returns BB.end() if it does not find any
|
|
// Instructions.
|
|
return BB->getFirstNonPseudo() != BB->end();
|
|
});
|
|
// If a function is already split in the input, the first FF can also start
|
|
// with Signed state. This covers that scenario as well.
|
|
auto II = (*FirstNonEmpty)->getFirstNonPseudo();
|
|
auto RAState = BC.MIB->getRAState(*II);
|
|
if (!RAState) {
|
|
BC.errs() << "BOLT-ERROR: unknown RAState after inferUnknownStates "
|
|
<< " in function " << BF.getPrintName() << "\n";
|
|
PassFailed = true;
|
|
return;
|
|
}
|
|
if (*RAState)
|
|
BF.addCFIInstruction(*FirstNonEmpty, II,
|
|
MCCFIInstruction::createNegateRAState(nullptr));
|
|
}
|
|
|
|
void InsertNegateRAState::inferUnknownStates(BinaryFunction &BF) {
|
|
BinaryContext &BC = BF.getBinaryContext();
|
|
bool FirstIter = true;
|
|
MCInst PrevInst;
|
|
for (BinaryBasicBlock &BB : BF) {
|
|
for (MCInst &Inst : BB) {
|
|
if (BC.MIB->isCFI(Inst))
|
|
continue;
|
|
|
|
auto RAState = BC.MIB->getRAState(Inst);
|
|
if (!FirstIter && !RAState) {
|
|
if (BC.MIB->isPSignOnLR(PrevInst))
|
|
RAState = true;
|
|
else if (BC.MIB->isPAuthOnLR(PrevInst))
|
|
RAState = false;
|
|
else {
|
|
auto PrevRAState = BC.MIB->getRAState(PrevInst);
|
|
RAState = PrevRAState ? *PrevRAState : false;
|
|
}
|
|
BC.MIB->setRAState(Inst, *RAState);
|
|
} else {
|
|
FirstIter = false;
|
|
if (!RAState)
|
|
BC.MIB->setRAState(Inst, BF.getInitialRAState());
|
|
}
|
|
PrevInst = Inst;
|
|
}
|
|
}
|
|
}
|
|
|
|
Error InsertNegateRAState::runOnFunctions(BinaryContext &BC) {
|
|
std::atomic<uint64_t> FunctionsModified{0};
|
|
ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
|
|
FunctionsModified++;
|
|
runOnFunction(BF);
|
|
};
|
|
|
|
ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) {
|
|
// We can skip functions which did not include negate-ra-state CFIs. This
|
|
// includes code using pac-ret hardening as well, if the binary is
|
|
// compiled with `-fno-exceptions -fno-unwind-tables
|
|
// -fno-asynchronous-unwind-tables`
|
|
return !BF.containedNegateRAState() || BF.isIgnored();
|
|
};
|
|
|
|
ParallelUtilities::runOnEachFunction(
|
|
BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,
|
|
SkipPredicate, "InsertNegateRAStatePass");
|
|
|
|
BC.outs() << "BOLT-INFO: rewritten pac-ret DWARF info in "
|
|
<< FunctionsModified << " out of " << BC.getBinaryFunctions().size()
|
|
<< " functions "
|
|
<< format("(%.2lf%%).\n", (100.0 * FunctionsModified) /
|
|
BC.getBinaryFunctions().size());
|
|
if (PassFailed)
|
|
return createFatalBOLTError("");
|
|
return Error::success();
|
|
}
|
|
|
|
} // end namespace bolt
|
|
} // end namespace llvm
|