mirror of
https://github.com/intel/llvm.git
synced 2026-01-22 23:49:22 +08:00
Rename passes to names that better reflect their intent, and describe their relationship to each other. InsertNegateRAStatePass renamed to PointerAuthCFIFixup, MarkRAStates renamed to PointerAuthCFIAnalyzer. Added the --print-<passname> flags for these passes.
269 lines
9.6 KiB
C++
269 lines
9.6 KiB
C++
//===- bolt/Passes/PointerAuthCFIFixup.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 PointerAuthCFIFixup class. It inserts
|
|
// OpNegateRAState CFIs to places where the state of two consecutive
|
|
// instructions are different.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "bolt/Passes/PointerAuthCFIFixup.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 PointerAuthCFIFixup::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 PointerAuthCFIFixup\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;
|
|
std::optional<bool> RAState = BC.MIB->getRAState(Inst);
|
|
if (!RAState.has_value()) {
|
|
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 PointerAuthCFIFixup::inferUnknownStates(BinaryFunction &BF) {
|
|
BinaryContext &BC = BF.getBinaryContext();
|
|
|
|
// Fill in missing RAStates in simple cases (inside BBs).
|
|
for (BinaryBasicBlock &BB : BF) {
|
|
fillUnknownStateInBB(BC, BB);
|
|
}
|
|
// BasicBlocks which are made entirely of "new instructions" (instructions
|
|
// without RAState annotation) are stubs, and do not have correct unwind info.
|
|
// We should iterate in layout order and fill them based on previous known
|
|
// RAState.
|
|
fillUnknownStubs(BF);
|
|
}
|
|
|
|
void PointerAuthCFIFixup::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();
|
|
std::optional<bool> RAState = BC.MIB->getRAState(*II);
|
|
if (!RAState.has_value()) {
|
|
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));
|
|
}
|
|
|
|
std::optional<bool>
|
|
PointerAuthCFIFixup::getFirstKnownRAState(BinaryContext &BC,
|
|
BinaryBasicBlock &BB) {
|
|
for (const MCInst &Inst : BB) {
|
|
if (BC.MIB->isCFI(Inst))
|
|
continue;
|
|
std::optional<bool> RAState = BC.MIB->getRAState(Inst);
|
|
if (RAState.has_value())
|
|
return RAState;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
bool PointerAuthCFIFixup::isUnknownBlock(BinaryContext &BC,
|
|
BinaryBasicBlock &BB) {
|
|
std::optional<bool> FirstRAState = getFirstKnownRAState(BC, BB);
|
|
return !FirstRAState.has_value();
|
|
}
|
|
|
|
void PointerAuthCFIFixup::fillUnknownStateInBB(BinaryContext &BC,
|
|
BinaryBasicBlock &BB) {
|
|
|
|
auto First = BB.getFirstNonPseudo();
|
|
if (First == BB.end())
|
|
return;
|
|
// If the first instruction has unknown RAState, we should copy the first
|
|
// known RAState.
|
|
std::optional<bool> RAState = BC.MIB->getRAState(*First);
|
|
if (!RAState.has_value()) {
|
|
std::optional<bool> FirstRAState = getFirstKnownRAState(BC, BB);
|
|
if (!FirstRAState.has_value())
|
|
// We fill unknown BBs later.
|
|
return;
|
|
|
|
BC.MIB->setRAState(*First, *FirstRAState);
|
|
}
|
|
|
|
// At this point we know the RAState of the first instruction,
|
|
// so we can propagate the RAStates to all subsequent unknown instructions.
|
|
MCInst Prev = *First;
|
|
for (auto It = First + 1; It != BB.end(); ++It) {
|
|
MCInst &Inst = *It;
|
|
if (BC.MIB->isCFI(Inst))
|
|
continue;
|
|
|
|
// No need to check for nullopt: we only entered this loop after the first
|
|
// instruction had its RAState set, and RAState is always set for the
|
|
// previous instruction in the previous iteration of the loop.
|
|
std::optional<bool> PrevRAState = BC.MIB->getRAState(Prev);
|
|
|
|
std::optional<bool> RAState = BC.MIB->getRAState(Inst);
|
|
if (!RAState.has_value()) {
|
|
if (BC.MIB->isPSignOnLR(Prev))
|
|
PrevRAState = true;
|
|
else if (BC.MIB->isPAuthOnLR(Prev))
|
|
PrevRAState = false;
|
|
BC.MIB->setRAState(Inst, *PrevRAState);
|
|
}
|
|
Prev = Inst;
|
|
}
|
|
}
|
|
|
|
void PointerAuthCFIFixup::markUnknownBlock(BinaryContext &BC,
|
|
BinaryBasicBlock &BB, bool State) {
|
|
// If we call this when an Instruction has either kRASigned or kRAUnsigned
|
|
// annotation, setRASigned or setRAUnsigned would fail.
|
|
assert(isUnknownBlock(BC, BB) &&
|
|
"markUnknownBlock should only be called on unknown blocks");
|
|
for (MCInst &Inst : BB) {
|
|
if (BC.MIB->isCFI(Inst))
|
|
continue;
|
|
BC.MIB->setRAState(Inst, State);
|
|
}
|
|
}
|
|
|
|
void PointerAuthCFIFixup::fillUnknownStubs(BinaryFunction &BF) {
|
|
BinaryContext &BC = BF.getBinaryContext();
|
|
bool FirstIter = true;
|
|
MCInst PrevInst;
|
|
for (FunctionFragment &FF : BF.getLayout().fragments()) {
|
|
for (BinaryBasicBlock *BB : FF) {
|
|
if (FirstIter) {
|
|
FirstIter = false;
|
|
if (isUnknownBlock(BC, *BB))
|
|
// If the first BasicBlock is unknown, the function's entry RAState
|
|
// should be used.
|
|
markUnknownBlock(BC, *BB, BF.getInitialRAState());
|
|
} else if (isUnknownBlock(BC, *BB)) {
|
|
// As explained in issue #160989, the unwind info is incorrect for
|
|
// stubs. Indicating the correct RAState without the rest of the unwind
|
|
// info being correct is not useful. Instead, we copy the RAState from
|
|
// the previous instruction.
|
|
std::optional<bool> PrevRAState = BC.MIB->getRAState(PrevInst);
|
|
if (!PrevRAState.has_value()) {
|
|
// No non-cfi instruction encountered in the function yet.
|
|
// This means the RAState is the same as at the function entry.
|
|
markUnknownBlock(BC, *BB, BF.getInitialRAState());
|
|
continue;
|
|
}
|
|
|
|
if (BC.MIB->isPSignOnLR(PrevInst))
|
|
PrevRAState = true;
|
|
else if (BC.MIB->isPAuthOnLR(PrevInst))
|
|
PrevRAState = false;
|
|
markUnknownBlock(BC, *BB, *PrevRAState);
|
|
}
|
|
// This function iterates on BasicBlocks, so the PrevInst has to be
|
|
// updated to the last instruction of the current BasicBlock. If the
|
|
// BasicBlock is empty, or only has PseudoInstructions, PrevInst will not
|
|
// be updated.
|
|
auto Last = BB->getLastNonPseudo();
|
|
if (Last != BB->rend())
|
|
PrevInst = *Last;
|
|
}
|
|
}
|
|
}
|
|
|
|
Error PointerAuthCFIFixup::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, "PointerAuthCFIFixup");
|
|
|
|
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
|