diff options
| -rw-r--r-- | llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h | 191 | ||||
| -rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp | 655 | ||||
| -rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/EHFrameSupportImpl.h | 96 | ||||
| -rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.cpp | 5 | ||||
| -rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h | 68 | ||||
| -rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/MachO_arm64.cpp | 14 | ||||
| -rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp | 19 | ||||
| -rw-r--r-- | llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_arm64_ehframe.o | bin | 0 -> 684 bytes | |||
| -rw-r--r-- | llvm/test/ExecutionEngine/JITLink/AArch64/MachO_arm64_ehframe.test | 4 |
9 files changed, 704 insertions, 348 deletions
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h index cef4e334041..aebd55563e6 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h @@ -131,7 +131,6 @@ private: 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 @@ private: } /// 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 @@ public: /// 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 @@ private: Section &Parent; const char *Data = nullptr; size_t Size = 0; - BlockOrdinal Ordinal = 0; std::vector<Edge> Edges; }; @@ -357,6 +351,7 @@ private: 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 @@ private: 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 @@ public: /// 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 @@ private: StringRef Name; sys::Memory::ProtectionFlags Prot; SectionOrdinal SecOrdinal = 0; - BlockOrdinal NextBlockOrdinal = 0; BlockSet Blocks; SymbolSet Symbols; }; @@ -815,15 +807,13 @@ public: 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 @@ public: /// 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 @@ public: 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 @@ private: 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 f80b0e7f890..f1114e92c36 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 6f9f68ad838..a8cd32c664d 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 @@ private: 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 @@ private: 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 d4270b5aa79..9707b9624d9 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 e1123cd1104..91b1d5a2238 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h +++ b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.h @@ -30,74 +30,6 @@ public: 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 945343bff89..944767449ce 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 d83787ffd59..9dbfb6556e3 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 Binary files differnew file mode 100644 index 00000000000..648f7b2a236 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_arm64_ehframe.o 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 00000000000..1e95b135986 --- /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. |

