//===-------- JITLink_EHFrameSupport.cpp - JITLink eh-frame utils ---------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "EHFrameSupportImpl.h"

#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Config/config.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#include "llvm/Support/DynamicLibrary.h"

#define DEBUG_TYPE "jitlink"

namespace llvm {
namespace jitlink {

EHFrameEdgeFixer::EHFrameEdgeFixer(StringRef EHFrameSectionName,
                                   unsigned PointerSize, Edge::Kind Pointer32,
                                   Edge::Kind Pointer64, Edge::Kind Delta32,
                                   Edge::Kind Delta64, Edge::Kind NegDelta32)
    : EHFrameSectionName(EHFrameSectionName), PointerSize(PointerSize),
      Pointer32(Pointer32), Pointer64(Pointer64), Delta32(Delta32),
      Delta64(Delta64), NegDelta32(NegDelta32) {}

Error EHFrameEdgeFixer::operator()(LinkGraph &G) {
  auto *EHFrame = G.findSectionByName(EHFrameSectionName);

  if (!EHFrame) {
    LLVM_DEBUG({
      dbgs() << "EHFrameEdgeFixer: No " << EHFrameSectionName
             << " section in \"" << G.getName() << "\". Nothing to do.\n";
    });
    return Error::success();
  }

  // Check that we support the graph's pointer size.
  if (G.getPointerSize() != 4 && G.getPointerSize() != 8)
    return make_error<JITLinkError>(
        "EHFrameEdgeFixer only supports 32 and 64 bit targets");

  LLVM_DEBUG({
    dbgs() << "EHFrameEdgeFixer: Processing " << EHFrameSectionName << " in \""
           << G.getName() << "\"...\n";
  });

  ParseContext PC(G);

  // Build a map of all blocks and symbols in the text sections. We will use
  // these for finding / building edge targets when processing FDEs.
  for (auto &Sec : G.sections()) {
    // Just record the most-canonical symbol (for eh-frame purposes) at each
    // address.
    for (auto *Sym : Sec.symbols()) {
      auto &CurSym = PC.AddrToSym[Sym->getAddress()];
      if (!CurSym || (std::make_tuple(Sym->getLinkage(), Sym->getScope(),
                                      !Sym->hasName(), Sym->getName()) <
                      std::make_tuple(CurSym->getLinkage(), CurSym->getScope(),
                                      !CurSym->hasName(), CurSym->getName())))
        CurSym = Sym;
    }
    if (auto Err = PC.AddrToBlock.addBlocks(Sec.blocks(),
                                            BlockAddressMap::includeNonNull))
      return Err;
  }

  // Sort eh-frame blocks into address order to ensure we visit CIEs before
  // their child FDEs.
  std::vector<Block *> EHFrameBlocks;
  llvm::append_range(EHFrameBlocks, EHFrame->blocks());
  llvm::sort(EHFrameBlocks, [](const Block *LHS, const Block *RHS) {
    return LHS->getAddress() < RHS->getAddress();
  });

  // Loop over the blocks in address order.
  for (auto *B : EHFrameBlocks)
    if (auto Err = processBlock(PC, *B))
      return Err;

  return Error::success();
}

static Expected<size_t> readCFIRecordLength(const Block &B,
                                            BinaryStreamReader &R) {
  uint32_t Length;
  if (auto Err = R.readInteger(Length))
    return std::move(Err);

  // If Length < 0xffffffff then use the regular length field, otherwise
  // read the extended length field.
  if (Length != 0xffffffff)
    return Length;

  uint64_t ExtendedLength;
  if (auto Err = R.readInteger(ExtendedLength))
    return std::move(Err);

  if (ExtendedLength > std::numeric_limits<size_t>::max())
    return make_error<JITLinkError>(
        "In CFI record at " +
        formatv("{0:x}", B.getAddress() + R.getOffset() - 12) +
        ", extended length of " + formatv("{0:x}", ExtendedLength) +
        " exceeds address-range max (" +
        formatv("{0:x}", std::numeric_limits<size_t>::max()));

  return ExtendedLength;
}

Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {

  LLVM_DEBUG(dbgs() << "  Processing block at " << B.getAddress() << "\n");

  // eh-frame should not contain zero-fill blocks.
  if (B.isZeroFill())
    return make_error<JITLinkError>("Unexpected zero-fill block in " +
                                    EHFrameSectionName + " section");

  if (B.getSize() == 0) {
    LLVM_DEBUG(dbgs() << "    Block is empty. Skipping.\n");
    return Error::success();
  }

  // Find the offsets of any existing edges from this block.
  BlockEdgesInfo BlockEdges;
  for (auto &E : B.edges())
    if (E.isRelocation()) {
      // Check if we already saw more than one relocation at this offset.
      if (BlockEdges.Multiple.contains(E.getOffset()))
        continue;

      // Otherwise check if we previously had exactly one relocation at this
      // offset. If so, we now have a second one and move it from the TargetMap
      // into the Multiple set.
      auto [It, Inserted] = BlockEdges.TargetMap.try_emplace(E.getOffset(), E);
      if (!Inserted) {
        BlockEdges.TargetMap.erase(It);
        BlockEdges.Multiple.insert(E.getOffset());
      }
    }

  BinaryStreamReader BlockReader(
      StringRef(B.getContent().data(), B.getContent().size()),
      PC.G.getEndianness());

  // Get the record length.
  Expected<size_t> RecordRemaining = readCFIRecordLength(B, BlockReader);
  if (!RecordRemaining)
    return RecordRemaining.takeError();

  // We expect DWARFRecordSectionSplitter to split each CFI record into its own
  // block.
  if (BlockReader.bytesRemaining() != *RecordRemaining)
    return make_error<JITLinkError>("Incomplete CFI record at " +
                                    formatv("{0:x16}", B.getAddress()));

  // Read the CIE delta for this record.
  uint64_t CIEDeltaFieldOffset = BlockReader.getOffset();
  uint32_t CIEDelta;
  if (auto Err = BlockReader.readInteger(CIEDelta))
    return Err;

  if (CIEDelta == 0) {
    if (auto Err = processCIE(PC, B, CIEDeltaFieldOffset, BlockEdges))
      return Err;
  } else {
    if (auto Err = processFDE(PC, B, CIEDeltaFieldOffset, CIEDelta, BlockEdges))
      return Err;
  }

  return Error::success();
}

Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
                                   size_t CIEDeltaFieldOffset,
                                   const BlockEdgesInfo &BlockEdges) {

  LLVM_DEBUG(dbgs() << "    Record is CIE\n");

  BinaryStreamReader RecordReader(
      StringRef(B.getContent().data(), B.getContent().size()),
      PC.G.getEndianness());

  // Skip past the CIE delta field: we've already processed this far.
  RecordReader.setOffset(CIEDeltaFieldOffset + 4);

  auto &CIESymbol = PC.G.addAnonymousSymbol(B, 0, B.getSize(), false, false);
  CIEInformation CIEInfo(CIESymbol);

  uint8_t Version = 0;
  if (auto Err = RecordReader.readInteger(Version))
    return Err;

  if (Version != 0x01)
    return make_error<JITLinkError>("Bad CIE version " + Twine(Version) +
                                    " (should be 0x01) in eh-frame");

  auto AugInfo = parseAugmentationString(RecordReader);
  if (!AugInfo)
    return AugInfo.takeError();

  // Skip the EH Data field if present.
  if (AugInfo->EHDataFieldPresent)
    if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
      return Err;

  // Read and validate the code alignment factor.
  {
    uint64_t CodeAlignmentFactor = 0;
    if (auto Err = RecordReader.readULEB128(CodeAlignmentFactor))
      return Err;
  }

  // Read and validate the data alignment factor.
  {
    int64_t DataAlignmentFactor = 0;
    if (auto Err = RecordReader.readSLEB128(DataAlignmentFactor))
      return Err;
  }

  // Skip the return address register field.
  if (auto Err = RecordReader.skip(1))
    return Err;

  if (AugInfo->AugmentationDataPresent) {

    CIEInfo.AugmentationDataPresent = true;

    uint64_t AugmentationDataLength = 0;
    if (auto Err = RecordReader.readULEB128(AugmentationDataLength))
      return Err;

    uint32_t AugmentationDataStartOffset = RecordReader.getOffset();

    uint8_t *NextField = &AugInfo->Fields[0];
    while (uint8_t Field = *NextField++) {
      switch (Field) {
      case 'L':
        CIEInfo.LSDAPresent = true;
        if (auto PE = readPointerEncoding(RecordReader, B, "LSDA"))
          CIEInfo.LSDAEncoding = *PE;
        else
          return PE.takeError();
        break;
      case 'P': {
        auto PersonalityPointerEncoding =
            readPointerEncoding(RecordReader, B, "personality");
        if (!PersonalityPointerEncoding)
          return PersonalityPointerEncoding.takeError();
        if (auto Err =
                getOrCreateEncodedPointerEdge(
                    PC, BlockEdges, *PersonalityPointerEncoding, RecordReader,
                    B, RecordReader.getOffset(), "personality")
                    .takeError())
          return Err;
        break;
      }
      case 'R':
        if (auto PE = readPointerEncoding(RecordReader, B, "address")) {
          CIEInfo.AddressEncoding = *PE;
          if (CIEInfo.AddressEncoding == dwarf::DW_EH_PE_omit)
            return make_error<JITLinkError>(
                "Invalid address encoding DW_EH_PE_omit in CIE at " +
                formatv("{0:x}", B.getAddress().getValue()));
        } else
          return PE.takeError();
        break;
      default:
        llvm_unreachable("Invalid augmentation string field");
      }
    }

    if (RecordReader.getOffset() - AugmentationDataStartOffset >
        AugmentationDataLength)
      return make_error<JITLinkError>("Read past the end of the augmentation "
                                      "data while parsing fields");
  }

  assert(!PC.CIEInfos.count(CIESymbol.getAddress()) &&
         "Multiple CIEs recorded at the same address?");
  PC.CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo);

