[llvm] 76aee8a - [JITLink] Refactor EH-frame handling to support eh-frames with existing relocs.

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 6 14:30:33 PST 2019


Author: Lang Hames
Date: 2019-11-06T14:30:26-08:00
New Revision: 76aee8a389447409905c58b178b0554c9bae8a0a

URL: https://github.com/llvm/llvm-project/commit/76aee8a389447409905c58b178b0554c9bae8a0a
DIFF: https://github.com/llvm/llvm-project/commit/76aee8a389447409905c58b178b0554c9bae8a0a.diff

LOG: [JITLink] Refactor EH-frame handling to support eh-frames with existing relocs.

Some targets (E.g. MachO/arm64) use relocations to fix some CFI record fields
in the eh-frame section. When relocations are used the initial (pre-relocation)
content of the eh-frame section can no longer be interpreted by following the
eh-frame specification. This causes errors in the existing eh-frame parser.

This patch moves eh-frame handling into two LinkGraph passes that are run after
relocations have been parsed (but before they are applied). The first] pass
breaks up blocks in the eh-frame section into per-CFI-record blocks, and the
second parses blocks of (potentially multiple) CFI records and adds the
appropriate edges to any CFI fields that do not have existing relocations.
These passes can be run independently of one another. By handling eh-frame
splitting/fixing with LinkGraph passes we can both re-use existing relocations
for CFI record fields and avoid applying eh-frame fixups before parsing the
section (which would complicate the linker and require extra temporary
allocations of working memory).

Added: 
    llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_arm64_ehframe.o
    llvm/test/ExecutionEngine/JITLink/AArch64/MachO_arm64_ehframe.test

Modified: 
    llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
    llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
    llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
    llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.cpp
    llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h
    llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
    llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
index cef4e3340416..aebd55563e61 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
@@ -131,7 +131,6 @@ class Addressable {
   uint64_t IsAbsolute : 1;
 };
 
-using BlockOrdinal = unsigned;
 using SectionOrdinal = unsigned;
 
 /// An Addressable with content and edges.
