brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.6 KiB · ed4de8a Raw
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