diff options
Diffstat (limited to 'bolt/lib/Passes/InsertNegateRAStatePass.cpp')
-rw-r--r-- | bolt/lib/Passes/InsertNegateRAStatePass.cpp | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/bolt/lib/Passes/InsertNegateRAStatePass.cpp b/bolt/lib/Passes/InsertNegateRAStatePass.cpp new file mode 100644 index 0000000..33664e1 --- /dev/null +++ b/bolt/lib/Passes/InsertNegateRAStatePass.cpp @@ -0,0 +1,142 @@ +//===- 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 |