  return Error::success();
}

Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
                                   size_t CIEDeltaFieldOffset,
                                   uint32_t CIEDelta,
                                   const BlockEdgesInfo &BlockEdges) {
  LLVM_DEBUG(dbgs() << "    Record is FDE\n");

  orc::ExecutorAddr RecordAddress = B.getAddress();

  BinaryStreamReader RecordReader(
      StringRef(B.getContent().data(), B.getContent().size()),
      PC.G.getEndianness());

  // Skip past the CIE delta field: we've already read this far.
  RecordReader.setOffset(CIEDeltaFieldOffset + 4);

  auto &FDESymbol = PC.G.addAnonymousSymbol(B, 0, B.getSize(), false, false);

  CIEInformation *CIEInfo = nullptr;

  {
    // Process the CIE pointer field.
    if (BlockEdges.Multiple.contains(CIEDeltaFieldOffset))
      return make_error<JITLinkError>(
          "CIE pointer field already has multiple edges at " +
          formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset));

    auto CIEEdgeItr = BlockEdges.TargetMap.find(CIEDeltaFieldOffset);

    orc::ExecutorAddr CIEAddress =
        RecordAddress + orc::ExecutorAddrDiff(CIEDeltaFieldOffset) -
        orc::ExecutorAddrDiff(CIEDelta);
    if (CIEEdgeItr == BlockEdges.TargetMap.end()) {
      LLVM_DEBUG({
        dbgs() << "        Adding edge at "
               << (RecordAddress + CIEDeltaFieldOffset)
               << " to CIE at: " << CIEAddress << "\n";
      });
      if (auto CIEInfoOrErr = PC.findCIEInfo(CIEAddress))
        CIEInfo = *CIEInfoOrErr;
      else
        return CIEInfoOrErr.takeError();
      assert(CIEInfo->CIESymbol && "CIEInfo has no CIE symbol set");
      B.addEdge(NegDelta32, CIEDeltaFieldOffset, *CIEInfo->CIESymbol, 0);
    } else {
      LLVM_DEBUG({
        dbgs() << "        Already has edge at "
               << (RecordAddress + CIEDeltaFieldOffset) << " to CIE at "
               << CIEAddress << "\n";
      });
      auto &EI = CIEEdgeItr->second;
      if (EI.Addend)
        return make_error<JITLinkError>(
            "CIE edge at " +
            formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset) +
            " has non-zero addend");
      if (auto CIEInfoOrErr = PC.findCIEInfo(EI.Target->getAddress()))
        CIEInfo = *CIEInfoOrErr;
      else
        return CIEInfoOrErr.takeError();
    }
  }

  // Process the PC-Begin field.
  LLVM_DEBUG({
    dbgs() << "      Processing PC-begin at "
           << (RecordAddress + RecordReader.getOffset()) << "\n";
  });
  if (auto PCBegin = getOrCreateEncodedPointerEdge(
          PC, BlockEdges, CIEInfo->AddressEncoding, RecordReader, B,
          RecordReader.getOffset(), "PC begin")) {
    assert(*PCBegin && "PC-begin symbol not set");
    if ((*PCBegin)->isDefined()) {
      // Add a keep-alive edge from the FDE target to the FDE to ensure that the
      // FDE is kept alive if its target is.
      LLVM_DEBUG({
        dbgs() << "        Adding keep-alive edge from target at "
               << (*PCBegin)->getBlock().getAddress() << " to FDE at "
               << RecordAddress << "\n";
      });
      (*PCBegin)->getBlock().addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
    } else {
      LLVM_DEBUG({
        dbgs() << "        WARNING: Not adding keep-alive edge to FDE at "
               << RecordAddress << ", which points to "
               << ((*PCBegin)->isExternal() ? "external" : "absolute")
               << " symbol \"" << (*PCBegin)->getName()
               << "\" -- FDE must be kept alive manually or it will be "
               << "dead stripped.\n";
      });
    }
  } else
    return PCBegin.takeError();

  // Skip over the PC range size field.
  if (auto Err = skipEncodedPointer(CIEInfo->AddressEncoding, RecordReader))
    return Err;

  if (CIEInfo->AugmentationDataPresent) {
    uint64_t AugmentationDataSize;
    if (auto Err = RecordReader.readULEB128(AugmentationDataSize))
      return Err;

    if (CIEInfo->LSDAPresent)
      if (auto Err = getOrCreateEncodedPointerEdge(
                         PC, BlockEdges, CIEInfo->LSDAEncoding, RecordReader, B,
                         RecordReader.getOffset(), "LSDA")
                         .takeError())
        return Err;
  } else {
    LLVM_DEBUG(dbgs() << "        Record does not have LSDA field.\n");
  }

  return Error::success();
}

