uint64_t IsAbsolute : 1;
};
-using BlockOrdinal = unsigned;
using SectionOrdinal = unsigned;
/// An Addressable with content and edges.
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");
}
/// 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");
/// 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
Section &Parent;
const char *Data = nullptr;
size_t Size = 0;
- BlockOrdinal Ordinal = 0;
std::vector<Edge> Edges;
};
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);
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);
/// 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");
StringRef Name;
sys::Memory::ProtectionFlags Prot;
SectionOrdinal SecOrdinal = 0;
- BlockOrdinal NextBlockOrdinal = 0;
BlockSet Blocks;
SymbolSet Symbols;
};
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.
/// 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);
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;
}
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 &)>;
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 " +
// 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 " +
}
// 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++) {
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>(
}
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))
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>(
}
}
- 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.
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";
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);