@@ -140,10 +139,9 @@ class Block : public Addressable {
 
 private:
   /// Create a zero-fill defined addressable.
-  Block(Section &Parent, BlockOrdinal Ordinal, JITTargetAddress Size,
-        JITTargetAddress Address, uint64_t Alignment, uint64_t AlignmentOffset)
-      : Addressable(Address, true), Parent(Parent), Size(Size),
-        Ordinal(Ordinal) {
+  Block(Section &Parent, JITTargetAddress Size, JITTargetAddress Address,
+        uint64_t Alignment, uint64_t AlignmentOffset)
+      : Addressable(Address, true), Parent(Parent), Size(Size) {
     assert(isPowerOf2_64(Alignment) && "Alignment must be power of 2");
     assert(AlignmentOffset < Alignment &&
            "Alignment offset cannot exceed alignment");
@@ -154,10 +152,10 @@ class Block : public Addressable {
   }
 
   /// Create a defined addressable for the given content.
-  Block(Section &Parent, BlockOrdinal Ordinal, StringRef Content,
-        JITTargetAddress Address, uint64_t Alignment, uint64_t AlignmentOffset)
+  Block(Section &Parent, StringRef Content, JITTargetAddress Address,
+        uint64_t Alignment, uint64_t AlignmentOffset)
       : Addressable(Address, true), Parent(Parent), Data(Content.data()),
-        Size(Content.size()), Ordinal(Ordinal) {
+        Size(Content.size()) {
     assert(isPowerOf2_64(Alignment) && "Alignment must be power of 2");
     assert(AlignmentOffset < Alignment &&
            "Alignment offset cannot exceed alignment");
@@ -180,9 +178,6 @@ class Block : public Addressable {
   /// Return the parent section for this block.
   Section &getSection() const { return Parent; }
 
-  /// Return the ordinal for this block.
-  BlockOrdinal getOrdinal() const { return Ordinal; }
-
   /// Returns true if this is a zero-fill block.
   ///
   /// If true, getSize is callable but getContent is not (the content is
@@ -263,7 +258,6 @@ class Block : public Addressable {
   Section &Parent;
   const char *Data = nullptr;
   size_t Size = 0;
-  BlockOrdinal Ordinal = 0;
   std::vector<Edge> Edges;
 };
 
@@ -357,6 +351,7 @@ class Symbol {
                                   JITTargetAddress Size, bool IsCallable,
                                   bool IsLive) {
     assert(SymStorage && "Storage cannot be null");
+    assert(Offset < Base.getSize() && "Symbol offset is outside block");
     auto *Sym = reinterpret_cast<Symbol *>(SymStorage);
     new (Sym) Symbol(Base, Offset, StringRef(), Size, Linkage::Strong,
                      Scope::Local, IsLive, IsCallable);
@@ -368,6 +363,7 @@ class Symbol {
                                    JITTargetAddress Size, Linkage L, Scope S,
                                    bool IsLive, bool IsCallable) {
     assert(SymStorage && "Storage cannot be null");
+    assert(Offset < Base.getSize() && "Symbol offset is outside block");
     assert(!Name.empty() && "Name cannot be empty");
     auto *Sym = reinterpret_cast<Symbol *>(SymStorage);
     new (Sym) Symbol(Base, Offset, Name, Size, L, S, IsLive, IsCallable);
@@ -588,9 +584,6 @@ class Section {
   /// Return true if this section contains no symbols.
   bool symbols_empty() const { return Symbols.empty(); }
 
-  /// Returns the ordinal for the next block.
-  BlockOrdinal getNextBlockOrdinal() { return NextBlockOrdinal++; }
-
 private:
   void addSymbol(Symbol &Sym) {
     assert(!Symbols.count(&Sym) && "Symbol is already in this section");
@@ -615,7 +608,6 @@ class Section {
   StringRef Name;
   sys::Memory::ProtectionFlags Prot;
   SectionOrdinal SecOrdinal = 0;
-  BlockOrdinal NextBlockOrdinal = 0;
   BlockSet Blocks;
   SymbolSet Symbols;
 };
@@ -815,15 +807,13 @@ class LinkGraph {
   Block &createContentBlock(Section &Parent, StringRef Content,
                             uint64_t Address, uint64_t Alignment,
                             uint64_t AlignmentOffset) {
-    return createBlock(Parent, Parent.getNextBlockOrdinal(), Content, Address,
-                       Alignment, AlignmentOffset);
+    return createBlock(Parent, Content, Address, Alignment, AlignmentOffset);
   }
 
   /// Create a zero-fill block.
   Block &createZeroFillBlock(Section &Parent, uint64_t Size, uint64_t Address,
                              uint64_t Alignment, uint64_t AlignmentOffset) {
-    return createBlock(Parent, Parent.getNextBlockOrdinal(), Size, Address,
-                       Alignment, AlignmentOffset);
+    return createBlock(Parent, Size, Address, Alignment, AlignmentOffset);
   }
 
   /// Cache type for the splitBlock function.
@@ -841,11 +831,18 @@ class LinkGraph {
   /// is assumed to contain the list of Symbols pointing at B, sorted in
   /// descending order of offset.
   ///
-  /// Note: The cache is not automatically updated if new symbols are introduced
-  ///       between calls to splitBlock. Any newly introduced symbols may be
-  ///       added to the cache manually (descending offset order must be
-  ///       preserved), or the cache can be set to None and rebuilt by
-  ///       splitBlock on the next call.
+  /// Notes:
+  ///
+  /// 1. The newly introduced block will have a new ordinal which will be
+  ///    higher than any other ordinals in the section. Clients are responsible
+  ///    for re-assigning block ordinals to restore a compatible order if
+  ///    needed.
+  ///
+  /// 2. The cache is not automatically updated if new symbols are introduced
+  ///    between calls to splitBlock. Any newly introduced symbols may be
+  ///    added to the cache manually (descending offset order must be
+  ///    preserved), or the cache can be set to None and rebuilt by
+  ///    splitBlock on the next call.
   Block &splitBlock(Block &B, size_t SplitIndex,
                     SplitBlockCache *Cache = nullptr);
 
@@ -875,9 +872,8 @@ class LinkGraph {
                           uint64_t Alignment, bool IsLive) {
     auto &Sym = Symbol::constructCommon(
         Allocator.Allocate<Symbol>(),
-        createBlock(Section, Section.getNextBlockOrdinal(), Address, Size,
-                    Alignment, 0),
-        Name, Size, S, IsLive);
+        createBlock(Section, Address, Size, Alignment, 0), Name, Size, S,
+        IsLive);
     Section.addSymbol(Sym);
     return Sym;
   }
@@ -1019,6 +1015,145 @@ class LinkGraph {
   ExternalSymbolSet AbsoluteSymbols;
 };
 
+/// Enables easy lookup of blocks by addresses.
+class BlockAddressMap {
+public:
+  using AddrToBlockMap = std::map<JITTargetAddress, Block *>;
+  using const_iterator = AddrToBlockMap::const_iterator;
+
+  /// A block predicate that always adds all blocks.
+  static bool includeAllBlocks(const Block &B) { return true; }
+
+  /// A block predicate that always includes blocks with non-null addresses.
+  static bool includeNonNull(const Block &B) { return B.getAddress(); }
+
+  BlockAddressMap() = default;
+
+  /// Add a block to the map. Returns an error if the block overlaps with any
+  /// existing block.
+  template <typename PredFn = decltype(includeAllBlocks)>
+  Error addBlock(Block &B, PredFn Pred = includeAllBlocks) {
+    if (!Pred(B))
+      return Error::success();
+
+    auto I = AddrToBlock.upper_bound(B.getAddress());
+
+    // If we're not at the end of the map, check for overlap with the next
+    // element.
+    if (I != AddrToBlock.end()) {
+      if (B.getAddress() + B.getSize() > I->second->getAddress())
+        return overlapError(B, *I->second);
+    }
+
+    // If we're not at the start of the map, check for overlap with the previous
+    // element.
+    if (I != AddrToBlock.begin()) {
+      auto &PrevBlock = *std::prev(I)->second;
+      if (PrevBlock.getAddress() + PrevBlock.getSize() > B.getAddress())
+        return overlapError(B, PrevBlock);
+    }
+
+    AddrToBlock.insert(I, std::make_pair(B.getAddress(), &B));
+    return Error::success();
+  }
+
+  /// Add a block to the map without checking for overlap with existing blocks.
+  /// The client is responsible for ensuring that the block added does not
+  /// overlap with any existing block.
+  void addBlockWithoutChecking(Block &B) { AddrToBlock[B.getAddress()] = &B; }
+
+  /// Add a range of blocks to the map. Returns an error if any block in the
+  /// range overlaps with any other block in the range, or with any existing
+  /// block in the map.
+  template <typename BlockPtrRange,
+            typename PredFn = decltype(includeAllBlocks)>
+  Error addBlocks(BlockPtrRange &&Blocks, PredFn Pred = includeAllBlocks) {
+    for (auto *B : Blocks)
+      if (auto Err = addBlock(*B, Pred))
+        return Err;
+    return Error::success();
+  }
+
+  /// Add a range of blocks to the map without checking for overlap with
+  /// existing blocks. The client is responsible for ensuring that the block
+  /// added does not overlap with any existing block.
+  template <typename BlockPtrRange>
+  void addBlocksWithoutChecking(BlockPtrRange &&Blocks) {
+    for (auto *B : Blocks)
+      addBlockWithoutChecking(*B);
+  }
+
+  /// Iterates over (Address, Block*) pairs in ascending order of address.
+  const_iterator begin() const { return AddrToBlock.begin(); }
+  const_iterator end() const { return AddrToBlock.end(); }
+
+  /// Returns the block starting at the given address, or nullptr if no such
+  /// block exists.
+  Block *getBlockAt(JITTargetAddress Addr) const {
+    auto I = AddrToBlock.find(Addr);
+    if (I == AddrToBlock.end())
+      return nullptr;
+    return I->second;
+  }
+
+  /// Returns the block covering the given address, or nullptr if no such block
+  /// exists.
+  Block *getBlockCovering(JITTargetAddress Addr) const {
+    auto I = AddrToBlock.upper_bound(Addr);
+    if (I == AddrToBlock.begin())
+      return nullptr;
+    auto *B = std::prev(I)->second;
+    if (Addr < B->getAddress() + B->getSize())
+      return B;
+    return nullptr;
+  }
+
+private:
+  Error overlapError(Block &NewBlock, Block &ExistingBlock) {
+    auto NewBlockEnd = NewBlock.getAddress() + NewBlock.getSize();
+    auto ExistingBlockEnd =
+        ExistingBlock.getAddress() + ExistingBlock.getSize();
+    return make_error<JITLinkError>(
+        "Block at " +
+        formatv("{0:x16} -- {1:x16}", NewBlock.getAddress(), NewBlockEnd) +
+        " overlaps " +
+        formatv("{0:x16} -- {1:x16}", ExistingBlock.getAddress(),
+                ExistingBlockEnd));
+  }
+
+  AddrToBlockMap AddrToBlock;
+};
+
+/// A map of addresses to Symbols.
+class SymbolAddressMap {
+public:
+  using SymbolVector = SmallVector<Symbol *, 1>;
+
+  /// Add a symbol to the SymbolAddressMap.
+  void addSymbol(Symbol &Sym) {
+    AddrToSymbols[Sym.getAddress()].push_back(&Sym);
+  }
+
+  /// Add all symbols in a given range to the SymbolAddressMap.
+  template <typename SymbolPtrCollection>
+  void addSymbols(SymbolPtrCollection &&Symbols) {
+    for (auto *Sym : Symbols)
+      addSymbol(*Sym);
+  }
+
+  /// Returns the list of symbols that start at the given address, or nullptr if
+  /// no such symbols exist.
+  const SymbolVector *getSymbolsAt(JITTargetAddress Addr) const {
+    auto I = AddrToSymbols.find(Addr);
+    if (I == AddrToSymbols.end())
+      return nullptr;
+    return &I->second;
+  }
+
+private:
+  std::map<JITTargetAddress, SymbolVector> AddrToSymbols;
+};
+
 /// A function for mutating LinkGraphs.
 using LinkGraphPassFunction = std::function<Error(LinkGraph &)>;
 

diff  --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
index f80b0e7f8909..f1114e92c360 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp
@@ -17,174 +17,281 @@
 namespace llvm {
 namespace jitlink {
 
-EHFrameBinaryParser::EHFrameBinaryParser(JITTargetAddress EHFrameAddress,
-                                         StringRef EHFrameContent,
-                                         unsigned PointerSize,
-                                         support::endianness Endianness)
-    : EHFrameAddress(EHFrameAddress), EHFrameContent(EHFrameContent),
-      PointerSize(PointerSize), EHFrameReader(EHFrameContent, Endianness) {}
+EHFrameSplitter::EHFrameSplitter(StringRef EHFrameSectionName)
+    : EHFrameSectionName(EHFrameSectionName) {}
 
-Error EHFrameBinaryParser::addToGraph() {
-  while (!EHFrameReader.empty()) {
-    size_t RecordOffset = EHFrameReader.getOffset();
+Error EHFrameSplitter::operator()(LinkGraph &G) {
+  auto *EHFrame = G.findSectionByName(EHFrameSectionName);
 
+  if (!EHFrame) {
     LLVM_DEBUG({
-      dbgs() << "Processing eh-frame record at "
-             << format("0x%016" PRIx64, EHFrameAddress + RecordOffset)
-             << " (offset " << RecordOffset << ")\n";
+      dbgs() << "EHFrameSplitter: No " << EHFrameSectionName
+             << " section. Nothing to do\n";
     });
+    return Error::success();
+  }
 
-    size_t RecordLength = 0;
-    uint32_t RecordLengthField;
-    if (auto Err = EHFrameReader.readInteger(RecordLengthField))
+  LLVM_DEBUG({
+    dbgs() << "EHFrameSplitter: Processing " << EHFrameSectionName << "...\n";
+  });
+
+  DenseMap<Block *, LinkGraph::SplitBlockCache> Caches;
+
+  {
+    // Pre-build the split caches.
+    for (auto *B : EHFrame->blocks())
+      Caches[B] = LinkGraph::SplitBlockCache::value_type();
+    for (auto *Sym : EHFrame->symbols())
+      Caches[&Sym->getBlock()]->push_back(Sym);
+    for (auto *B : EHFrame->blocks())
+      llvm::sort(*Caches[B], [](const Symbol *LHS, const Symbol *RHS) {
+        return LHS->getOffset() > RHS->getOffset();
+      });
+  }
+
+  // Iterate over blocks (we do this by iterating over Caches entries rather
+  // than EHFrame->blocks() as we will be inserting new blocks along the way,
+  // which would invalidate iterators in the latter sequence.
+  for (auto &KV : Caches) {
+    auto &B = *KV.first;
+    auto &BCache = KV.second;
+    if (auto Err = processBlock(G, B, BCache))
       return Err;
+  }
 
-    // Process CIE/FDE length/extended-length fields to build the blocks.
-    //
-    // The value of these fields describe the length of the *rest* of the CIE
-    // (not including data up to the end of the field itself) so we have to
-    // bump RecordLength to include the data up to the end of the field: 4 bytes
-    // for Length, or 12 bytes (4 bytes + 8 bytes) for ExtendedLength.
-    if (RecordLengthField == 0) // Length 0 means end of __eh_frame section.
-      break;
+  return Error::success();
+}
 
-    // If the regular length field's value is 0xffffffff, use extended length.
-    if (RecordLengthField == 0xffffffff) {
-      uint64_t ExtendedLengthField;
-      if (auto Err = EHFrameReader.readInteger(ExtendedLengthField))
-        return Err;
-      if (ExtendedLengthField > EHFrameReader.bytesRemaining())
-        return make_error<JITLinkError>("CIE record extends past the end of "
-                                        "the __eh_frame section");
-      if (ExtendedLengthField + 12 > std::numeric_limits<size_t>::max())
-        return make_error<JITLinkError>("CIE record too large to process");
-      RecordLength = ExtendedLengthField + 12;
-    } else {
-      if (RecordLengthField > EHFrameReader.bytesRemaining())
-        return make_error<JITLinkError>("CIE record extends past the end of "
-                                        "the __eh_frame section");
-      RecordLength = RecordLengthField + 4;
-    }
+Error EHFrameSplitter::processBlock(LinkGraph &G, Block &B,
+                                    LinkGraph::SplitBlockCache &Cache) {
+  LLVM_DEBUG({
+    dbgs() << "  Processing block at " << formatv("{0:x16}", B.getAddress())
+           << "\n";
+  });
 
-    LLVM_DEBUG(dbgs() << "  length: " << RecordLength << "\n");
+  // eh-frame should not contain zero-fill blocks.
+  if (B.isZeroFill())
+    return make_error<JITLinkError>("Unexpected zero-fill block in " +
+                                    EHFrameSectionName + " section");
 
-    // Read the CIE Pointer.
-    size_t CIEPointerAddress = EHFrameAddress + EHFrameReader.getOffset();
-    uint32_t CIEPointer;
-    if (auto Err = EHFrameReader.readInteger(CIEPointer))
-      return Err;
+  if (B.getSize() == 0) {
+    LLVM_DEBUG(dbgs() << "    Block is empty. Skipping.\n");
+    return Error::success();
+  }
+
+  BinaryStreamReader BlockReader(B.getContent(), G.getEndianness());
 
-    // Based on the CIE pointer value, parse this as a CIE or FDE record.
-    if (CIEPointer == 0) {
-      if (auto Err = processCIE(RecordOffset, RecordLength))
+  while (true) {
+    uint64_t RecordStartOffset = BlockReader.getOffset();
+
+    LLVM_DEBUG({
+      dbgs() << "    Processing CFI record at "
+             << formatv("{0:x16}", B.getAddress()) << "\n";
+    });
+
+    uint32_t Length;
+    if (auto Err = BlockReader.readInteger(Length))
+      return Err;
+    if (Length != 0xffffffff) {
+      if (auto Err = BlockReader.skip(Length))
         return Err;
     } else {
-      if (auto Err = processFDE(RecordOffset, RecordLength, CIEPointerAddress,
-                                CIEPointer))
+      uint64_t ExtendedLength;
+      if (auto Err = BlockReader.readInteger(ExtendedLength))
+        return Err;
+      if (auto Err = BlockReader.skip(ExtendedLength))
         return Err;
     }
 
-    EHFrameReader.setOffset(RecordOffset + RecordLength);
+    // If this was the last block then there's nothing to split
+    if (BlockReader.empty()) {
+      LLVM_DEBUG(dbgs() << "      Extracted " << B << "\n");
+      return Error::success();
+    }
+
+    uint64_t BlockSize = BlockReader.getOffset() - RecordStartOffset;
+    auto &NewBlock = G.splitBlock(B, BlockSize);
+    (void)NewBlock;
+    LLVM_DEBUG(dbgs() << "      Extracted " << NewBlock << "\n");
+  }
+}
+
+EHFrameEdgeFixer::EHFrameEdgeFixer(StringRef EHFrameSectionName,
+                                   Edge::Kind FDEToCIE, Edge::Kind FDEToPCBegin,
+                                   Edge::Kind FDEToLSDA)
+    : EHFrameSectionName(EHFrameSectionName), FDEToCIE(FDEToCIE),
+      FDEToPCBegin(FDEToPCBegin), FDEToLSDA(FDEToLSDA) {}
+
+Error EHFrameEdgeFixer::operator()(LinkGraph &G) {
+  auto *EHFrame = G.findSectionByName(EHFrameSectionName);
+
+  if (!EHFrame) {
+    LLVM_DEBUG({
+      dbgs() << "EHFrameEdgeFixer: No " << EHFrameSectionName
+             << " section. Nothing to do\n";
+    });
+    return Error::success();
+  }
+
+  LLVM_DEBUG({
+    dbgs() << "EHFrameEdgeFixer: Processing " << EHFrameSectionName << "...\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()) {
+    PC.AddrToSyms.addSymbols(Sec.symbols());
+    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;
+  for (auto *B : EHFrame->blocks())
+    EHFrameBlocks.push_back(B);
+  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();
 }
 
-void EHFrameBinaryParser::anchor() {}
+Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
 
-Expected<EHFrameBinaryParser::AugmentationInfo>
-EHFrameBinaryParser::parseAugmentationString() {
-  AugmentationInfo AugInfo;
-  uint8_t NextChar;
-  uint8_t *NextField = &AugInfo.Fields[0];
+  LLVM_DEBUG({
+    dbgs() << "  Processing block at " << formatv("{0:x16}", B.getAddress())
+           << "\n";
+  });
 
-  if (auto Err = EHFrameReader.readInteger(NextChar))
-    return std::move(Err);
+  // eh-frame should not contain zero-fill blocks.
+  if (B.isZeroFill())
+    return make_error<JITLinkError>("Unexpected zero-fill block in " +
+                                    EHFrameSectionName + " section");
 
-  while (NextChar != 0) {
-    switch (NextChar) {
-    case 'z':
-      AugInfo.AugmentationDataPresent = true;
-      break;
-    case 'e':
-      if (auto Err = EHFrameReader.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 (B.getSize() == 0) {
+    LLVM_DEBUG(dbgs() << "    Block is empty. Skipping.\n");
+    return Error::success();
+  }
+
+  // Find the offsets of any existing edges from this block.
+  BlockEdgeMap BlockEdges;
+  for (auto &E : B.edges())
+    if (E.isRelocation()) {
+      if (BlockEdges.count(E.getOffset()))
+        return make_error<JITLinkError>(
+            "Multiple relocations at offset " +
+            formatv("{0:x16}", E.getOffset()) + " in " + EHFrameSectionName +
+            " block at address " + formatv("{0:x16}", B.getAddress()));
+
+      BlockEdges[E.getOffset()] = EdgeTarget(E);
     }
 
-    if (auto Err = EHFrameReader.readInteger(NextChar))
-      return std::move(Err);
-  }
+  CIEInfosMap CIEInfos;
+  BinaryStreamReader BlockReader(B.getContent(), PC.G.getEndianness());
+  while (!BlockReader.empty()) {
+    size_t RecordStartOffset = BlockReader.getOffset();
 
-  return std::move(AugInfo);
-}
+    LLVM_DEBUG({
+      dbgs() << "    Processing CFI record at "
+             << formatv("{0:x16}", B.getAddress() + RecordStartOffset) << "\n";
+    });
 
-Expected<JITTargetAddress> EHFrameBinaryParser::readAbsolutePointer() {
-  static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t),
-                "Result must be able to hold a uint64_t");
-  JITTargetAddress Addr;
-  if (PointerSize == 8) {
-    if (auto Err = EHFrameReader.readInteger(Addr))
-      return std::move(Err);
-  } else if (PointerSize == 4) {
-    uint32_t Addr32;
-    if (auto Err = EHFrameReader.readInteger(Addr32))
-      return std::move(Err);
-    Addr = Addr32;
-  } else
-    llvm_unreachable("Pointer size is not 32-bit or 64-bit");
-  return Addr;
+    // Get the record length.
+    size_t RecordRemaining;
+    {
+      uint32_t Length;
+      if (auto Err = BlockReader.readInteger(Length))
+        return Err;
+      // If Length < 0xffffffff then use the regular length field, otherwise
+      // read the extended length field.
+      if (Length != 0xffffffff)
+        RecordRemaining = Length;
+      else {
+        uint64_t ExtendedLength;
+        if (auto Err = BlockReader.readInteger(ExtendedLength))
+          return Err;
+        RecordRemaining = ExtendedLength;
+      }
+    }
+
+    if (BlockReader.bytesRemaining() < RecordRemaining)
+      return make_error<JITLinkError>(
+          "Incomplete CFI record at " +
+          formatv("{0:x16}", B.getAddress() + RecordStartOffset));
+
+    // Read the CIE delta for this record.
+    uint64_t CIEDeltaFieldOffset = BlockReader.getOffset() - RecordStartOffset;
+    uint32_t CIEDelta;
+    if (auto Err = BlockReader.readInteger(CIEDelta))
+      return Err;
+
+    if (CIEDelta == 0) {
+      if (auto Err = processCIE(PC, B, RecordStartOffset,
+                                CIEDeltaFieldOffset + RecordRemaining,
+                                CIEDeltaFieldOffset))
+        return Err;
+    } else {
+      if (auto Err = processFDE(PC, B, RecordStartOffset,
+                                CIEDeltaFieldOffset + RecordRemaining,
+                                CIEDeltaFieldOffset, CIEDelta, BlockEdges))
+        return Err;
+    }
+
+    // Move to the next record.
+    BlockReader.setOffset(RecordStartOffset + CIEDeltaFieldOffset +
+                          RecordRemaining);
+  }
+
+  return Error::success();
 }
 
-Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
-                                      size_t RecordLength) {
-  // Use the dwarf namespace for convenient access to pointer encoding
-  // constants.
+Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
+                                   size_t RecordOffset, size_t RecordLength,
+                                   size_t CIEDeltaFieldOffset) {
   using namespace dwarf;
 
-  LLVM_DEBUG(dbgs() << "  Record is CIE\n");
+  LLVM_DEBUG(dbgs() << "      Record is CIE\n");
 
-  auto &CIESymbol =
-      createCIERecord(EHFrameAddress + RecordOffset,
-                      EHFrameContent.substr(RecordOffset, RecordLength));
+  auto RecordContent = B.getContent().substr(RecordOffset, RecordLength);
+  BinaryStreamReader RecordReader(RecordContent, 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, RecordOffset, RecordLength, false, false);
   CIEInformation CIEInfo(CIESymbol);
 
   uint8_t Version = 0;
-  if (auto Err = EHFrameReader.readInteger(Version))
+  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();
+  auto AugInfo = parseAugmentationString(RecordReader);
   if (!AugInfo)
     return AugInfo.takeError();
 
   // Skip the EH Data field if present.
   if (AugInfo->EHDataFieldPresent)
-    if (auto Err = EHFrameReader.skip(PointerSize))
+    if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
       return Err;
 
   // Read and sanity check the code alignment factor.
   {
     uint64_t CodeAlignmentFactor = 0;
-    if (auto Err = EHFrameReader.readULEB128(CodeAlignmentFactor))
+    if (auto Err = RecordReader.readULEB128(CodeAlignmentFactor))
       return Err;
     if (CodeAlignmentFactor != 1)
       return make_error<JITLinkError>("Unsupported CIE code alignment factor " +
@@ -195,7 +302,7 @@ Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
   // Read and sanity check the data alignment factor.
   {
     int64_t DataAlignmentFactor = 0;
-    if (auto Err = EHFrameReader.readSLEB128(DataAlignmentFactor))
+    if (auto Err = RecordReader.readSLEB128(DataAlignmentFactor))
       return Err;
     if (DataAlignmentFactor != -8)
       return make_error<JITLinkError>("Unsupported CIE data alignment factor " +
@@ -204,14 +311,14 @@ Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
   }
 
   // Skip the return address register field.
-  if (auto Err = EHFrameReader.skip(1))
+  if (auto Err = RecordReader.skip(1))
     return Err;
 
   uint64_t AugmentationDataLength = 0;
-  if (auto Err = EHFrameReader.readULEB128(AugmentationDataLength))
+  if (auto Err = RecordReader.readULEB128(AugmentationDataLength))
     return Err;
 
-  uint32_t AugmentationDataStartOffset = EHFrameReader.getOffset();
+  uint32_t AugmentationDataStartOffset = RecordReader.getOffset();
 
   uint8_t *NextField = &AugInfo->Fields[0];
   while (uint8_t Field = *NextField++) {
@@ -219,7 +326,7 @@ Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
     case 'L': {
       CIEInfo.FDEsHaveLSDAField = true;
       uint8_t LSDAPointerEncoding;
-      if (auto Err = EHFrameReader.readInteger(LSDAPointerEncoding))
+      if (auto Err = RecordReader.readInteger(LSDAPointerEncoding))
         return Err;
       if (LSDAPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
         return make_error<JITLinkError>(
@@ -230,7 +337,7 @@ Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
     }
     case 'P': {
       uint8_t PersonalityPointerEncoding = 0;
-      if (auto Err = EHFrameReader.readInteger(PersonalityPointerEncoding))
+      if (auto Err = RecordReader.readInteger(PersonalityPointerEncoding))
         return Err;
       if (PersonalityPointerEncoding !=
           (DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4))
@@ -240,13 +347,13 @@ Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
             formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " +
             formatv("{0:x16}", CIESymbol.getAddress()));
       uint32_t PersonalityPointerAddress;
-      if (auto Err = EHFrameReader.readInteger(PersonalityPointerAddress))
+      if (auto Err = RecordReader.readInteger(PersonalityPointerAddress))
         return Err;
       break;
     }
     case 'R': {
       uint8_t FDEPointerEncoding;
-      if (auto Err = EHFrameReader.readInteger(FDEPointerEncoding))
+      if (auto Err = RecordReader.readInteger(FDEPointerEncoding))
         return Err;
       if (FDEPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
         return make_error<JITLinkError>(
@@ -261,107 +368,265 @@ Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
     }
   }
 
-  if (EHFrameReader.getOffset() - AugmentationDataStartOffset >
+  if (RecordReader.getOffset() - AugmentationDataStartOffset >
       AugmentationDataLength)
     return make_error<JITLinkError>("Read past the end of the augmentation "
                                     "data while parsing fields");
 
-  assert(!CIEInfos.count(CIESymbol.getAddress()) &&
+  assert(!PC.CIEInfos.count(CIESymbol.getAddress()) &&
          "Multiple CIEs recorded at the same address?");
-  CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo);
+  PC.CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo);
 
   return Error::success();
 }
 
-Error EHFrameBinaryParser::processFDE(size_t RecordOffset, size_t RecordLength,
-                                      JITTargetAddress CIEPointerAddress,
-                                      uint32_t CIEPointer) {
-  LLVM_DEBUG(dbgs() << "  Record is FDE\n");
+Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
+                                   size_t RecordOffset, size_t RecordLength,
+                                   size_t CIEDeltaFieldOffset,
+                                   uint32_t CIEDelta,
+                                   BlockEdgeMap &BlockEdges) {
+  LLVM_DEBUG(dbgs() << "      Record is FDE\n");
 
-  LLVM_DEBUG({
-    dbgs() << "  CIE pointer: "
-           << format("0x%016" PRIx64, CIEPointerAddress - CIEPointer) << "\n";
-  });
-
-  auto CIEInfoItr = CIEInfos.find(CIEPointerAddress - CIEPointer);
-  if (CIEInfoItr == CIEInfos.end())
-    return make_error<JITLinkError>(
-        "FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset) +
-        " points to non-existant CIE at " +
-        formatv("{0:x16}", CIEPointerAddress - CIEPointer));
-  auto &CIEInfo = CIEInfoItr->second;
+  JITTargetAddress RecordAddress = B.getAddress() + RecordOffset;
 
-  // Read and sanity check the PC-start pointer and size.
-  JITTargetAddress PCBeginAddress = EHFrameAddress + EHFrameReader.getOffset();
+  auto RecordContent = B.getContent().substr(RecordOffset, RecordLength);
+  BinaryStreamReader RecordReader(RecordContent, PC.G.getEndianness());
 
-  auto PCBeginDelta = readAbsolutePointer();
-  if (!PCBeginDelta)
-    return PCBeginDelta.takeError();
+  // Skip past the CIE delta field: we've already read this far.
+  RecordReader.setOffset(CIEDeltaFieldOffset + 4);
 
-  JITTargetAddress PCBegin = PCBeginAddress + *PCBeginDelta;
-  LLVM_DEBUG({
-    dbgs() << "    PC begin: " << format("0x%016" PRIx64, PCBegin) << "\n";
-  });
+  auto &FDESymbol =
+      PC.G.addAnonymousSymbol(B, RecordOffset, RecordLength, false, false);
 
-  auto *TargetSymbol = getSymbolAtAddress(PCBegin);
+  CIEInformation *CIEInfo = nullptr;
 
-  if (!TargetSymbol)
-    return make_error<JITLinkError>("FDE PC-begin " +
-                                    formatv("{0:x16}", PCBegin) +
-                                    " does not point at symbol");
+  {
+    // Process the CIE pointer field.
+    auto CIEEdgeItr = BlockEdges.find(RecordOffset + CIEDeltaFieldOffset);
+    JITTargetAddress CIEAddress =
+        RecordAddress + CIEDeltaFieldOffset - CIEDelta;
+    if (CIEEdgeItr == BlockEdges.end()) {
+
+      LLVM_DEBUG({
+        dbgs() << "        Adding edge at "
+               << formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset)
+               << " to CIE at: " << formatv("{0:x16}", 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(FDEToCIE, RecordOffset + CIEDeltaFieldOffset,
+                *CIEInfo->CIESymbol, 0);
+    } else {
+      LLVM_DEBUG({
+        dbgs() << "        Already has edge at "
+               << formatv("{0:x16}", RecordAddress + CIEDeltaFieldOffset)
+               << " to CIE at " << formatv("{0:x16}", 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();
+    }
+  }
 
-  if (TargetSymbol->getAddress() != PCBegin)
-    return make_error<JITLinkError>(
-        "FDE PC-begin " + formatv("{0:x16}", PCBegin) +
-        " does not point to start of symbol at " +
-        formatv("{0:x16}", TargetSymbol->getAddress()));
+  {
+    // Process the PC-Begin field.
+    Block *PCBeginBlock = nullptr;
+    JITTargetAddress PCBeginFieldOffset = RecordReader.getOffset();
+    auto PCEdgeItr = BlockEdges.find(RecordOffset + PCBeginFieldOffset);
+    if (PCEdgeItr == BlockEdges.end()) {
+      auto PCBeginDelta = readAbsolutePointer(PC.G, RecordReader);
+      if (!PCBeginDelta)
+        return PCBeginDelta.takeError();
+      JITTargetAddress PCBegin =
+          RecordAddress + PCBeginFieldOffset + *PCBeginDelta;
+      LLVM_DEBUG({
+        dbgs() << "        Adding edge at "
+               << formatv("{0:x16}", RecordAddress + PCBeginFieldOffset)
+               << " to PC at " << formatv("{0:x16}", PCBegin) << "\n";
+      });
+      auto PCBeginSym = getOrCreateSymbol(PC, PCBegin);
+      if (!PCBeginSym)
+        return PCBeginSym.takeError();
+      B.addEdge(FDEToPCBegin, RecordOffset + PCBeginFieldOffset, *PCBeginSym,
+                0);
+      PCBeginBlock = &PCBeginSym->getBlock();
+    } else {
+      auto &EI = PCEdgeItr->second;
+      LLVM_DEBUG({
+        dbgs() << "        Already has edge at "
+               << formatv("{0:x16}", RecordAddress + PCBeginFieldOffset)
+               << " to PC at " << formatv("{0:x16}", EI.Target->getAddress());
+        if (EI.Addend)
+          dbgs() << " + " << formatv("{0:x16}", EI.Addend);
+        dbgs() << "\n";
+      });
+
+      // Make sure the existing edge points at a defined block.
+      if (!EI.Target->isDefined()) {
+        auto EdgeAddr = RecordAddress + PCBeginFieldOffset;
+        return make_error<JITLinkError>("FDE edge at " +
+                                        formatv("{0:x16}", EdgeAddr) +
+                                        " points at external block");
+      }
+      PCBeginBlock = &EI.Target->getBlock();
+      if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
+        return Err;
+    }
 
-  LLVM_DEBUG(dbgs() << "  FDE target: " << *TargetSymbol << "\n");
+    // Add a keep-alive edge from the FDE target to the FDE to ensure that the
+    // FDE is kept alive if its target is.
+    assert(PCBeginBlock && "PC-begin block not recorded");
+    PCBeginBlock->addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
+  }
 
   // Skip over the PC range size field.
-  if (auto Err = EHFrameReader.skip(PointerSize))
+  if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
     return Err;
 
-  Symbol *LSDASymbol = nullptr;
-  JITTargetAddress LSDAAddress = 0;
-  if (CIEInfo.FDEsHaveLSDAField) {
+  if (CIEInfo->FDEsHaveLSDAField) {
     uint64_t AugmentationDataSize;
-    if (auto Err = EHFrameReader.readULEB128(AugmentationDataSize))
+    if (auto Err = RecordReader.readULEB128(AugmentationDataSize))
       return Err;
-    if (AugmentationDataSize != PointerSize)
+    if (AugmentationDataSize != PC.G.getPointerSize())
       return make_error<JITLinkError>(
           "Unexpected FDE augmentation data size (expected " +
-          Twine(PointerSize) + ", got " + Twine(AugmentationDataSize) +
-          ") for FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset));
-    LSDAAddress = EHFrameAddress + EHFrameReader.getOffset();
-    auto LSDADelta = readAbsolutePointer();
-    if (!LSDADelta)
-      return LSDADelta.takeError();
+          Twine(PC.G.getPointerSize()) + ", got " +
+          Twine(AugmentationDataSize) + ") for FDE at " +
+          formatv("{0:x16}", RecordAddress));
+
+    JITTargetAddress LSDAFieldOffset = RecordReader.getOffset();
+    auto LSDAEdgeItr = BlockEdges.find(RecordOffset + LSDAFieldOffset);
+    if (LSDAEdgeItr == BlockEdges.end()) {
+      auto LSDADelta = readAbsolutePointer(PC.G, RecordReader);
+      if (!LSDADelta)
+        return LSDADelta.takeError();
+      JITTargetAddress LSDA = RecordAddress + LSDAFieldOffset + *LSDADelta;
+      auto LSDASym = getOrCreateSymbol(PC, LSDA);
+      if (!LSDASym)
+        return LSDASym.takeError();
+      LLVM_DEBUG({
+        dbgs() << "        Adding edge at "
+               << formatv("{0:x16}", RecordAddress + LSDAFieldOffset)
+               << " to LSDA at " << formatv("{0:x16}", LSDA) << "\n";
+      });
+      B.addEdge(FDEToLSDA, RecordOffset + LSDAFieldOffset, *LSDASym, 0);
+    } else {
+      LLVM_DEBUG({
+        auto &EI = LSDAEdgeItr->second;
+        dbgs() << "        Already has edge at "
+               << formatv("{0:x16}", RecordAddress + LSDAFieldOffset)
+               << " to LSDA at " << formatv("{0:x16}", EI.Target->getAddress());
+        if (EI.Addend)
+          dbgs() << " + " << formatv("{0:x16}", EI.Addend);
+        dbgs() << "\n";
+      });
+      if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
+        return Err;
+    }
+  } else {
+    LLVM_DEBUG(dbgs() << "        Record does not have LSDA field.\n");
+  }
 
-    JITTargetAddress LSDA = LSDAAddress + *LSDADelta;
+  return Error::success();
+}
 
-    LSDASymbol = getSymbolAtAddress(LSDA);
+Expected<EHFrameEdgeFixer::AugmentationInfo>
+EHFrameEdgeFixer::parseAugmentationString(BinaryStreamReader &RecordReader) {
+  AugmentationInfo AugInfo;
+  uint8_t NextChar;
+  uint8_t *NextField = &AugInfo.Fields[0];
 
-    if (!LSDASymbol)
-      return make_error<JITLinkError>("FDE LSDA " + formatv("{0:x16}", LSDA) +
-                                      " does not point at symbol");
+  if (auto Err = RecordReader.readInteger(NextChar))
+    return std::move(Err);
 
-    if (LSDASymbol->getAddress() != LSDA)
-      return make_error<JITLinkError>(
-          "FDE LSDA " + formatv("{0:x16}", LSDA) +
-          " does not point to start of symbol at " +
-          formatv("{0:x16}", LSDASymbol->getAddress()));
+  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");
+    }
 
-    LLVM_DEBUG(dbgs() << "  FDE LSDA: " << *LSDASymbol << "\n");
+    if (auto Err = RecordReader.readInteger(NextChar))
+      return std::move(Err);
   }
 
-  JITTargetAddress RecordAddress = EHFrameAddress + RecordOffset;
-  auto FDESymbol = createFDERecord(
-      RecordAddress, EHFrameContent.substr(RecordOffset, RecordLength),
-      *CIEInfo.CIESymbol, CIEPointerAddress - RecordAddress, *TargetSymbol,
-      PCBeginAddress - RecordAddress, LSDASymbol, LSDAAddress - RecordAddress);
+  return std::move(AugInfo);
+}
+
+Expected<JITTargetAddress>
+EHFrameEdgeFixer::readAbsolutePointer(LinkGraph &G,
+                                      BinaryStreamReader &RecordReader) {
+  static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t),
+                "Result must be able to hold a uint64_t");
+  JITTargetAddress Addr;
+  if (G.getPointerSize() == 8) {
+    if (auto Err = RecordReader.readInteger(Addr))
+      return std::move(Err);
+  } else if (G.getPointerSize() == 4) {
+    uint32_t Addr32;
+    if (auto Err = RecordReader.readInteger(Addr32))
+      return std::move(Err);
+    Addr = Addr32;
+  } else
+    llvm_unreachable("Pointer size is not 32-bit or 64-bit");
+  return Addr;
+}
+
+Expected<Symbol &> EHFrameEdgeFixer::getOrCreateSymbol(ParseContext &PC,
+                                                       JITTargetAddress Addr) {
+  Symbol *CanonicalSym = nullptr;
+
+  auto UpdateCanonicalSym = [&](Symbol *Sym) {
+    if (!CanonicalSym || Sym->getLinkage() < CanonicalSym->getLinkage() ||
+        Sym->getScope() < CanonicalSym->getScope() ||
+        (Sym->hasName() && !CanonicalSym->hasName()) ||
+        Sym->getName() < CanonicalSym->getName())
+      CanonicalSym = Sym;
+  };
+
+  if (auto *SymbolsAtAddr = PC.AddrToSyms.getSymbolsAt(Addr))
+    for (auto *Sym : *SymbolsAtAddr)
+      UpdateCanonicalSym(Sym);
+
+  // If we found an existing symbol at the given address then use it.
+  if (CanonicalSym)
+    return *CanonicalSym;
 
-  return FDESymbol.takeError();
+  // 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));
+
+  return PC.G.addAnonymousSymbol(*B, Addr - B->getAddress(), 0, false, false);
 }
 
 // Determine whether we can register EH tables.
@@ -444,9 +709,6 @@ Error walkAppleEHFrameSection(const char *const SectionStart,
     else
       Size += 4;
     uint32_t Offset = *reinterpret_cast<const uint32_t *>(OffsetField);
-    if (Offset != 0)
-      if (auto Err = HandleFDE(CurCFIRecord))
-        return Err;
 
     LLVM_DEBUG({
       dbgs() << "Registering eh-frame section:\n";
@@ -456,6 +718,11 @@ Error walkAppleEHFrameSection(const char *const SectionStart,
         dbgs() << format(" 0x%02" PRIx8, *(CurCFIRecord + I));
       dbgs() << " ]\n";
     });
+
+    if (Offset != 0)
+      if (auto Err = HandleFDE(CurCFIRecord))
+        return Err;
+
     CurCFIRecord += Size;
 
     Size = *reinterpret_cast<const uint32_t *>(CurCFIRecord);

diff  --git a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
index 6f9f68ad8382..a8cd32c664dc 100644
--- a/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
+++ b/llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h
@@ -21,30 +21,30 @@
 namespace llvm {
 namespace jitlink {
 
-/// A generic binary parser for eh-frame sections.
-///
-/// Adds blocks and symbols representing CIE and FDE entries to a JITLink graph.
-///
-/// This parser assumes that the user has already verified that the EH-frame's
-/// address range does not overlap any other section/symbol, so that generated
-/// CIE/FDE records do not overlap other sections/symbols.
-class EHFrameBinaryParser {
+/// A LinkGraph pass that splits blocks in an eh-frame section into sub-blocks
+/// representing individual eh-frames.
+/// EHFrameSplitter should not be run without EHFrameEdgeFixer, which is
+/// responsible for adding FDE-to-CIE edges.
+class EHFrameSplitter {
 public:
-  EHFrameBinaryParser(JITTargetAddress EHFrameAddress, StringRef EHFrameContent,
-                      unsigned PointerSize, support::endianness Endianness);
-  virtual ~EHFrameBinaryParser() {}
+  EHFrameSplitter(StringRef EHFrameSectionName);
+  Error operator()(LinkGraph &G);
 
-  Error addToGraph();
+private:
+  Error processBlock(LinkGraph &G, Block &B, LinkGraph::SplitBlockCache &Cache);
+
+  StringRef EHFrameSectionName;
+};
+
+/// A LinkGraph pass that adds missing FDE-to-CIE, FDE-to-PC and FDE-to-LSDA
+/// edges.
+class EHFrameEdgeFixer {
+public:
+  EHFrameEdgeFixer(StringRef EHFrameSectionName, Edge::Kind FDEToCIE,
+                   Edge::Kind FDEToPCBegin, Edge::Kind FDEToLSDA);
+  Error operator()(LinkGraph &G);
 
 private:
-  virtual void anchor();
-  virtual Symbol *getSymbolAtAddress(JITTargetAddress Addr) = 0;
-  virtual Symbol &createCIERecord(JITTargetAddress RecordAddr,
-                                  StringRef RecordContent) = 0;
-  virtual Expected<Symbol &>
-  createFDERecord(JITTargetAddress RecordAddr, StringRef RecordContent,
-                  Symbol &CIE, size_t CIEOffset, Symbol &Func,
-                  size_t FuncOffset, Symbol *LSDA, size_t LSDAOffset) = 0;
 
   struct AugmentationInfo {
     bool AugmentationDataPresent = false;
@@ -52,12 +52,6 @@ class EHFrameBinaryParser {
     uint8_t Fields[4] = {0x0, 0x0, 0x0, 0x0};
   };
 
-  Expected<AugmentationInfo> parseAugmentationString();
-  Expected<JITTargetAddress> readAbsolutePointer();
-  Error processCIE(size_t RecordOffset, size_t RecordLength);
-  Error processFDE(size_t RecordOffset, size_t RecordLength,
-                   JITTargetAddress CIEPointerOffset, uint32_t CIEPointer);
-
   struct CIEInformation {
     CIEInformation() = default;
     CIEInformation(Symbol &CIESymbol) : CIESymbol(&CIESymbol) {}
@@ -65,11 +59,51 @@ class EHFrameBinaryParser {
     bool FDEsHaveLSDAField = false;
   };
 
-  JITTargetAddress EHFrameAddress;
-  StringRef EHFrameContent;
-  unsigned PointerSize;
-  BinaryStreamReader EHFrameReader;
-  DenseMap<JITTargetAddress, CIEInformation> CIEInfos;
+  struct EdgeTarget {
+    EdgeTarget() = default;
+    EdgeTarget(const Edge &E) : Target(&E.getTarget()), Addend(E.getAddend()) {}
+
+    Symbol *Target = nullptr;
+    Edge::AddendT Addend = 0;
+  };
+
+  using BlockEdgeMap = DenseMap<Edge::OffsetT, EdgeTarget>;
+  using CIEInfosMap = DenseMap<JITTargetAddress, CIEInformation>;
+
+  struct ParseContext {
+    ParseContext(LinkGraph &G) : G(G) {}
+
+    Expected<CIEInformation *> findCIEInfo(JITTargetAddress Address) {
+      auto I = CIEInfos.find(Address);
+      if (I == CIEInfos.end())
+        return make_error<JITLinkError>("No CIE found at address " +
+                                        formatv("{0:x16}", Address));
+      return &I->second;
+    }
+
+    LinkGraph &G;
+    CIEInfosMap CIEInfos;
+    BlockAddressMap AddrToBlock;
+    SymbolAddressMap AddrToSyms;
+  };
+
+  Error processBlock(ParseContext &PC, Block &B);
+  Error processCIE(ParseContext &PC, Block &B, size_t RecordOffset,
+                   size_t RecordLength, size_t CIEDeltaFieldOffset);
+  Error processFDE(ParseContext &PC, Block &B, size_t RecordOffset,
+                   size_t RecordLength, size_t CIEDeltaFieldOffset,
+                   uint32_t CIEDelta, BlockEdgeMap &BlockEdges);
+
+  Expected<AugmentationInfo>
+  parseAugmentationString(BinaryStreamReader &RecordReader);
+  Expected<JITTargetAddress>
+  readAbsolutePointer(LinkGraph &G, BinaryStreamReader &RecordReader);
+  Expected<Symbol &> getOrCreateSymbol(ParseContext &PC, JITTargetAddress Addr);
+
+  StringRef EHFrameSectionName;
+  Edge::Kind FDEToCIE;
+  Edge::Kind FDEToPCBegin;
+  Edge::Kind FDEToLSDA;
 };
 
 } // end namespace jitlink

diff  --git a/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.cpp
index d4270b5aa796..9707b9624d93 100644
--- a/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.cpp
@@ -151,9 +151,12 @@ JITLinkerBase::SegmentLayoutMap JITLinkerBase::layOutBlocks() {
   for (auto &KV : Layout) {
 
     auto CompareBlocks = [](const Block *LHS, const Block *RHS) {
+      // Sort by section, address and size
       if (LHS->getSection().getOrdinal() != RHS->getSection().getOrdinal())
         return LHS->getSection().getOrdinal() < RHS->getSection().getOrdinal();
-      return LHS->getOrdinal() < RHS->getOrdinal();
+      if (LHS->getAddress() != RHS->getAddress())
+        return LHS->getAddress() < RHS->getAddress();
+      return LHS->getSize() < RHS->getSize();
     };
 
     auto &SegLists = KV.second;

diff  --git a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h
index e1123cd11048..91b1d5a22387 100644
--- a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h
+++ b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h
@@ -30,74 +30,6 @@ class MachOLinkGraphBuilder {
   Expected<std::unique_ptr<LinkGraph>> buildGraph();
 
 protected:
-  class MachOEHFrameBinaryParser : public EHFrameBinaryParser {
-  public:
-    MachOEHFrameBinaryParser(MachOLinkGraphBuilder &Builder,
-                             JITTargetAddress EHFrameAddress,
-                             StringRef EHFrameContent, Section &EHFrameSection,
-                             uint64_t CIEAlignment, uint64_t FDEAlignment,
-                             Edge::Kind FDEToCIERelocKind,
-                             Edge::Kind FDEToTargetRelocKind)
-        : EHFrameBinaryParser(EHFrameAddress, EHFrameContent,
-                              Builder.getGraph().getPointerSize(),
-                              Builder.getGraph().getEndianness()),
-          Builder(Builder), EHFrameSection(EHFrameSection),
-          CIEAlignment(CIEAlignment), FDEAlignment(FDEAlignment),
-          FDEToCIERelocKind(FDEToCIERelocKind),
-          FDEToTargetRelocKind(FDEToTargetRelocKind) {}
-
-    Symbol *getSymbolAtAddress(JITTargetAddress Address) override {
-      if (auto *Sym = Builder.getSymbolByAddress(Address))
-        if (Sym->getAddress() == Address)
-          return Sym;
-      return nullptr;
-    }
-
-    Symbol &createCIERecord(JITTargetAddress RecordAddr,
-                            StringRef RecordContent) override {
-      auto &G = Builder.getGraph();
-      auto &B = G.createContentBlock(EHFrameSection, RecordContent, RecordAddr,
-                                     CIEAlignment, 0);
-      auto &CIESymbol =
-          G.addAnonymousSymbol(B, 0, RecordContent.size(), false, false);
-      Builder.setCanonicalSymbol(CIESymbol);
-      return CIESymbol;
-    }
-
-    Expected<Symbol &> createFDERecord(JITTargetAddress RecordAddr,
-                                       StringRef RecordContent, Symbol &CIE,
-                                       size_t CIEOffset, Symbol &Func,
-                                       size_t FuncOffset, Symbol *LSDA,
-                                       size_t LSDAOffset) override {
-      auto &G = Builder.getGraph();
-      auto &B = G.createContentBlock(EHFrameSection, RecordContent, RecordAddr,
-                                     FDEAlignment, 0);
-
-      // Add edges to CIE, Func, and (conditionally) LSDA.
-      B.addEdge(FDEToCIERelocKind, CIEOffset, CIE, 0);
-      B.addEdge(FDEToTargetRelocKind, FuncOffset, Func, 0);
-
-      if (LSDA)
-        B.addEdge(FDEToTargetRelocKind, LSDAOffset, *LSDA, 0);
-
-      auto &FDESymbol =
-          G.addAnonymousSymbol(B, 0, RecordContent.size(), false, false);
-
-      // Add a keep-alive relocation from the function to the FDE to ensure it
-      // is not dead stripped.
-      Func.getBlock().addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
-
-      return FDESymbol;
-    }
-
-  private:
-    MachOLinkGraphBuilder &Builder;
-    Section &EHFrameSection;
-    uint64_t CIEAlignment;
-    uint64_t FDEAlignment;
-    Edge::Kind FDEToCIERelocKind;
-    Edge::Kind FDEToTargetRelocKind;
-  };
 
   struct NormalizedSymbol {
     friend class MachOLinkGraphBuilder;

diff  --git a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
index 945343bff89d..944767449ce2 100644
--- a/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp
@@ -27,19 +27,7 @@ class MachOLinkGraphBuilder_arm64 : public MachOLinkGraphBuilder {
 public:
   MachOLinkGraphBuilder_arm64(const object::MachOObjectFile &Obj)
       : MachOLinkGraphBuilder(Obj),
-        NumSymbols(Obj.getSymtabLoadCommand().nsyms) {
-    addCustomSectionParser(
-        "__eh_frame", [this](NormalizedSection &EHFrameSection) {
-          if (!EHFrameSection.Data)
-            return make_error<JITLinkError>(
-                "__eh_frame section is marked zero-fill");
-          return MachOEHFrameBinaryParser(
-                     *this, EHFrameSection.Address,
-                     StringRef(EHFrameSection.Data, EHFrameSection.Size),
-                     *EHFrameSection.GraphSection, 8, 4, NegDelta32, Delta64)
-              .addToGraph();
-        });
-  }
+        NumSymbols(Obj.getSymtabLoadCommand().nsyms) {}
 
 private:
   static Expected<MachOARM64RelocationKind>

diff  --git a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp
index d83787ffd598..9dbfb6556e31 100644
--- a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp
@@ -26,19 +26,7 @@ namespace {
 class MachOLinkGraphBuilder_x86_64 : public MachOLinkGraphBuilder {
 public:
   MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile &Obj)
-      : MachOLinkGraphBuilder(Obj) {
-    addCustomSectionParser(
-        "__eh_frame", [this](NormalizedSection &EHFrameSection) {
-          if (!EHFrameSection.Data)
-            return make_error<JITLinkError>(
-                "__eh_frame section is marked zero-fill");
-          return MachOEHFrameBinaryParser(
-                     *this, EHFrameSection.Address,
-                     StringRef(EHFrameSection.Data, EHFrameSection.Size),
-                     *EHFrameSection.GraphSection, 8, 4, NegDelta32, Delta64)
-              .addToGraph();
-        });
-  }
+      : MachOLinkGraphBuilder(Obj) {}
 
 private:
   static Expected<MachOX86RelocationKind>
@@ -566,6 +554,11 @@ void jitLink_MachO_x86_64(std::unique_ptr<JITLinkContext> Ctx) {
   Triple TT("x86_64-apple-macosx");
 
   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
+    // Add eh-frame passses.
+    Config.PrePrunePasses.push_back(EHFrameSplitter("__eh_frame"));
+    Config.PrePrunePasses.push_back(
+        EHFrameEdgeFixer("__eh_frame", NegDelta32, Delta64, Delta64));
+
     // Add a mark-live pass.
     if (auto MarkLive = Ctx->getMarkLivePass(TT))
       Config.PrePrunePasses.push_back(std::move(MarkLive));

diff  --git a/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_arm64_ehframe.o b/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_arm64_ehframe.o
new file mode 100644
index 000000000000..648f7b2a236b
Binary files /dev/null and b/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_arm64_ehframe.o 
diff er

diff  --git a/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_arm64_ehframe.test b/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_arm64_ehframe.test
new file mode 100644
index 000000000000..1e95b135986a
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_arm64_ehframe.test
@@ -0,0 +1,4 @@
+# RUN: llvm-jitlink -noexec %S/Inputs/MachO_arm64_ehframe.o
+#
+# Perform a no-exec link of MachO_arm64_ehframe.o and verify that it does not
+# generate any errors despite having a relocation on the pc-begin field.


        


More information about the llvm-commits mailing list