Expected<EHFrameEdgeFixer::AugmentationInfo>
EHFrameEdgeFixer::parseAugmentationString(BinaryStreamReader &RecordReader) {
  AugmentationInfo AugInfo;
  uint8_t NextChar;
  uint8_t *NextField = &AugInfo.Fields[0];

  if (auto Err = RecordReader.readInteger(NextChar))
    return std::move(Err);

  while (NextChar != 0) {
    switch (NextChar) {
    case 'z':
      AugInfo.AugmentationDataPresent = true;
      break;
    case 'e':
      if (auto Err = RecordReader.readInteger(NextChar))
        return std::move(Err);
      if (NextChar != 'h')
        return make_error<JITLinkError>("Unrecognized substring e" +
                                        Twine(NextChar) +
                                        " in augmentation string");
      AugInfo.EHDataFieldPresent = true;
      break;
    case 'L':
    case 'P':
    case 'R':
      *NextField++ = NextChar;
      break;
    default:
      return make_error<JITLinkError>("Unrecognized character " +
                                      Twine(NextChar) +
                                      " in augmentation string");
    }

    if (auto Err = RecordReader.readInteger(NextChar))
      return std::move(Err);
  }

  return std::move(AugInfo);
}

Expected<uint8_t> EHFrameEdgeFixer::readPointerEncoding(BinaryStreamReader &R,
                                                        Block &InBlock,
                                                        const char *FieldName) {
  using namespace dwarf;

  uint8_t PointerEncoding;
  if (auto Err = R.readInteger(PointerEncoding))
    return std::move(Err);

  bool Supported = true;
  switch (PointerEncoding & 0xf) {
  case DW_EH_PE_uleb128:
  case DW_EH_PE_udata2:
  case DW_EH_PE_sleb128:
  case DW_EH_PE_sdata2:
    Supported = false;
    break;
  }
  if (Supported) {
    switch (PointerEncoding & 0x70) {
    case DW_EH_PE_textrel:
    case DW_EH_PE_datarel:
    case DW_EH_PE_funcrel:
    case DW_EH_PE_aligned:
      Supported = false;
      break;
    }
  }

  if (Supported)
    return PointerEncoding;

  return make_error<JITLinkError>("Unsupported pointer encoding " +
                                  formatv("{0:x2}", PointerEncoding) + " for " +
                                  FieldName + "in CFI record at " +
                                  formatv("{0:x16}", InBlock.getAddress()));
}

