forked from OSchip/llvm-project
[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).
This commit is contained in:
parent
8243918f43
commit
76aee8a389
|
@ -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 &)>;
|
||||
|
||||
|
|
|
@ -17,88 +17,536 @@
|
|||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
LLVM_DEBUG(dbgs() << " length: " << RecordLength << "\n");
|
||||
|
||||
// Read the CIE Pointer.
|
||||
size_t CIEPointerAddress = EHFrameAddress + EHFrameReader.getOffset();
|
||||
uint32_t CIEPointer;
|
||||
if (auto Err = EHFrameReader.readInteger(CIEPointer))
|
||||
return Err;
|
||||
|
||||
// Based on the CIE pointer value, parse this as a CIE or FDE record.
|
||||
if (CIEPointer == 0) {
|
||||
if (auto Err = processCIE(RecordOffset, RecordLength))
|
||||
return Err;
|
||||
} else {
|
||||
if (auto Err = processFDE(RecordOffset, RecordLength, CIEPointerAddress,
|
||||
CIEPointer))
|
||||
return Err;
|
||||
}
|
||||
|
||||
EHFrameReader.setOffset(RecordOffset + RecordLength);
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void EHFrameBinaryParser::anchor() {}
|
||||
Error EHFrameSplitter::processBlock(LinkGraph &G, Block &B,
|
||||
LinkGraph::SplitBlockCache &Cache) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Processing block at " << formatv("{0:x16}", B.getAddress())
|
||||
<< "\n";
|
||||
});
|
||||
|
||||
Expected<EHFrameBinaryParser::AugmentationInfo>
|
||||
EHFrameBinaryParser::parseAugmentationString() {
|
||||
// eh-frame should not contain zero-fill blocks.
|
||||
if (B.isZeroFill())
|
||||
return make_error<JITLinkError>("Unexpected zero-fill block in " +
|
||||
EHFrameSectionName + " section");
|
||||
|
||||
if (B.getSize() == 0) {
|
||||
LLVM_DEBUG(dbgs() << " Block is empty. Skipping.\n");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
BinaryStreamReader BlockReader(B.getContent(), G.getEndianness());
|
||||
|
||||
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 {
|
||||
uint64_t ExtendedLength;
|
||||
if (auto Err = BlockReader.readInteger(ExtendedLength))
|
||||
return Err;
|
||||
if (auto Err = BlockReader.skip(ExtendedLength))
|
||||
return Err;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Processing block at " << formatv("{0:x16}", B.getAddress())
|
||||
<< "\n";
|
||||
});
|
||||
|
||||
// eh-frame should not contain zero-fill blocks.
|
||||
if (B.isZeroFill())
|
||||
return make_error<JITLinkError>("Unexpected zero-fill block in " +
|
||||
EHFrameSectionName + " section");
|
||||
|
||||
if (B.getSize() == 0) {
|
||||
LLVM_DEBUG(dbgs() << " Block is empty. Skipping.\n");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
// Find the offsets of any existing edges from this block.
|
||||
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);
|
||||
}
|
||||
|
||||
CIEInfosMap CIEInfos;
|
||||
BinaryStreamReader BlockReader(B.getContent(), PC.G.getEndianness());
|
||||
while (!BlockReader.empty()) {
|
||||
size_t RecordStartOffset = BlockReader.getOffset();
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " Processing CFI record at "
|
||||
<< formatv("{0:x16}", B.getAddress() + RecordStartOffset) << "\n";
|
||||
});
|
||||
|
||||
// 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 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");
|
||||
|
||||
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 = RecordReader.readInteger(Version))
|
||||
return Err;
|
||||
|
||||
if (Version != 0x01)
|
||||
return make_error<JITLinkError>("Bad CIE version " + Twine(Version) +
|
||||
" (should be 0x01) in eh-frame");
|
||||
|
||||
auto AugInfo = parseAugmentationString(RecordReader);
|
||||
if (!AugInfo)
|
||||
return AugInfo.takeError();
|
||||
|
||||
// Skip the EH Data field if present.
|
||||
if (AugInfo->EHDataFieldPresent)
|
||||
if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
|
||||
return Err;
|
||||
|
||||
// Read and sanity check the code alignment factor.
|
||||
{
|
||||
uint64_t CodeAlignmentFactor = 0;
|
||||
if (auto Err = RecordReader.readULEB128(CodeAlignmentFactor))
|
||||
return Err;
|
||||
if (CodeAlignmentFactor != 1)
|
||||
return make_error<JITLinkError>("Unsupported CIE code alignment factor " +
|
||||
Twine(CodeAlignmentFactor) +
|
||||
" (expected 1)");
|
||||
}
|
||||
|
||||
// Read and sanity check the data alignment factor.
|
||||
{
|
||||
int64_t DataAlignmentFactor = 0;
|
||||
if (auto Err = RecordReader.readSLEB128(DataAlignmentFactor))
|
||||
return Err;
|
||||
if (DataAlignmentFactor != -8)
|
||||
return make_error<JITLinkError>("Unsupported CIE data alignment factor " +
|
||||
Twine(DataAlignmentFactor) +
|
||||
" (expected -8)");
|
||||
}
|
||||
|
||||
// Skip the return address register field.
|
||||
if (auto Err = RecordReader.skip(1))
|
||||
return Err;
|
||||
|
||||
uint64_t AugmentationDataLength = 0;
|
||||
if (auto Err = RecordReader.readULEB128(AugmentationDataLength))
|
||||
return Err;
|
||||
|
||||
uint32_t AugmentationDataStartOffset = RecordReader.getOffset();
|
||||
|
||||
uint8_t *NextField = &AugInfo->Fields[0];
|
||||
while (uint8_t Field = *NextField++) {
|
||||
switch (Field) {
|
||||
case 'L': {
|
||||
CIEInfo.FDEsHaveLSDAField = true;
|
||||
uint8_t LSDAPointerEncoding;
|
||||
if (auto Err = RecordReader.readInteger(LSDAPointerEncoding))
|
||||
return Err;
|
||||
if (LSDAPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
|
||||
return make_error<JITLinkError>(
|
||||
"Unsupported LSDA pointer encoding " +
|
||||
formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
break;
|
||||
}
|
||||
case 'P': {
|
||||
uint8_t PersonalityPointerEncoding = 0;
|
||||
if (auto Err = RecordReader.readInteger(PersonalityPointerEncoding))
|
||||
return Err;
|
||||
if (PersonalityPointerEncoding !=
|
||||
(DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4))
|
||||
return make_error<JITLinkError>(
|
||||
"Unspported personality pointer "
|
||||
"encoding " +
|
||||
formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
uint32_t PersonalityPointerAddress;
|
||||
if (auto Err = RecordReader.readInteger(PersonalityPointerAddress))
|
||||
return Err;
|
||||
break;
|
||||
}
|
||||
case 'R': {
|
||||
uint8_t FDEPointerEncoding;
|
||||
if (auto Err = RecordReader.readInteger(FDEPointerEncoding))
|
||||
return Err;
|
||||
if (FDEPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
|
||||
return make_error<JITLinkError>(
|
||||
"Unsupported FDE address pointer "
|
||||
"encoding " +
|
||||
formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Invalid augmentation string field");
|
||||
}
|
||||
}
|
||||
|
||||
if (RecordReader.getOffset() - AugmentationDataStartOffset >
|
||||
AugmentationDataLength)
|
||||
return make_error<JITLinkError>("Read past the end of the augmentation "
|
||||
"data while parsing fields");
|
||||
|
||||
assert(!PC.CIEInfos.count(CIESymbol.getAddress()) &&
|
||||
"Multiple CIEs recorded at the same address?");
|
||||
PC.CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
|
||||
size_t RecordOffset, size_t RecordLength,
|
||||
size_t CIEDeltaFieldOffset,
|
||||
uint32_t CIEDelta,
|
||||
BlockEdgeMap &BlockEdges) {
|
||||
LLVM_DEBUG(dbgs() << " Record is FDE\n");
|
||||
|
||||
JITTargetAddress RecordAddress = B.getAddress() + RecordOffset;
|
||||
|
||||
auto RecordContent = B.getContent().substr(RecordOffset, RecordLength);
|
||||
BinaryStreamReader RecordReader(RecordContent, PC.G.getEndianness());
|
||||
|
||||
// Skip past the CIE delta field: we've already read this far.
|
||||
RecordReader.setOffset(CIEDeltaFieldOffset + 4);
|
||||
|
||||
auto &FDESymbol =
|
||||
PC.G.addAnonymousSymbol(B, RecordOffset, RecordLength, false, false);
|
||||
|
||||
CIEInformation *CIEInfo = nullptr;
|
||||
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 = RecordReader.skip(PC.G.getPointerSize()))
|
||||
return Err;
|
||||
|
||||
if (CIEInfo->FDEsHaveLSDAField) {
|
||||
uint64_t AugmentationDataSize;
|
||||
if (auto Err = RecordReader.readULEB128(AugmentationDataSize))
|
||||
return Err;
|
||||
if (AugmentationDataSize != PC.G.getPointerSize())
|
||||
return make_error<JITLinkError>(
|
||||
"Unexpected FDE augmentation data size (expected " +
|
||||
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");
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<EHFrameEdgeFixer::AugmentationInfo>
|
||||
EHFrameEdgeFixer::parseAugmentationString(BinaryStreamReader &RecordReader) {
|
||||
AugmentationInfo AugInfo;
|
||||
uint8_t NextChar;
|
||||
uint8_t *NextField = &AugInfo.Fields[0];
|
||||
|
||||
if (auto Err = EHFrameReader.readInteger(NextChar))
|
||||
if (auto Err = RecordReader.readInteger(NextChar))
|
||||
return std::move(Err);
|
||||
|
||||
while (NextChar != 0) {
|
||||
|
@ -107,7 +555,7 @@ EHFrameBinaryParser::parseAugmentationString() {
|
|||
AugInfo.AugmentationDataPresent = true;
|
||||
break;
|
||||
case 'e':
|
||||
if (auto Err = EHFrameReader.readInteger(NextChar))
|
||||
if (auto Err = RecordReader.readInteger(NextChar))
|
||||
return std::move(Err);
|
||||
if (NextChar != 'h')
|
||||
return make_error<JITLinkError>("Unrecognized substring e" +
|
||||
|
@ -126,23 +574,25 @@ EHFrameBinaryParser::parseAugmentationString() {
|
|||
" in augmentation string");
|
||||
}
|
||||
|
||||
if (auto Err = EHFrameReader.readInteger(NextChar))
|
||||
if (auto Err = RecordReader.readInteger(NextChar))
|
||||
return std::move(Err);
|
||||
}
|
||||
|
||||
return std::move(AugInfo);
|
||||
}
|
||||
|
||||
Expected<JITTargetAddress> EHFrameBinaryParser::readAbsolutePointer() {
|
||||
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 (PointerSize == 8) {
|
||||
if (auto Err = EHFrameReader.readInteger(Addr))
|
||||
if (G.getPointerSize() == 8) {
|
||||
if (auto Err = RecordReader.readInteger(Addr))
|
||||
return std::move(Err);
|
||||
} else if (PointerSize == 4) {
|
||||
} else if (G.getPointerSize() == 4) {
|
||||
uint32_t Addr32;
|
||||
if (auto Err = EHFrameReader.readInteger(Addr32))
|
||||
if (auto Err = RecordReader.readInteger(Addr32))
|
||||
return std::move(Err);
|
||||
Addr = Addr32;
|
||||
} else
|
||||
|
@ -150,218 +600,33 @@ Expected<JITTargetAddress> EHFrameBinaryParser::readAbsolutePointer() {
|
|||
return Addr;
|
||||
}
|
||||
|
||||
Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
|
||||
size_t RecordLength) {
|
||||
// Use the dwarf namespace for convenient access to pointer encoding
|
||||
// constants.
|
||||
using namespace dwarf;
|
||||
Expected<Symbol &> EHFrameEdgeFixer::getOrCreateSymbol(ParseContext &PC,
|
||||
JITTargetAddress Addr) {
|
||||
Symbol *CanonicalSym = nullptr;
|
||||
|
||||
LLVM_DEBUG(dbgs() << " Record is CIE\n");
|
||||
auto UpdateCanonicalSym = [&](Symbol *Sym) {
|
||||
if (!CanonicalSym || Sym->getLinkage() < CanonicalSym->getLinkage() ||
|
||||
Sym->getScope() < CanonicalSym->getScope() ||
|
||||
(Sym->hasName() && !CanonicalSym->hasName()) ||
|
||||
Sym->getName() < CanonicalSym->getName())
|
||||
CanonicalSym = Sym;
|
||||
};
|
||||
|
||||
auto &CIESymbol =
|
||||
createCIERecord(EHFrameAddress + RecordOffset,
|
||||
EHFrameContent.substr(RecordOffset, RecordLength));
|
||||
if (auto *SymbolsAtAddr = PC.AddrToSyms.getSymbolsAt(Addr))
|
||||
for (auto *Sym : *SymbolsAtAddr)
|
||||
UpdateCanonicalSym(Sym);
|
||||
|
||||
CIEInformation CIEInfo(CIESymbol);
|
||||
// If we found an existing symbol at the given address then use it.
|
||||
if (CanonicalSym)
|
||||
return *CanonicalSym;
|
||||
|
||||
uint8_t Version = 0;
|
||||
if (auto Err = EHFrameReader.readInteger(Version))
|
||||
return Err;
|
||||
// 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));
|
||||
|
||||
if (Version != 0x01)
|
||||
return make_error<JITLinkError>("Bad CIE version " + Twine(Version) +
|
||||
" (should be 0x01) in eh-frame");
|
||||
|
||||
auto AugInfo = parseAugmentationString();
|
||||
if (!AugInfo)
|
||||
return AugInfo.takeError();
|
||||
|
||||
// Skip the EH Data field if present.
|
||||
if (AugInfo->EHDataFieldPresent)
|
||||
if (auto Err = EHFrameReader.skip(PointerSize))
|
||||
return Err;
|
||||
|
||||
// Read and sanity check the code alignment factor.
|
||||
{
|
||||
uint64_t CodeAlignmentFactor = 0;
|
||||
if (auto Err = EHFrameReader.readULEB128(CodeAlignmentFactor))
|
||||
return Err;
|
||||
if (CodeAlignmentFactor != 1)
|
||||
return make_error<JITLinkError>("Unsupported CIE code alignment factor " +
|
||||
Twine(CodeAlignmentFactor) +
|
||||
" (expected 1)");
|
||||
}
|
||||
|
||||
// Read and sanity check the data alignment factor.
|
||||
{
|
||||
int64_t DataAlignmentFactor = 0;
|
||||
if (auto Err = EHFrameReader.readSLEB128(DataAlignmentFactor))
|
||||
return Err;
|
||||
if (DataAlignmentFactor != -8)
|
||||
return make_error<JITLinkError>("Unsupported CIE data alignment factor " +
|
||||
Twine(DataAlignmentFactor) +
|
||||
" (expected -8)");
|
||||
}
|
||||
|
||||
// Skip the return address register field.
|
||||
if (auto Err = EHFrameReader.skip(1))
|
||||
return Err;
|
||||
|
||||
uint64_t AugmentationDataLength = 0;
|
||||
if (auto Err = EHFrameReader.readULEB128(AugmentationDataLength))
|
||||
return Err;
|
||||
|
||||
uint32_t AugmentationDataStartOffset = EHFrameReader.getOffset();
|
||||
|
||||
uint8_t *NextField = &AugInfo->Fields[0];
|
||||
while (uint8_t Field = *NextField++) {
|
||||
switch (Field) {
|
||||
case 'L': {
|
||||
CIEInfo.FDEsHaveLSDAField = true;
|
||||
uint8_t LSDAPointerEncoding;
|
||||
if (auto Err = EHFrameReader.readInteger(LSDAPointerEncoding))
|
||||
return Err;
|
||||
if (LSDAPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
|
||||
return make_error<JITLinkError>(
|
||||
"Unsupported LSDA pointer encoding " +
|
||||
formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
break;
|
||||
}
|
||||
case 'P': {
|
||||
uint8_t PersonalityPointerEncoding = 0;
|
||||
if (auto Err = EHFrameReader.readInteger(PersonalityPointerEncoding))
|
||||
return Err;
|
||||
if (PersonalityPointerEncoding !=
|
||||
(DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4))
|
||||
return make_error<JITLinkError>(
|
||||
"Unspported personality pointer "
|
||||
"encoding " +
|
||||
formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
uint32_t PersonalityPointerAddress;
|
||||
if (auto Err = EHFrameReader.readInteger(PersonalityPointerAddress))
|
||||
return Err;
|
||||
break;
|
||||
}
|
||||
case 'R': {
|
||||
uint8_t FDEPointerEncoding;
|
||||
if (auto Err = EHFrameReader.readInteger(FDEPointerEncoding))
|
||||
return Err;
|
||||
if (FDEPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
|
||||
return make_error<JITLinkError>(
|
||||
"Unsupported FDE address pointer "
|
||||
"encoding " +
|
||||
formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " +
|
||||
formatv("{0:x16}", CIESymbol.getAddress()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Invalid augmentation string field");
|
||||
}
|
||||
}
|
||||
|
||||
if (EHFrameReader.getOffset() - AugmentationDataStartOffset >
|
||||
AugmentationDataLength)
|
||||
return make_error<JITLinkError>("Read past the end of the augmentation "
|
||||
"data while parsing fields");
|
||||
|
||||
assert(!CIEInfos.count(CIESymbol.getAddress()) &&
|
||||
"Multiple CIEs recorded at the same address?");
|
||||
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");
|
||||
|
||||
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;
|
||||
|
||||
// Read and sanity check the PC-start pointer and size.
|
||||
JITTargetAddress PCBeginAddress = EHFrameAddress + EHFrameReader.getOffset();
|
||||
|
||||
auto PCBeginDelta = readAbsolutePointer();
|
||||
if (!PCBeginDelta)
|
||||
return PCBeginDelta.takeError();
|
||||
|
||||
JITTargetAddress PCBegin = PCBeginAddress + *PCBeginDelta;
|
||||
LLVM_DEBUG({
|
||||
dbgs() << " PC begin: " << format("0x%016" PRIx64, PCBegin) << "\n";
|
||||
});
|
||||
|
||||
auto *TargetSymbol = getSymbolAtAddress(PCBegin);
|
||||
|
||||
if (!TargetSymbol)
|
||||
return make_error<JITLinkError>("FDE PC-begin " +
|
||||
formatv("{0:x16}", PCBegin) +
|
||||
" does not point at symbol");
|
||||
|
||||
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()));
|
||||
|
||||
LLVM_DEBUG(dbgs() << " FDE target: " << *TargetSymbol << "\n");
|
||||
|
||||
// Skip over the PC range size field.
|
||||
if (auto Err = EHFrameReader.skip(PointerSize))
|
||||
return Err;
|
||||
|
||||
Symbol *LSDASymbol = nullptr;
|
||||
JITTargetAddress LSDAAddress = 0;
|
||||
if (CIEInfo.FDEsHaveLSDAField) {
|
||||
uint64_t AugmentationDataSize;
|
||||
if (auto Err = EHFrameReader.readULEB128(AugmentationDataSize))
|
||||
return Err;
|
||||
if (AugmentationDataSize != PointerSize)
|
||||
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();
|
||||
|
||||
JITTargetAddress LSDA = LSDAAddress + *LSDADelta;
|
||||
|
||||
LSDASymbol = getSymbolAtAddress(LSDA);
|
||||
|
||||
if (!LSDASymbol)
|
||||
return make_error<JITLinkError>("FDE LSDA " + formatv("{0:x16}", LSDA) +
|
||||
" does not point at symbol");
|
||||
|
||||
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()));
|
||||
|
||||
LLVM_DEBUG(dbgs() << " FDE LSDA: " << *LSDASymbol << "\n");
|
||||
}
|
||||
|
||||
JITTargetAddress RecordAddress = EHFrameAddress + RecordOffset;
|
||||
auto FDESymbol = createFDERecord(
|
||||
RecordAddress, EHFrameContent.substr(RecordOffset, RecordLength),
|
||||
*CIEInfo.CIESymbol, CIEPointerAddress - RecordAddress, *TargetSymbol,
|
||||
PCBeginAddress - RecordAddress, LSDASymbol, LSDAAddress - RecordAddress);
|
||||
|
||||
return FDESymbol.takeError();
|
||||
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);
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
Error addToGraph();
|
||||
EHFrameSplitter(StringRef EHFrameSectionName);
|
||||
Error operator()(LinkGraph &G);
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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));
|
||||
|
|
Binary file not shown.
|
@ -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.
|
Loading…
Reference in New Issue