269 lines · cpp
1//===- bolt/Passes/InsertNegateRAStatePass.cpp ----------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the InsertNegateRAStatePass class. It inserts10// OpNegateRAState CFIs to places where the state of two consecutive11// instructions are different.12//13//===----------------------------------------------------------------------===//14#include "bolt/Passes/InsertNegateRAStatePass.h"15#include "bolt/Core/BinaryFunction.h"16#include "bolt/Core/ParallelUtilities.h"17#include <cstdlib>18 19using namespace llvm;20 21namespace llvm {22namespace bolt {23 24static bool PassFailed = false;25 26void InsertNegateRAState::runOnFunction(BinaryFunction &BF) {27 if (PassFailed)28 return;29 30 BinaryContext &BC = BF.getBinaryContext();31 32 if (BF.getState() == BinaryFunction::State::Empty)33 return;34 35 if (BF.getState() != BinaryFunction::State::CFG &&36 BF.getState() != BinaryFunction::State::CFG_Finalized) {37 BC.outs() << "BOLT-INFO: no CFG for " << BF.getPrintName()38 << " in InsertNegateRAStatePass\n";39 return;40 }41 42 inferUnknownStates(BF);43 44 for (FunctionFragment &FF : BF.getLayout().fragments()) {45 coverFunctionFragmentStart(BF, FF);46 bool FirstIter = true;47 bool PrevRAState = false;48 // As this pass runs after function splitting, we should only check49 // consecutive instructions inside FunctionFragments.50 for (BinaryBasicBlock *BB : FF) {51 for (auto It = BB->begin(); It != BB->end(); ++It) {52 MCInst &Inst = *It;53 if (BC.MIB->isCFI(Inst))54 continue;55 std::optional<bool> RAState = BC.MIB->getRAState(Inst);56 if (!RAState.has_value()) {57 BC.errs() << "BOLT-ERROR: unknown RAState after inferUnknownStates "58 << " in function " << BF.getPrintName() << "\n";59 PassFailed = true;60 return;61 }62 if (!FirstIter) {63 // Consecutive instructions with different RAState means we need to64 // add a OpNegateRAState.65 if (*RAState != PrevRAState)66 It = BF.addCFIInstruction(67 BB, It, MCCFIInstruction::createNegateRAState(nullptr));68 } else {69 FirstIter = false;70 }71 PrevRAState = *RAState;72 }73 }74 }75}76 77void InsertNegateRAState::inferUnknownStates(BinaryFunction &BF) {78 BinaryContext &BC = BF.getBinaryContext();79 80 // Fill in missing RAStates in simple cases (inside BBs).81 for (BinaryBasicBlock &BB : BF) {82 fillUnknownStateInBB(BC, BB);83 }84 // BasicBlocks which are made entirely of "new instructions" (instructions85 // without RAState annotation) are stubs, and do not have correct unwind info.86 // We should iterate in layout order and fill them based on previous known87 // RAState.88 fillUnknownStubs(BF);89}90 91void InsertNegateRAState::coverFunctionFragmentStart(BinaryFunction &BF,92 FunctionFragment &FF) {93 BinaryContext &BC = BF.getBinaryContext();94 if (FF.empty())95 return;96 // Find the first BB in the FF which has Instructions.97 // BOLT can generate empty BBs at function splitting which are only used as98 // target labels. We should add the negate-ra-state CFI to the first99 // non-empty BB.100 auto *FirstNonEmpty =101 std::find_if(FF.begin(), FF.end(), [](BinaryBasicBlock *BB) {102 // getFirstNonPseudo returns BB.end() if it does not find any103 // Instructions.104 return BB->getFirstNonPseudo() != BB->end();105 });106 // If a function is already split in the input, the first FF can also start107 // with Signed state. This covers that scenario as well.108 auto II = (*FirstNonEmpty)->getFirstNonPseudo();109 std::optional<bool> RAState = BC.MIB->getRAState(*II);110 if (!RAState.has_value()) {111 BC.errs() << "BOLT-ERROR: unknown RAState after inferUnknownStates "112 << " in function " << BF.getPrintName() << "\n";113 PassFailed = true;114 return;115 }116 if (*RAState)117 BF.addCFIInstruction(*FirstNonEmpty, II,118 MCCFIInstruction::createNegateRAState(nullptr));119}120 121std::optional<bool>122InsertNegateRAState::getFirstKnownRAState(BinaryContext &BC,123 BinaryBasicBlock &BB) {124 for (const MCInst &Inst : BB) {125 if (BC.MIB->isCFI(Inst))126 continue;127 std::optional<bool> RAState = BC.MIB->getRAState(Inst);128 if (RAState.has_value())129 return RAState;130 }131 return std::nullopt;132}133 134bool InsertNegateRAState::isUnknownBlock(BinaryContext &BC,135 BinaryBasicBlock &BB) {136 std::optional<bool> FirstRAState = getFirstKnownRAState(BC, BB);137 return !FirstRAState.has_value();138}139 140void InsertNegateRAState::fillUnknownStateInBB(BinaryContext &BC,141 BinaryBasicBlock &BB) {142 143 auto First = BB.getFirstNonPseudo();144 if (First == BB.end())145 return;146 // If the first instruction has unknown RAState, we should copy the first147 // known RAState.148 std::optional<bool> RAState = BC.MIB->getRAState(*First);149 if (!RAState.has_value()) {150 std::optional<bool> FirstRAState = getFirstKnownRAState(BC, BB);151 if (!FirstRAState.has_value())152 // We fill unknown BBs later.153 return;154 155 BC.MIB->setRAState(*First, *FirstRAState);156 }157 158 // At this point we know the RAState of the first instruction,159 // so we can propagate the RAStates to all subsequent unknown instructions.160 MCInst Prev = *First;161 for (auto It = First + 1; It != BB.end(); ++It) {162 MCInst &Inst = *It;163 if (BC.MIB->isCFI(Inst))164 continue;165 166 // No need to check for nullopt: we only entered this loop after the first167 // instruction had its RAState set, and RAState is always set for the168 // previous instruction in the previous iteration of the loop.169 std::optional<bool> PrevRAState = BC.MIB->getRAState(Prev);170 171 std::optional<bool> RAState = BC.MIB->getRAState(Inst);172 if (!RAState.has_value()) {173 if (BC.MIB->isPSignOnLR(Prev))174 PrevRAState = true;175 else if (BC.MIB->isPAuthOnLR(Prev))176 PrevRAState = false;177 BC.MIB->setRAState(Inst, *PrevRAState);178 }179 Prev = Inst;180 }181}182 183void InsertNegateRAState::markUnknownBlock(BinaryContext &BC,184 BinaryBasicBlock &BB, bool State) {185 // If we call this when an Instruction has either kRASigned or kRAUnsigned186 // annotation, setRASigned or setRAUnsigned would fail.187 assert(isUnknownBlock(BC, BB) &&188 "markUnknownBlock should only be called on unknown blocks");189 for (MCInst &Inst : BB) {190 if (BC.MIB->isCFI(Inst))191 continue;192 BC.MIB->setRAState(Inst, State);193 }194}195 196void InsertNegateRAState::fillUnknownStubs(BinaryFunction &BF) {197 BinaryContext &BC = BF.getBinaryContext();198 bool FirstIter = true;199 MCInst PrevInst;200 for (FunctionFragment &FF : BF.getLayout().fragments()) {201 for (BinaryBasicBlock *BB : FF) {202 if (FirstIter) {203 FirstIter = false;204 if (isUnknownBlock(BC, *BB))205 // If the first BasicBlock is unknown, the function's entry RAState206 // should be used.207 markUnknownBlock(BC, *BB, BF.getInitialRAState());208 } else if (isUnknownBlock(BC, *BB)) {209 // As explained in issue #160989, the unwind info is incorrect for210 // stubs. Indicating the correct RAState without the rest of the unwind211 // info being correct is not useful. Instead, we copy the RAState from212 // the previous instruction.213 std::optional<bool> PrevRAState = BC.MIB->getRAState(PrevInst);214 if (!PrevRAState.has_value()) {215 // No non-cfi instruction encountered in the function yet.216 // This means the RAState is the same as at the function entry.217 markUnknownBlock(BC, *BB, BF.getInitialRAState());218 continue;219 }220 221 if (BC.MIB->isPSignOnLR(PrevInst))222 PrevRAState = true;223 else if (BC.MIB->isPAuthOnLR(PrevInst))224 PrevRAState = false;225 markUnknownBlock(BC, *BB, *PrevRAState);226 }227 // This function iterates on BasicBlocks, so the PrevInst has to be228 // updated to the last instruction of the current BasicBlock. If the229 // BasicBlock is empty, or only has PseudoInstructions, PrevInst will not230 // be updated.231 auto Last = BB->getLastNonPseudo();232 if (Last != BB->rend())233 PrevInst = *Last;234 }235 }236}237 238Error InsertNegateRAState::runOnFunctions(BinaryContext &BC) {239 std::atomic<uint64_t> FunctionsModified{0};240 ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {241 FunctionsModified++;242 runOnFunction(BF);243 };244 245 ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) {246 // We can skip functions which did not include negate-ra-state CFIs. This247 // includes code using pac-ret hardening as well, if the binary is248 // compiled with `-fno-exceptions -fno-unwind-tables249 // -fno-asynchronous-unwind-tables`250 return !BF.containedNegateRAState() || BF.isIgnored();251 };252 253 ParallelUtilities::runOnEachFunction(254 BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,255 SkipPredicate, "InsertNegateRAStatePass");256 257 BC.outs() << "BOLT-INFO: rewritten pac-ret DWARF info in "258 << FunctionsModified << " out of " << BC.getBinaryFunctions().size()259 << " functions "260 << format("(%.2lf%%).\n", (100.0 * FunctionsModified) /261 BC.getBinaryFunctions().size());262 if (PassFailed)263 return createFatalBOLTError("");264 return Error::success();265}266 267} // end namespace bolt268} // end namespace llvm269