Error EHFrameEdgeFixer::skipEncodedPointer(uint8_t PointerEncoding,
                                           BinaryStreamReader &RecordReader) {
  using namespace dwarf;

  // Switch absptr to corresponding udata encoding.
  if ((PointerEncoding & 0xf) == DW_EH_PE_absptr)
    PointerEncoding |= (PointerSize == 8) ? DW_EH_PE_udata8 : DW_EH_PE_udata4;

  switch (PointerEncoding & 0xf) {
  case DW_EH_PE_udata4:
  case DW_EH_PE_sdata4:
    if (auto Err = RecordReader.skip(4))
      return Err;
    break;
  case DW_EH_PE_udata8:
  case DW_EH_PE_sdata8:
    if (auto Err = RecordReader.skip(8))
      return Err;
    break;
  default:
    llvm_unreachable("Unrecognized encoding");
  }
  return Error::success();
}

Expected<Symbol *> EHFrameEdgeFixer::getOrCreateEncodedPointerEdge(
    ParseContext &PC, const BlockEdgesInfo &BlockEdges, uint8_t PointerEncoding,
    BinaryStreamReader &RecordReader, Block &BlockToFix,
    size_t PointerFieldOffset, const char *FieldName) {
  using namespace dwarf;

  if (PointerEncoding == DW_EH_PE_omit)
    return nullptr;

  // If there's already an edge here then just skip the encoded pointer and
  // return the edge's target.
  {
    auto EdgeI = BlockEdges.TargetMap.find(PointerFieldOffset);
    if (EdgeI != BlockEdges.TargetMap.end()) {
      LLVM_DEBUG({
        dbgs() << "      Existing edge at "
               << (BlockToFix.getAddress() + PointerFieldOffset) << " to "
               << FieldName << " at " << EdgeI->second.Target->getAddress();
        if (EdgeI->second.Target->hasName())
          dbgs() << " (" << EdgeI->second.Target->getName() << ")";
        dbgs() << "\n";
      });
      if (auto Err = skipEncodedPointer(PointerEncoding, RecordReader))
        return std::move(Err);
      return EdgeI->second.Target;
    }

    if (BlockEdges.Multiple.contains(PointerFieldOffset))
      return make_error<JITLinkError>("Multiple relocations at offset " +
                                      formatv("{0:x16}", PointerFieldOffset));
  }

  // Switch absptr to corresponding udata encoding.
  if ((PointerEncoding & 0xf) == DW_EH_PE_absptr)
    PointerEncoding |= (PointerSize == 8) ? DW_EH_PE_udata8 : DW_EH_PE_udata4;

  // We need to create an edge. Start by reading the field value.
  uint64_t FieldValue;
  bool Is64Bit = false;
  switch (PointerEncoding & 0xf) {
  case DW_EH_PE_udata4: {
    uint32_t Val;
    if (auto Err = RecordReader.readInteger(Val))
      return std::move(Err);
    FieldValue = Val;
    break;
  }
  case DW_EH_PE_sdata4: {
    uint32_t Val;
    if (auto Err = RecordReader.readInteger(Val))
      return std::move(Err);
    FieldValue = Val;
    break;
  }
  case DW_EH_PE_udata8:
  case DW_EH_PE_sdata8:
    Is64Bit = true;
    if (auto Err = RecordReader.readInteger(FieldValue))
      return std::move(Err);
    break;
  default:
    llvm_unreachable("Unsupported encoding");
  }

  // Find the edge target and edge kind to use.
  orc::ExecutorAddr Target;
  Edge::Kind PtrEdgeKind = Edge::Invalid;
  if ((PointerEncoding & 0x70) == DW_EH_PE_pcrel) {
    Target = BlockToFix.getAddress() + PointerFieldOffset;
    PtrEdgeKind = Is64Bit ? Delta64 : Delta32;
  } else
    PtrEdgeKind = Is64Bit ? Pointer64 : Pointer32;
  Target += FieldValue;

  // Find or create a symbol to point the edge at.
  auto TargetSym = getOrCreateSymbol(PC, Target);
  if (!TargetSym)
    return TargetSym.takeError();
  BlockToFix.addEdge(PtrEdgeKind, PointerFieldOffset, *TargetSym, 0);

  LLVM_DEBUG({
    dbgs() << "      Adding edge at "
           << (BlockToFix.getAddress() + PointerFieldOffset) << " to "
           << FieldName << " at " << TargetSym->getAddress();
    if (TargetSym->hasName())
      dbgs() << " (" << TargetSym->getName() << ")";
    dbgs() << "\n";
  });

  return &*TargetSym;
}

