brintos

brintos / llvm-project-archived public Read only

0
0
Text · 34.3 KiB · 27656c7 Raw
922 lines · cpp
1//===- bolt/Core/Exceptions.cpp - Helpers for C++ exceptions --------------===//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 functions for handling C++ exception meta data.10//11// Some of the code is taken from examples/ExceptionDemo12//13//===----------------------------------------------------------------------===//14 15#include "bolt/Core/Exceptions.h"16#include "bolt/Core/BinaryFunction.h"17#include "llvm/ADT/ArrayRef.h"18#include "llvm/ADT/Twine.h"19#include "llvm/BinaryFormat/Dwarf.h"20#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"21#include "llvm/Support/Casting.h"22#include "llvm/Support/CommandLine.h"23#include "llvm/Support/Debug.h"24#include "llvm/Support/Errc.h"25#include "llvm/Support/LEB128.h"26#include "llvm/Support/MathExtras.h"27#include "llvm/Support/raw_ostream.h"28#include <map>29 30#undef  DEBUG_TYPE31#define DEBUG_TYPE "bolt-exceptions"32 33using namespace llvm::dwarf;34 35namespace opts {36 37extern llvm::cl::OptionCategory BoltCategory;38 39extern llvm::cl::opt<unsigned> Verbosity;40 41static llvm::cl::opt<bool>42    PrintExceptions("print-exceptions",43                    llvm::cl::desc("print exception handling data"),44                    llvm::cl::Hidden, llvm::cl::cat(BoltCategory));45 46} // namespace opts47 48namespace llvm {49namespace bolt {50 51// Read and dump the .gcc_exception_table section entry.52//53// .gcc_except_table section contains a set of Language-Specific Data Areas -54// a fancy name for exception handling tables. There's one  LSDA entry per55// function. However, we can't actually tell which function LSDA refers to56// unless we parse .eh_frame entry that refers to the LSDA.57// Then inside LSDA most addresses are encoded relative to the function start,58// so we need the function context in order to get to real addresses.59//60// The best visual representation of the tables comprising LSDA and61// relationships between them is illustrated at:62//   https://github.com/itanium-cxx-abi/cxx-abi/blob/master/exceptions.pdf63// Keep in mind that GCC implementation deviates slightly from that document.64//65// To summarize, there are 4 tables in LSDA: call site table, actions table,66// types table, and types index table (for indirection). The main table contains67// call site entries. Each call site includes a PC range that can throw an68// exception, a handler (landing pad), and a reference to an entry in the action69// table. The handler and/or action could be 0. The action entry is a head70// of a list of actions associated with a call site. The action table contains71// all such lists (it could be optimized to share list tails). Each action could72// be either to catch an exception of a given type, to perform a cleanup, or to73// propagate the exception after filtering it out (e.g. to make sure function74// exception specification is not violated). Catch action contains a reference75// to an entry in the type table, and filter action refers to an entry in the76// type index table to encode a set of types to filter.77//78// Call site table follows LSDA header. Action table immediately follows the79// call site table.80//81// Both types table and type index table start at the same location, but they82// grow in opposite directions (types go up, indices go down). The beginning of83// these tables is encoded in LSDA header. Sizes for both of the tables are not84// included anywhere.85//86// We have to parse all of the tables to determine their sizes. Then we have87// to parse the call site table and associate discovered information with88// actual call instructions and landing pad blocks.89//90// For the purpose of rewriting exception handling tables, we can reuse action,91// and type index tables in their original binary format.92//93// Type table could be encoded using position-independent references, and thus94// may require relocation.95//96// Ideally we should be able to re-write LSDA in-place, without the need to97// allocate a new space for it. Sadly there's no guarantee that the new call98// site table will be the same size as GCC uses uleb encodings for PC offsets.99//100// Note: some functions have LSDA entries with 0 call site entries.101Error BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,102                                uint64_t LSDASectionAddress) {103  assert(CurrentState == State::Disassembled && "unexpected function state");104 105  if (!getLSDAAddress())106    return Error::success();107 108  DWARFDataExtractor Data(109      StringRef(reinterpret_cast<const char *>(LSDASectionData.data()),110                LSDASectionData.size()),111      BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize());112  uint64_t Offset = getLSDAAddress() - LSDASectionAddress;113  assert(Data.isValidOffset(Offset) && "wrong LSDA address");114 115  const uint8_t LPStartEncoding = Data.getU8(&Offset);116  uint64_t LPStart = Address;117  if (LPStartEncoding != dwarf::DW_EH_PE_omit) {118    std::optional<uint64_t> MaybeLPStart = Data.getEncodedPointer(119        &Offset, LPStartEncoding, Offset + LSDASectionAddress);120    if (!MaybeLPStart) {121      BC.errs() << "BOLT-ERROR: unsupported LPStartEncoding: "122                << (unsigned)LPStartEncoding << '\n';123      return createFatalBOLTError("");124    }125    LPStart = *MaybeLPStart;126  }127 128  const uint8_t TTypeEncoding = Data.getU8(&Offset);129  LSDATypeEncoding = TTypeEncoding;130  size_t TTypeEncodingSize = 0;131  uintptr_t TTypeEnd = 0;132  if (TTypeEncoding != DW_EH_PE_omit) {133    TTypeEnd = Data.getULEB128(&Offset);134    TTypeEncodingSize = BC.getDWARFEncodingSize(TTypeEncoding);135  }136 137  if (opts::PrintExceptions) {138    BC.outs() << "[LSDA at 0x" << Twine::utohexstr(getLSDAAddress())139              << " for function " << *this << "]:\n";140    BC.outs() << "LPStart Encoding = 0x" << Twine::utohexstr(LPStartEncoding)141              << '\n';142    BC.outs() << "LPStart = 0x" << Twine::utohexstr(LPStart) << '\n';143    BC.outs() << "TType Encoding = 0x" << Twine::utohexstr(TTypeEncoding)144              << '\n';145    BC.outs() << "TType End = " << TTypeEnd << '\n';146  }147 148  // Table to store list of indices in type table. Entries are uleb128 values.149  const uint64_t TypeIndexTableStart = Offset + TTypeEnd;150 151  // Offset past the last decoded index.152  uint64_t MaxTypeIndexTableOffset = 0;153 154  // Max positive index used in type table.155  unsigned MaxTypeIndex = 0;156 157  // The actual type info table starts at the same location, but grows in158  // opposite direction. TTypeEncoding is used to encode stored values.159  const uint64_t TypeTableStart = Offset + TTypeEnd;160 161  uint8_t CallSiteEncoding = Data.getU8(&Offset);162  uint32_t CallSiteTableLength = Data.getULEB128(&Offset);163  uint64_t CallSiteTableStart = Offset;164  uint64_t CallSiteTableEnd = CallSiteTableStart + CallSiteTableLength;165  uint64_t CallSitePtr = CallSiteTableStart;166  uint64_t ActionTableStart = CallSiteTableEnd;167 168  if (opts::PrintExceptions) {169    BC.outs() << "CallSite Encoding = " << (unsigned)CallSiteEncoding << '\n';170    BC.outs() << "CallSite table length = " << CallSiteTableLength << '\n';171    BC.outs() << '\n';172  }173 174  this->HasEHRanges = CallSitePtr < CallSiteTableEnd;175  const uint64_t RangeBase = getAddress();176  while (CallSitePtr < CallSiteTableEnd) {177    uint64_t Start = *Data.getEncodedPointer(&CallSitePtr, CallSiteEncoding,178                                             CallSitePtr + LSDASectionAddress);179    uint64_t Length = *Data.getEncodedPointer(&CallSitePtr, CallSiteEncoding,180                                              CallSitePtr + LSDASectionAddress);181    uint64_t LandingPad = *Data.getEncodedPointer(182        &CallSitePtr, CallSiteEncoding, CallSitePtr + LSDASectionAddress);183    uint64_t ActionEntry = Data.getULEB128(&CallSitePtr);184    if (LandingPad)185      LandingPad += LPStart;186 187    if (opts::PrintExceptions) {188      BC.outs() << "Call Site: [0x" << Twine::utohexstr(RangeBase + Start)189                << ", 0x" << Twine::utohexstr(RangeBase + Start + Length)190                << "); landing pad: 0x" << Twine::utohexstr(LandingPad)191                << "; action entry: 0x" << Twine::utohexstr(ActionEntry)192                << "\n";193      BC.outs() << "  current offset is " << (CallSitePtr - CallSiteTableStart)194                << '\n';195    }196 197    // Create a handler entry if necessary.198    MCSymbol *LPSymbol = nullptr;199    if (LandingPad) {200      // Verify if landing pad code is located outside current function201      // Support landing pad to builtin_unreachable202      if (LandingPad < Address || LandingPad > Address + getSize()) {203        BinaryFunction *Fragment =204            BC.getBinaryFunctionContainingAddress(LandingPad);205        assert(Fragment != nullptr &&206               "BOLT-ERROR: cannot find landing pad fragment");207        BC.addInterproceduralReference(this, Fragment->getAddress());208        BC.processInterproceduralReferences();209        assert(BC.areRelatedFragments(this, Fragment) &&210               "BOLT-ERROR: cannot have landing pads in different functions");211        setHasIndirectTargetToSplitFragment(true);212        BC.addFragmentsToSkip(this);213        return Error::success();214      }215 216      const uint64_t LPOffset = LandingPad - getAddress();217      if (!getInstructionAtOffset(LPOffset)) {218        if (opts::Verbosity >= 1)219          BC.errs() << "BOLT-WARNING: landing pad "220                    << Twine::utohexstr(LPOffset)221                    << " not pointing to an instruction in function " << *this222                    << " - ignoring.\n";223      } else {224        auto Label = Labels.find(LPOffset);225        if (Label != Labels.end()) {226          LPSymbol = Label->second;227        } else {228          LPSymbol = BC.Ctx->createNamedTempSymbol("LP");229          Labels[LPOffset] = LPSymbol;230        }231      }232    }233 234    // Mark all call instructions in the range.235    auto II = Instructions.find(Start);236    auto IE = Instructions.end();237    assert(II != IE && "exception range not pointing to an instruction");238    do {239      MCInst &Instruction = II->second;240      if (BC.MIB->isCall(Instruction) &&241          !BC.MIB->getConditionalTailCall(Instruction)) {242        assert(!BC.MIB->isInvoke(Instruction) &&243               "overlapping exception ranges detected");244        // Add extra operands to a call instruction making it an invoke from245        // now on.246        BC.MIB->addEHInfo(Instruction,247                          MCPlus::MCLandingPad(LPSymbol, ActionEntry));248      }249      ++II;250    } while (II != IE && II->first < Start + Length);251 252    if (ActionEntry != 0) {253      auto printType = [&](int Index, raw_ostream &OS) {254        assert(Index > 0 && "only positive indices are valid");255        uint64_t TTEntry = TypeTableStart - Index * TTypeEncodingSize;256        const uint64_t TTEntryAddress = TTEntry + LSDASectionAddress;257        uint64_t TypeAddress =258            *Data.getEncodedPointer(&TTEntry, TTypeEncoding, TTEntryAddress);259        if ((TTypeEncoding & DW_EH_PE_pcrel) && TypeAddress == TTEntryAddress)260          TypeAddress = 0;261        if (TypeAddress == 0) {262          OS << "<all>";263          return;264        }265        if (TTypeEncoding & DW_EH_PE_indirect) {266          ErrorOr<uint64_t> PointerOrErr = BC.getPointerAtAddress(TypeAddress);267          assert(PointerOrErr && "failed to decode indirect address");268          TypeAddress = *PointerOrErr;269        }270        if (BinaryData *TypeSymBD = BC.getBinaryDataAtAddress(TypeAddress))271          OS << TypeSymBD->getName();272        else273          OS << "0x" << Twine::utohexstr(TypeAddress);274      };275      if (opts::PrintExceptions)276        BC.outs() << "    actions: ";277      uint64_t ActionPtr = ActionTableStart + ActionEntry - 1;278      int64_t ActionType;279      int64_t ActionNext;280      const char *Sep = "";281      do {282        ActionType = Data.getSLEB128(&ActionPtr);283        const uint32_t Self = ActionPtr;284        ActionNext = Data.getSLEB128(&ActionPtr);285        if (opts::PrintExceptions)286          BC.outs() << Sep << "(" << ActionType << ", " << ActionNext << ") ";287        if (ActionType == 0) {288          if (opts::PrintExceptions)289            BC.outs() << "cleanup";290        } else if (ActionType > 0) {291          // It's an index into a type table.292          MaxTypeIndex =293              std::max(MaxTypeIndex, static_cast<unsigned>(ActionType));294          if (opts::PrintExceptions) {295            BC.outs() << "catch type ";296            printType(ActionType, BC.outs());297          }298        } else { // ActionType < 0299          if (opts::PrintExceptions)300            BC.outs() << "filter exception types ";301          const char *TSep = "";302          // ActionType is a negative *byte* offset into *uleb128-encoded* table303          // of indices with base 1.304          // E.g. -1 means offset 0, -2 is offset 1, etc. The indices are305          // encoded using uleb128 thus we cannot directly dereference them.306          uint64_t TypeIndexTablePtr = TypeIndexTableStart - ActionType - 1;307          while (uint64_t Index = Data.getULEB128(&TypeIndexTablePtr)) {308            MaxTypeIndex = std::max(MaxTypeIndex, static_cast<unsigned>(Index));309            if (opts::PrintExceptions) {310              BC.outs() << TSep;311              printType(Index, BC.outs());312              TSep = ", ";313            }314          }315          MaxTypeIndexTableOffset = std::max(316              MaxTypeIndexTableOffset, TypeIndexTablePtr - TypeIndexTableStart);317        }318 319        Sep = "; ";320 321        ActionPtr = Self + ActionNext;322      } while (ActionNext);323      if (opts::PrintExceptions)324        BC.outs() << '\n';325    }326  }327  if (opts::PrintExceptions)328    BC.outs() << '\n';329 330  assert(TypeIndexTableStart + MaxTypeIndexTableOffset <=331             Data.getData().size() &&332         "LSDA entry has crossed section boundary");333 334  if (TTypeEnd) {335    LSDAActionTable = LSDASectionData.slice(336        ActionTableStart, TypeIndexTableStart -337                              MaxTypeIndex * TTypeEncodingSize -338                              ActionTableStart);339    for (unsigned Index = 1; Index <= MaxTypeIndex; ++Index) {340      uint64_t TTEntry = TypeTableStart - Index * TTypeEncodingSize;341      const uint64_t TTEntryAddress = TTEntry + LSDASectionAddress;342      uint64_t TypeAddress =343          *Data.getEncodedPointer(&TTEntry, TTypeEncoding, TTEntryAddress);344      if ((TTypeEncoding & DW_EH_PE_pcrel) && (TypeAddress == TTEntryAddress))345        TypeAddress = 0;346      if (TTypeEncoding & DW_EH_PE_indirect) {347        LSDATypeAddressTable.emplace_back(TypeAddress);348        if (TypeAddress) {349          ErrorOr<uint64_t> PointerOrErr = BC.getPointerAtAddress(TypeAddress);350          assert(PointerOrErr && "failed to decode indirect address");351          TypeAddress = *PointerOrErr;352        }353      }354      LSDATypeTable.emplace_back(TypeAddress);355    }356    LSDATypeIndexTable =357        LSDASectionData.slice(TypeIndexTableStart, MaxTypeIndexTableOffset);358  }359  return Error::success();360}361 362void BinaryFunction::updateEHRanges() {363  if (getSize() == 0)364    return;365 366  assert(CurrentState == State::CFG_Finalized && "unexpected state");367 368  // Build call sites table.369  struct EHInfo {370    const MCSymbol *LP; // landing pad371    uint64_t Action;372  };373 374  // Sites to update.375  CallSitesList Sites;376 377  for (FunctionFragment &FF : getLayout().fragments()) {378    // If previous call can throw, this is its exception handler.379    EHInfo PreviousEH = {nullptr, 0};380 381    // Marker for the beginning of exceptions range.382    const MCSymbol *StartRange = nullptr;383 384    for (BinaryBasicBlock *const BB : FF) {385      for (MCInst &Instr : *BB) {386        if (!BC.MIB->isCall(Instr))387          continue;388 389        // Instruction can throw an exception that should be handled.390        const bool Throws = BC.MIB->isInvoke(Instr);391 392        // Ignore the call if it's a continuation of a no-throw gap.393        if (!Throws && !StartRange)394          continue;395 396        // Extract exception handling information from the instruction.397        const MCSymbol *LP = nullptr;398        uint64_t Action = 0;399        if (const std::optional<MCPlus::MCLandingPad> EHInfo =400                BC.MIB->getEHInfo(Instr))401          std::tie(LP, Action) = *EHInfo;402 403        // No action if the exception handler has not changed.404        if (Throws && StartRange && PreviousEH.LP == LP &&405            PreviousEH.Action == Action)406          continue;407 408        // Same symbol is used for the beginning and the end of the range.409        MCSymbol *EHSymbol;410        if (MCSymbol *InstrLabel = BC.MIB->getInstLabel(Instr)) {411          EHSymbol = InstrLabel;412        } else {413          std::unique_lock<llvm::sys::RWMutex> Lock(BC.CtxMutex);414          EHSymbol = BC.MIB->getOrCreateInstLabel(Instr, "EH", BC.Ctx.get());415        }416 417        // At this point we could be in one of the following states:418        //419        // I. Exception handler has changed and we need to close previous range420        //    and start a new one.421        //422        // II. Start a new exception range after the gap.423        //424        // III. Close current exception range and start a new gap.425        const MCSymbol *EndRange;426        if (StartRange) {427          // I, III:428          EndRange = EHSymbol;429        } else {430          // II:431          StartRange = EHSymbol;432          EndRange = nullptr;433        }434 435        // Close the previous range.436        if (EndRange)437          Sites.emplace_back(438              FF.getFragmentNum(),439              CallSite{StartRange, EndRange, PreviousEH.LP, PreviousEH.Action});440 441        if (Throws) {442          // I, II:443          StartRange = EHSymbol;444          PreviousEH = EHInfo{LP, Action};445        } else {446          StartRange = nullptr;447        }448      }449    }450 451    // Check if we need to close the range.452    if (StartRange) {453      const MCSymbol *EndRange = getFunctionEndLabel(FF.getFragmentNum());454      Sites.emplace_back(455          FF.getFragmentNum(),456          CallSite{StartRange, EndRange, PreviousEH.LP, PreviousEH.Action});457    }458  }459 460  addCallSites(Sites);461}462 463const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;464 465CFIReaderWriter::CFIReaderWriter(BinaryContext &BC,466                                 const DWARFDebugFrame &EHFrame)467    : BC(BC) {468  // Prepare FDEs for fast lookup469  for (const dwarf::FrameEntry &Entry : EHFrame.entries()) {470    const auto *CurFDE = dyn_cast<dwarf::FDE>(&Entry);471    // Skip CIEs.472    if (!CurFDE)473      continue;474    // There could me multiple FDEs with the same initial address, and perhaps475    // different sizes (address ranges). Use the first entry with non-zero size.476    auto FDEI = FDEs.lower_bound(CurFDE->getInitialLocation());477    if (FDEI != FDEs.end() && FDEI->first == CurFDE->getInitialLocation()) {478      if (CurFDE->getAddressRange()) {479        if (FDEI->second->getAddressRange() == 0) {480          FDEI->second = CurFDE;481        } else if (opts::Verbosity > 0) {482          BC.errs() << "BOLT-WARNING: different FDEs for function at 0x"483                    << Twine::utohexstr(FDEI->first)484                    << " detected; sizes: " << FDEI->second->getAddressRange()485                    << " and " << CurFDE->getAddressRange() << '\n';486        }487      }488    } else {489      FDEs.emplace_hint(FDEI, CurFDE->getInitialLocation(), CurFDE);490    }491  }492}493 494bool CFIReaderWriter::fillCFIInfoFor(BinaryFunction &Function) const {495  uint64_t Address = Function.getAddress();496  auto I = FDEs.find(Address);497  // Ignore zero-length FDE ranges.498  if (I == FDEs.end() || !I->second->getAddressRange())499    return true;500 501  const FDE &CurFDE = *I->second;502  std::optional<uint64_t> LSDA = CurFDE.getLSDAAddress();503  Function.setLSDAAddress(LSDA.value_or(0));504 505  uint64_t Offset = Function.getFirstInstructionOffset();506  uint64_t CodeAlignment = CurFDE.getLinkedCIE()->getCodeAlignmentFactor();507  uint64_t DataAlignment = CurFDE.getLinkedCIE()->getDataAlignmentFactor();508  if (CurFDE.getLinkedCIE()->getPersonalityAddress()) {509    Function.setPersonalityFunction(510        *CurFDE.getLinkedCIE()->getPersonalityAddress());511    Function.setPersonalityEncoding(512        *CurFDE.getLinkedCIE()->getPersonalityEncoding());513  }514 515  auto decodeFrameInstruction = [this, &Function, &Offset, Address,516                                 CodeAlignment, DataAlignment](517                                    const CFIProgram::Instruction &Instr) {518    uint8_t Opcode = Instr.Opcode;519    if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK)520      Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK;521    switch (Instr.Opcode) {522    case DW_CFA_nop:523      break;524    case DW_CFA_advance_loc4:525    case DW_CFA_advance_loc2:526    case DW_CFA_advance_loc1:527    case DW_CFA_advance_loc:528      // Advance our current address529      Offset += CodeAlignment * int64_t(Instr.Ops[0]);530      break;531    case DW_CFA_offset_extended_sf:532      Function.addCFIInstruction(533          Offset,534          MCCFIInstruction::createOffset(535              nullptr, Instr.Ops[0], DataAlignment * int64_t(Instr.Ops[1])));536      break;537    case DW_CFA_offset_extended:538    case DW_CFA_offset:539      Function.addCFIInstruction(540          Offset, MCCFIInstruction::createOffset(nullptr, Instr.Ops[0],541                                                 DataAlignment * Instr.Ops[1]));542      break;543    case DW_CFA_restore_extended:544    case DW_CFA_restore:545      Function.addCFIInstruction(546          Offset, MCCFIInstruction::createRestore(nullptr, Instr.Ops[0]));547      break;548    case DW_CFA_set_loc:549      assert(Instr.Ops[0] >= Address && "set_loc out of function bounds");550      assert(Instr.Ops[0] <= Address + Function.getSize() &&551             "set_loc out of function bounds");552      Offset = Instr.Ops[0] - Address;553      break;554 555    case DW_CFA_undefined:556      Function.addCFIInstruction(557          Offset, MCCFIInstruction::createUndefined(nullptr, Instr.Ops[0]));558      break;559    case DW_CFA_same_value:560      Function.addCFIInstruction(561          Offset, MCCFIInstruction::createSameValue(nullptr, Instr.Ops[0]));562      break;563    case DW_CFA_register:564      Function.addCFIInstruction(565          Offset, MCCFIInstruction::createRegister(nullptr, Instr.Ops[0],566                                                   Instr.Ops[1]));567      break;568    case DW_CFA_remember_state:569      Function.addCFIInstruction(570          Offset, MCCFIInstruction::createRememberState(nullptr));571 572      if (Function.getBinaryContext().isAArch64()) {573        // Support for pointer authentication:574        // We need to annotate instructions that modify the RA State, to work575        // out the state of each instruction in MarkRAStates Pass.576        if (Offset != 0)577          Function.setInstModifiesRAState(DW_CFA_remember_state, Offset);578      }579      break;580    case DW_CFA_restore_state:581      Function.addCFIInstruction(Offset,582                                 MCCFIInstruction::createRestoreState(nullptr));583      if (Function.getBinaryContext().isAArch64()) {584        // Support for pointer authentication:585        // We need to annotate instructions that modify the RA State, to work586        // out the state of each instruction in MarkRAStates Pass.587        if (Offset != 0)588          Function.setInstModifiesRAState(DW_CFA_restore_state, Offset);589      }590      break;591    case DW_CFA_def_cfa:592      Function.addCFIInstruction(593          Offset,594          MCCFIInstruction::cfiDefCfa(nullptr, Instr.Ops[0], Instr.Ops[1]));595      break;596    case DW_CFA_def_cfa_sf:597      Function.addCFIInstruction(598          Offset,599          MCCFIInstruction::cfiDefCfa(nullptr, Instr.Ops[0],600                                      DataAlignment * int64_t(Instr.Ops[1])));601      break;602    case DW_CFA_def_cfa_register:603      Function.addCFIInstruction(Offset, MCCFIInstruction::createDefCfaRegister(604                                             nullptr, Instr.Ops[0]));605      break;606    case DW_CFA_def_cfa_offset:607      Function.addCFIInstruction(608          Offset, MCCFIInstruction::cfiDefCfaOffset(nullptr, Instr.Ops[0]));609      break;610    case DW_CFA_def_cfa_offset_sf:611      Function.addCFIInstruction(612          Offset, MCCFIInstruction::cfiDefCfaOffset(613                      nullptr, DataAlignment * int64_t(Instr.Ops[0])));614      break;615    case DW_CFA_GNU_args_size:616      Function.addCFIInstruction(617          Offset, MCCFIInstruction::createGnuArgsSize(nullptr, Instr.Ops[0]));618      Function.setUsesGnuArgsSize();619      break;620    case DW_CFA_val_offset_sf:621    case DW_CFA_val_offset:622      if (opts::Verbosity >= 1) {623        BC.errs() << "BOLT-WARNING: DWARF val_offset() unimplemented\n";624      }625      return false;626    case DW_CFA_def_cfa_expression:627    case DW_CFA_val_expression:628    case DW_CFA_expression: {629      StringRef ExprBytes = Instr.Expression->getData();630      std::string Str;631      raw_string_ostream OS(Str);632      // Manually encode this instruction using CFI escape633      OS << Opcode;634      if (Opcode != DW_CFA_def_cfa_expression)635        encodeULEB128(Instr.Ops[0], OS);636      encodeULEB128(ExprBytes.size(), OS);637      OS << ExprBytes;638      Function.addCFIInstruction(639          Offset, MCCFIInstruction::createEscape(nullptr, OS.str()));640      break;641    }642    case DW_CFA_MIPS_advance_loc8:643      if (opts::Verbosity >= 1)644        BC.errs() << "BOLT-WARNING: DW_CFA_MIPS_advance_loc unimplemented\n";645      return false;646    case DW_CFA_GNU_window_save:647      // DW_CFA_GNU_window_save and DW_CFA_AARCH64_negate_ra_state just use the648      // same id but mean different things. The latter is used in AArch64.649      if (Function.getBinaryContext().isAArch64()) {650        Function.setContainedNegateRAState();651        // The location OpNegateRAState CFIs are needed depends on the order of652        // BasicBlocks, which changes during optimizations. Instead of adding653        // OpNegateRAState CFIs, an annotation is added to the instruction, to654        // mark that the instruction modifies the RA State. The actual state for655        // instructions are worked out in MarkRAStates based on these656        // annotations.657        if (Offset != 0)658          Function.setInstModifiesRAState(DW_CFA_AARCH64_negate_ra_state,659                                          Offset);660        else661          // We cannot Annotate an instruction at Offset == 0.662          // Instead, we save the initial (Signed) state, and push it to663          // MarkRAStates' RAStateStack.664          Function.setInitialRAState(true);665        break;666      }667      if (opts::Verbosity >= 1)668        BC.errs() << "BOLT-WARNING: DW_CFA_GNU_window_save unimplemented\n";669      return false;670    case DW_CFA_lo_user:671    case DW_CFA_hi_user:672      if (opts::Verbosity >= 1)673        BC.errs() << "BOLT-WARNING: DW_CFA_*_user unimplemented\n";674      return false;675    default:676      if (opts::Verbosity >= 1)677        BC.errs() << "BOLT-WARNING: Unrecognized CFI instruction: "678                  << Instr.Opcode << '\n';679      return false;680    }681 682    return true;683  };684 685  for (const CFIProgram::Instruction &Instr : CurFDE.getLinkedCIE()->cfis())686    if (!decodeFrameInstruction(Instr))687      return false;688 689  for (const CFIProgram::Instruction &Instr : CurFDE.cfis())690    if (!decodeFrameInstruction(Instr))691      return false;692 693  return true;694}695 696std::vector<char>697CFIReaderWriter::generateEHFrameHeader(const DWARFDebugFrame &OldEHFrame,698                                       const DWARFDebugFrame &NewEHFrame,699                                       uint64_t EHFrameHeaderAddress) const {700  // Common PC -> FDE map to be written into .eh_frame_hdr.701  std::map<uint64_t, uint64_t> PCToFDE;702 703  // Initialize PCToFDE using NewEHFrame.704  for (dwarf::FrameEntry &Entry : NewEHFrame.entries()) {705    const dwarf::FDE *FDE = dyn_cast<dwarf::FDE>(&Entry);706    if (FDE == nullptr)707      continue;708    const uint64_t FuncAddress = FDE->getInitialLocation();709    const uint64_t FDEAddress =710        NewEHFrame.getEHFrameAddress() + FDE->getOffset();711 712    // Ignore unused FDEs.713    if (FuncAddress == 0)714      continue;715 716    // Add the address to the map unless we failed to write it.717    PCToFDE[FuncAddress] = FDEAddress;718  };719 720  LLVM_DEBUG(dbgs() << "BOLT-DEBUG: new .eh_frame contains "721                    << llvm::size(NewEHFrame.entries()) << " entries\n");722 723  // Add entries from the original .eh_frame corresponding to the functions724  // that we did not update.725  for (const dwarf::FrameEntry &Entry : OldEHFrame) {726    const dwarf::FDE *FDE = dyn_cast<dwarf::FDE>(&Entry);727    if (FDE == nullptr)728      continue;729    const uint64_t FuncAddress = FDE->getInitialLocation();730    const uint64_t FDEAddress =731        OldEHFrame.getEHFrameAddress() + FDE->getOffset();732 733    // Add the address if we failed to write it.734    if (PCToFDE.count(FuncAddress) == 0) {735      LLVM_DEBUG(dbgs() << "BOLT-DEBUG: old FDE for function at 0x"736                        << Twine::utohexstr(FuncAddress) << " is at 0x"737                        << Twine::utohexstr(FDEAddress) << '\n');738      PCToFDE[FuncAddress] = FDEAddress;739    }740  };741 742  LLVM_DEBUG(dbgs() << "BOLT-DEBUG: old .eh_frame contains "743                    << llvm::size(OldEHFrame.entries()) << " entries\n");744 745  // Generate a new .eh_frame_hdr based on the new map.746 747  // Header plus table of entries of size 8 bytes.748  std::vector<char> EHFrameHeader(12 + PCToFDE.size() * 8);749 750  // Version is 1.751  EHFrameHeader[0] = 1;752  // Encoding of the eh_frame pointer.753  EHFrameHeader[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;754  // Encoding of the count field to follow.755  EHFrameHeader[2] = DW_EH_PE_udata4;756  // Encoding of the table entries - 4-byte offset from the start of the header.757  EHFrameHeader[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;758 759  // Address of eh_frame. Use the new one.760  support::ulittle32_t::ref(EHFrameHeader.data() + 4) =761      NewEHFrame.getEHFrameAddress() - (EHFrameHeaderAddress + 4);762 763  // Number of entries in the table (FDE count).764  support::ulittle32_t::ref(EHFrameHeader.data() + 8) = PCToFDE.size();765 766  // Write the table at offset 12.767  char *Ptr = EHFrameHeader.data();768  uint32_t Offset = 12;769  for (const auto &PCI : PCToFDE) {770    int64_t InitialPCOffset = PCI.first - EHFrameHeaderAddress;771    assert(isInt<32>(InitialPCOffset) && "PC offset out of bounds");772    support::ulittle32_t::ref(Ptr + Offset) = InitialPCOffset;773    Offset += 4;774    int64_t FDEOffset = PCI.second - EHFrameHeaderAddress;775    assert(isInt<32>(FDEOffset) && "FDE offset out of bounds");776    support::ulittle32_t::ref(Ptr + Offset) = FDEOffset;777    Offset += 4;778  }779 780  return EHFrameHeader;781}782 783Error EHFrameParser::parseCIE(uint64_t StartOffset) {784  uint8_t Version = Data.getU8(&Offset);785  const char *Augmentation = Data.getCStr(&Offset);786  StringRef AugmentationString(Augmentation ? Augmentation : "");787  uint8_t AddressSize =788      Version < 4 ? Data.getAddressSize() : Data.getU8(&Offset);789  Data.setAddressSize(AddressSize);790  // Skip segment descriptor size791  if (Version >= 4)792    Offset += 1;793  // Skip code alignment factor794  Data.getULEB128(&Offset);795  // Skip data alignment796  Data.getSLEB128(&Offset);797  // Skip return address register798  if (Version == 1)799    Offset += 1;800  else801    Data.getULEB128(&Offset);802 803  uint32_t FDEPointerEncoding = DW_EH_PE_absptr;804  uint32_t LSDAPointerEncoding = DW_EH_PE_omit;805  // Walk the augmentation string to get all the augmentation data.806  for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) {807    switch (AugmentationString[i]) {808    default:809      return createStringError(810          errc::invalid_argument,811          "unknown augmentation character in entry at 0x%" PRIx64, StartOffset);812    case 'L':813      LSDAPointerEncoding = Data.getU8(&Offset);814      break;815    case 'P': {816      uint32_t PersonalityEncoding = Data.getU8(&Offset);817      std::optional<uint64_t> Personality =818          Data.getEncodedPointer(&Offset, PersonalityEncoding,819                                 EHFrameAddress ? EHFrameAddress + Offset : 0);820      // Patch personality address821      if (Personality)822        PatcherCallback(*Personality, Offset, PersonalityEncoding);823      break;824    }825    case 'R':826      FDEPointerEncoding = Data.getU8(&Offset);827      break;828    case 'z':829      if (i)830        return createStringError(831            errc::invalid_argument,832            "'z' must be the first character at 0x%" PRIx64, StartOffset);833      // Skip augmentation length834      Data.getULEB128(&Offset);835      break;836    case 'S':837    case 'B':838      break;839    }840  }841  Entries.emplace_back(std::make_unique<CIEInfo>(842      FDEPointerEncoding, LSDAPointerEncoding, AugmentationString));843  CIEs[StartOffset] = &*Entries.back();844  return Error::success();845}846 847Error EHFrameParser::parseFDE(uint64_t CIEPointer,848                              uint64_t StartStructureOffset) {849  std::optional<uint64_t> LSDAAddress;850  CIEInfo *Cie = CIEs[StartStructureOffset - CIEPointer];851 852  // The address size is encoded in the CIE we reference.853  if (!Cie)854    return createStringError(errc::invalid_argument,855                             "parsing FDE data at 0x%" PRIx64856                             " failed due to missing CIE",857                             StartStructureOffset);858  // Patch initial location859  if (auto Val = Data.getEncodedPointer(&Offset, Cie->FDEPtrEncoding,860                                        EHFrameAddress + Offset)) {861    PatcherCallback(*Val, Offset, Cie->FDEPtrEncoding);862  }863  // Skip address range864  Data.getEncodedPointer(&Offset, Cie->FDEPtrEncoding, 0);865 866  // Process augmentation data for this FDE.867  StringRef AugmentationString = Cie->AugmentationString;868  if (!AugmentationString.empty() && Cie->LSDAPtrEncoding != DW_EH_PE_omit) {869    // Skip augmentation length870    Data.getULEB128(&Offset);871    LSDAAddress =872        Data.getEncodedPointer(&Offset, Cie->LSDAPtrEncoding,873                               EHFrameAddress ? Offset + EHFrameAddress : 0);874    // Patch LSDA address875    PatcherCallback(*LSDAAddress, Offset, Cie->LSDAPtrEncoding);876  }877  return Error::success();878}879 880Error EHFrameParser::parse() {881  while (Data.isValidOffset(Offset)) {882    const uint64_t StartOffset = Offset;883 884    uint64_t Length;885    DwarfFormat Format;886    std::tie(Length, Format) = Data.getInitialLength(&Offset);887 888    // If the Length is 0, then this CIE is a terminator889    if (Length == 0)890      break;891 892    const uint64_t StartStructureOffset = Offset;893    const uint64_t EndStructureOffset = Offset + Length;894 895    Error Err = Error::success();896    const uint64_t Id = Data.getRelocatedValue(4, &Offset,897                                               /*SectionIndex=*/nullptr, &Err);898    if (Err)899      return Err;900 901    if (!Id) {902      if (Error Err = parseCIE(StartOffset))903        return Err;904    } else {905      if (Error Err = parseFDE(Id, StartStructureOffset))906        return Err;907    }908    Offset = EndStructureOffset;909  }910 911  return Error::success();912}913 914Error EHFrameParser::parse(DWARFDataExtractor Data, uint64_t EHFrameAddress,915                           PatcherCallbackTy PatcherCallback) {916  EHFrameParser Parser(Data, EHFrameAddress, PatcherCallback);917  return Parser.parse();918}919 920} // namespace bolt921} // namespace llvm922