blob: 33664e1160a7b07c58809c2d00f08bb35180f46f [file] [log] [blame] [edit]
//===- 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 {
void InsertNegateRAState::runOnFunction(BinaryFunction &BF) {
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;
MCInst PrevInst;
// 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;
if (!FirstIter) {
// Consecutive instructions with different RAState means we need to
// add a OpNegateRAState.
if ((BC.MIB->isRASigned(PrevInst) && BC.MIB->isRAUnsigned(Inst)) ||
(BC.MIB->isRAUnsigned(PrevInst) && BC.MIB->isRASigned(Inst))) {
It = BF.addCFIInstruction(
BB, It, MCCFIInstruction::createNegateRAState(nullptr));
}
} else {
FirstIter = false;
}
PrevInst = *It;
}
}
}
}
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.
if (BC.MIB->isRASigned(*((*FirstNonEmpty)->begin()))) {
BF.addCFIInstruction(*FirstNonEmpty, (*FirstNonEmpty)->begin(),
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;
if (!FirstIter && BC.MIB->isRAStateUnknown(Inst)) {
if (BC.MIB->isRASigned(PrevInst) || BC.MIB->isPSignOnLR(PrevInst)) {
BC.MIB->setRASigned(Inst);
} else if (BC.MIB->isRAUnsigned(PrevInst) ||
BC.MIB->isPAuthOnLR(PrevInst)) {
BC.MIB->setRAUnsigned(Inst);
}
} else {
FirstIter = false;
}
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());
return Error::success();
}
} // end namespace bolt
} // end namespace llvm