Expected<Symbol &> EHFrameEdgeFixer::getOrCreateSymbol(ParseContext &PC,
                                                       orc::ExecutorAddr Addr) {
  // See whether we have a canonical symbol for the given address already.
  auto CanonicalSymI = PC.AddrToSym.find(Addr);
  if (CanonicalSymI != PC.AddrToSym.end())
    return *CanonicalSymI->second;

  // Otherwise search for a block covering the address and create a new symbol.
  auto *B = PC.AddrToBlock.getBlockCovering(Addr);
  if (!B)
    return make_error<JITLinkError>("No symbol or block covering address " +
                                    formatv("{0:x16}", Addr));

  auto &S =
      PC.G.addAnonymousSymbol(*B, Addr - B->getAddress(), 0, false, false);
  PC.AddrToSym[S.getAddress()] = &S;
  return S;
}

char EHFrameNullTerminator::NullTerminatorBlockContent[4] = {0, 0, 0, 0};

EHFrameNullTerminator::EHFrameNullTerminator(StringRef EHFrameSectionName)
    : EHFrameSectionName(EHFrameSectionName) {}

Error EHFrameNullTerminator::operator()(LinkGraph &G) {
  auto *EHFrame = G.findSectionByName(EHFrameSectionName);

  if (!EHFrame)
    return Error::success();

  LLVM_DEBUG({
    dbgs() << "EHFrameNullTerminator adding null terminator to "
           << EHFrameSectionName << "\n";
  });

  auto &NullTerminatorBlock =
      G.createContentBlock(*EHFrame, NullTerminatorBlockContent,
                           orc::ExecutorAddr(~uint64_t(4)), 1, 0);
  G.addAnonymousSymbol(NullTerminatorBlock, 0, 4, false, true);
  return Error::success();
}

EHFrameCFIBlockInspector EHFrameCFIBlockInspector::FromEdgeScan(Block &B) {
  if (B.edges_empty())
    return EHFrameCFIBlockInspector(nullptr);
  if (B.edges_size() == 1)
    return EHFrameCFIBlockInspector(&*B.edges().begin());
  SmallVector<Edge *, 3> Es(llvm::make_pointer_range(B.edges()));
  assert(Es.size() >= 2 && Es.size() <= 3 && "Unexpected number of edges");
  llvm::sort(Es, [](const Edge *LHS, const Edge *RHS) {
    return LHS->getOffset() < RHS->getOffset();
  });
  return EHFrameCFIBlockInspector(*Es[0], *Es[1],
                                  Es.size() == 3 ? Es[2] : nullptr);
  return EHFrameCFIBlockInspector(nullptr);
}

EHFrameCFIBlockInspector::EHFrameCFIBlockInspector(Edge *PersonalityEdge)
    : PersonalityEdge(PersonalityEdge) {}

EHFrameCFIBlockInspector::EHFrameCFIBlockInspector(Edge &CIEEdge,
                                                   Edge &PCBeginEdge,
                                                   Edge *LSDAEdge)
    : CIEEdge(&CIEEdge), PCBeginEdge(&PCBeginEdge), LSDAEdge(LSDAEdge) {}

Section *getEHFrameSection(LinkGraph &G) {
  const char *EHFrameSectionName = nullptr;
  switch (G.getTargetTriple().getObjectFormat()) {
  case Triple::MachO:
    EHFrameSectionName = "__TEXT,__eh_frame";
    break;
  case Triple::ELF:
    EHFrameSectionName = ".eh_frame";
    break;
  default:
    return nullptr;
  }

  if (auto *S = G.findSectionByName(EHFrameSectionName))
    if (!S->empty())
      return S;

  return nullptr;
}

} // end namespace jitlink
} // end namespace llvm
