[JITLink] Switch from an atom-based model to a "blocks and symbols" model.

In the Atom model the symbols, content and relocations of a relocatable object
file are represented as a graph of atoms, where each Atom represents a
contiguous block of content with a single name (or no name at all if the
content is anonymous), and where edges between Atoms represent relocations.
If more than one symbol is associated with a contiguous block of content then
the content is broken into multiple atoms and layout constraints (represented by
edges) are introduced to ensure that the content remains effectively contiguous.
These layout constraints must be kept in mind when examining the content
associated with a symbol (it may be spread over multiple atoms) or when applying
certain relocation types (e.g. MachO subtractors).

This patch replaces the Atom model in JITLink with a blocks-and-symbols model.
The blocks-and-symbols model represents relocatable object files as bipartite
graphs, with one set of nodes representing contiguous content (Blocks) and
another representing named or anonymous locations (Symbols) within a Block.
Relocations are represented as edges from Blocks to Symbols. This scheme
removes layout constraints (simplifying handling of MachO alt-entry symbols,
and hopefully ELF sections at some point in the future) and simplifies some
relocation logic.

llvm-svn: 373689
This commit is contained in:
Lang Hames 2019-10-04 03:55:26 +00:00
parent ff55e2e047
commit 4e920e58e6
25 changed files with 2468 additions and 2183 deletions

View File

@ -81,7 +81,7 @@ using StoreFrameRangeFunction =
/// Authors of JITLinkContexts can use this function to register a post-fixup /// Authors of JITLinkContexts can use this function to register a post-fixup
/// pass that records the range of the eh-frame section. This range can /// pass that records the range of the eh-frame section. This range can
/// be used after finalization to register and deregister the frame. /// be used after finalization to register and deregister the frame.
AtomGraphPassFunction LinkGraphPassFunction
createEHFrameRecorderPass(const Triple &TT, createEHFrameRecorderPass(const Triple &TT,
StoreFrameRangeFunction StoreFrameRange); StoreFrameRangeFunction StoreFrameRange);

File diff suppressed because it is too large Load Diff

View File

@ -33,20 +33,19 @@ public:
class SegmentRequest { class SegmentRequest {
public: public:
SegmentRequest() = default; SegmentRequest() = default;
SegmentRequest(size_t ContentSize, unsigned ContentAlign, SegmentRequest(uint64_t Alignment, size_t ContentSize,
uint64_t ZeroFillSize, unsigned ZeroFillAlign) uint64_t ZeroFillSize)
: ContentSize(ContentSize), ZeroFillSize(ZeroFillSize), : Alignment(Alignment), ContentSize(ContentSize),
ContentAlign(ContentAlign), ZeroFillAlign(ZeroFillAlign) {} ZeroFillSize(ZeroFillSize) {
assert(isPowerOf2_32(Alignment) && "Alignment must be power of 2");
}
uint64_t getAlignment() const { return Alignment; }
size_t getContentSize() const { return ContentSize; } size_t getContentSize() const { return ContentSize; }
unsigned getContentAlignment() const { return ContentAlign; }
uint64_t getZeroFillSize() const { return ZeroFillSize; } uint64_t getZeroFillSize() const { return ZeroFillSize; }
unsigned getZeroFillAlignment() const { return ZeroFillAlign; }
private: private:
uint64_t Alignment = 0;
size_t ContentSize = 0; size_t ContentSize = 0;
uint64_t ZeroFillSize = 0; uint64_t ZeroFillSize = 0;
unsigned ContentAlign = 0;
unsigned ZeroFillAlign = 0;
}; };
using SegmentsRequestMap = DenseMap<unsigned, SegmentRequest>; using SegmentsRequestMap = DenseMap<unsigned, SegmentRequest>;

View File

@ -20,24 +20,23 @@ namespace jitlink {
template <typename BuilderImpl> class BasicGOTAndStubsBuilder { template <typename BuilderImpl> class BasicGOTAndStubsBuilder {
public: public:
BasicGOTAndStubsBuilder(AtomGraph &G) : G(G) {} BasicGOTAndStubsBuilder(LinkGraph &G) : G(G) {}
void run() { void run() {
// We're going to be adding new atoms, but we don't want to iterate over // We're going to be adding new blocks, but we don't want to iterate over
// the newly added ones, so just copy the existing atoms out. // the newly added ones, so just copy the existing blocks out.
std::vector<DefinedAtom *> DAs(G.defined_atoms().begin(), std::vector<Block *> Blocks(G.blocks().begin(), G.blocks().end());
G.defined_atoms().end());
for (auto *DA : DAs) for (auto *B : Blocks)
for (auto &E : DA->edges()) for (auto &E : B->edges())
if (impl().isGOTEdge(E)) if (impl().isGOTEdge(E))
impl().fixGOTEdge(E, getGOTEntryAtom(E.getTarget())); impl().fixGOTEdge(E, getGOTEntrySymbol(E.getTarget()));
else if (impl().isExternalBranchEdge(E)) else if (impl().isExternalBranchEdge(E))
impl().fixExternalBranchEdge(E, getStubAtom(E.getTarget())); impl().fixExternalBranchEdge(E, getStubSymbol(E.getTarget()));
} }
protected: protected:
Atom &getGOTEntryAtom(Atom &Target) { Symbol &getGOTEntrySymbol(Symbol &Target) {
assert(Target.hasName() && "GOT edge cannot point to anonymous target"); assert(Target.hasName() && "GOT edge cannot point to anonymous target");
auto GOTEntryI = GOTEntries.find(Target.getName()); auto GOTEntryI = GOTEntries.find(Target.getName());
@ -49,31 +48,31 @@ protected:
GOTEntries.insert(std::make_pair(Target.getName(), &GOTEntry)).first; GOTEntries.insert(std::make_pair(Target.getName(), &GOTEntry)).first;
} }
assert(GOTEntryI != GOTEntries.end() && "Could not get GOT entry atom"); assert(GOTEntryI != GOTEntries.end() && "Could not get GOT entry symbol");
return *GOTEntryI->second; return *GOTEntryI->second;
} }
Atom &getStubAtom(Atom &Target) { Symbol &getStubSymbol(Symbol &Target) {
assert(Target.hasName() && assert(Target.hasName() &&
"External branch edge can not point to an anonymous target"); "External branch edge can not point to an anonymous target");
auto StubI = Stubs.find(Target.getName()); auto StubI = Stubs.find(Target.getName());
if (StubI == Stubs.end()) { if (StubI == Stubs.end()) {
auto &StubAtom = impl().createStub(Target); auto &StubSymbol = impl().createStub(Target);
StubI = Stubs.insert(std::make_pair(Target.getName(), &StubAtom)).first; StubI = Stubs.insert(std::make_pair(Target.getName(), &StubSymbol)).first;
} }
assert(StubI != Stubs.end() && "Count not get stub atom"); assert(StubI != Stubs.end() && "Count not get stub symbol");
return *StubI->second; return *StubI->second;
} }
AtomGraph &G; LinkGraph &G;
private: private:
BuilderImpl &impl() { return static_cast<BuilderImpl &>(*this); } BuilderImpl &impl() { return static_cast<BuilderImpl &>(*this); }
DenseMap<StringRef, DefinedAtom *> GOTEntries; DenseMap<StringRef, Symbol *> GOTEntries;
DenseMap<StringRef, DefinedAtom *> Stubs; DenseMap<StringRef, Symbol *> Stubs;
}; };
} // end namespace jitlink } // end namespace jitlink

View File

@ -5,7 +5,7 @@ add_llvm_library(LLVMJITLink
EHFrameSupport.cpp EHFrameSupport.cpp
MachO.cpp MachO.cpp
MachO_x86_64.cpp MachO_x86_64.cpp
MachOAtomGraphBuilder.cpp MachOLinkGraphBuilder.cpp
DEPENDS DEPENDS
intrinsics_gen intrinsics_gen

View File

@ -17,65 +17,57 @@
namespace llvm { namespace llvm {
namespace jitlink { namespace jitlink {
EHFrameParser::EHFrameParser(AtomGraph &G, Section &EHFrameSection, EHFrameBinaryParser::EHFrameBinaryParser(JITTargetAddress EHFrameAddress,
StringRef EHFrameContent, StringRef EHFrameContent,
JITTargetAddress EHFrameAddress, unsigned PointerSize,
Edge::Kind FDEToCIERelocKind, support::endianness Endianness)
Edge::Kind FDEToTargetRelocKind) : EHFrameAddress(EHFrameAddress), EHFrameContent(EHFrameContent),
: G(G), EHFrameSection(EHFrameSection), EHFrameContent(EHFrameContent), PointerSize(PointerSize), EHFrameReader(EHFrameContent, Endianness) {}
EHFrameAddress(EHFrameAddress),
EHFrameReader(EHFrameContent, G.getEndianness()),
FDEToCIERelocKind(FDEToCIERelocKind),
FDEToTargetRelocKind(FDEToTargetRelocKind) {}
Error EHFrameParser::atomize() { Error EHFrameBinaryParser::addToGraph() {
while (!EHFrameReader.empty()) { while (!EHFrameReader.empty()) {
size_t RecordOffset = EHFrameReader.getOffset(); size_t RecordOffset = EHFrameReader.getOffset();
JITTargetAddress RecordAddress = EHFrameAddress + RecordOffset;
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << "Processing eh-frame record at " dbgs() << "Processing eh-frame record at "
<< format("0x%016" PRIx64, EHFrameAddress + RecordOffset) << format("0x%016" PRIx64, RecordAddress) << " (offset "
<< " (offset " << RecordOffset << ")\n"; << RecordOffset << ")\n";
}); });
size_t CIELength = 0; size_t RecordLength = 0;
uint32_t CIELengthField; uint32_t RecordLengthField;
if (auto Err = EHFrameReader.readInteger(CIELengthField)) if (auto Err = EHFrameReader.readInteger(RecordLengthField))
return Err; return Err;
// Process CIE length/extended-length fields to build the atom. // 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 // 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 // (not including data up to the end of the field itself) so we have to
// bump CIELength to include the data up to the end of the field: 4 bytes // 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. // for Length, or 12 bytes (4 bytes + 8 bytes) for ExtendedLength.
if (CIELengthField == 0) // Length 0 means end of __eh_frame section. if (RecordLengthField == 0) // Length 0 means end of __eh_frame section.
break; break;
// If the regular length field's value is 0xffffffff, use extended length. // If the regular length field's value is 0xffffffff, use extended length.
if (CIELengthField == 0xffffffff) { if (RecordLengthField == 0xffffffff) {
uint64_t CIEExtendedLengthField; uint64_t ExtendedLengthField;
if (auto Err = EHFrameReader.readInteger(CIEExtendedLengthField)) if (auto Err = EHFrameReader.readInteger(ExtendedLengthField))
return Err; return Err;
if (CIEExtendedLengthField > EHFrameReader.bytesRemaining()) if (ExtendedLengthField > EHFrameReader.bytesRemaining())
return make_error<JITLinkError>("CIE record extends past the end of " return make_error<JITLinkError>("CIE record extends past the end of "
"the __eh_frame section"); "the __eh_frame section");
if (CIEExtendedLengthField + 12 > std::numeric_limits<size_t>::max()) if (ExtendedLengthField + 12 > std::numeric_limits<size_t>::max())
return make_error<JITLinkError>("CIE record too large to process"); return make_error<JITLinkError>("CIE record too large to process");
CIELength = CIEExtendedLengthField + 12; RecordLength = ExtendedLengthField + 12;
} else { } else {
if (CIELengthField > EHFrameReader.bytesRemaining()) if (RecordLengthField > EHFrameReader.bytesRemaining())
return make_error<JITLinkError>("CIE record extends past the end of " return make_error<JITLinkError>("CIE record extends past the end of "
"the __eh_frame section"); "the __eh_frame section");
CIELength = CIELengthField + 4; RecordLength = RecordLengthField + 4;
} }
LLVM_DEBUG(dbgs() << " length: " << CIELength << "\n"); LLVM_DEBUG(dbgs() << " length: " << RecordLength << "\n");
// Add an atom for this record.
CurRecordAtom = &G.addAnonymousAtom(
EHFrameSection, EHFrameAddress + RecordOffset, G.getPointerSize());
CurRecordAtom->setContent(EHFrameContent.substr(RecordOffset, CIELength));
// Read the CIE Pointer. // Read the CIE Pointer.
size_t CIEPointerAddress = EHFrameAddress + EHFrameReader.getOffset(); size_t CIEPointerAddress = EHFrameAddress + EHFrameReader.getOffset();
@ -85,21 +77,24 @@ Error EHFrameParser::atomize() {
// Based on the CIE pointer value, parse this as a CIE or FDE record. // Based on the CIE pointer value, parse this as a CIE or FDE record.
if (CIEPointer == 0) { if (CIEPointer == 0) {
if (auto Err = processCIE()) if (auto Err = processCIE(RecordOffset, RecordLength))
return Err; return Err;
} else { } else {
if (auto Err = processFDE(CIEPointerAddress, CIEPointer)) if (auto Err = processFDE(RecordOffset, RecordLength, CIEPointerAddress,
CIEPointer))
return Err; return Err;
} }
EHFrameReader.setOffset(RecordOffset + CIELength); EHFrameReader.setOffset(RecordOffset + RecordLength);
} }
return Error::success(); return Error::success();
} }
Expected<EHFrameParser::AugmentationInfo> void EHFrameBinaryParser::anchor() {}
EHFrameParser::parseAugmentationString() {
Expected<EHFrameBinaryParser::AugmentationInfo>
EHFrameBinaryParser::parseAugmentationString() {
AugmentationInfo AugInfo; AugmentationInfo AugInfo;
uint8_t NextChar; uint8_t NextChar;
uint8_t *NextField = &AugInfo.Fields[0]; uint8_t *NextField = &AugInfo.Fields[0];
@ -139,14 +134,14 @@ EHFrameParser::parseAugmentationString() {
return std::move(AugInfo); return std::move(AugInfo);
} }
Expected<JITTargetAddress> EHFrameParser::readAbsolutePointer() { Expected<JITTargetAddress> EHFrameBinaryParser::readAbsolutePointer() {
static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t), static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t),
"Result must be able to hold a uint64_t"); "Result must be able to hold a uint64_t");
JITTargetAddress Addr; JITTargetAddress Addr;
if (G.getPointerSize() == 8) { if (PointerSize == 8) {
if (auto Err = EHFrameReader.readInteger(Addr)) if (auto Err = EHFrameReader.readInteger(Addr))
return std::move(Err); return std::move(Err);
} else if (G.getPointerSize() == 4) { } else if (PointerSize == 4) {
uint32_t Addr32; uint32_t Addr32;
if (auto Err = EHFrameReader.readInteger(Addr32)) if (auto Err = EHFrameReader.readInteger(Addr32))
return std::move(Err); return std::move(Err);
@ -156,14 +151,19 @@ Expected<JITTargetAddress> EHFrameParser::readAbsolutePointer() {
return Addr; return Addr;
} }
Error EHFrameParser::processCIE() { Error EHFrameBinaryParser::processCIE(size_t RecordOffset,
size_t RecordLength) {
// Use the dwarf namespace for convenient access to pointer encoding // Use the dwarf namespace for convenient access to pointer encoding
// constants. // constants.
using namespace dwarf; using namespace dwarf;
LLVM_DEBUG(dbgs() << " Record is CIE\n"); LLVM_DEBUG(dbgs() << " Record is CIE\n");
CIEInformation CIEInfo(*CurRecordAtom); auto &CIESymbol =
createCIERecord(EHFrameAddress + RecordOffset,
EHFrameContent.substr(RecordOffset, RecordLength));
CIEInformation CIEInfo(CIESymbol);
uint8_t Version = 0; uint8_t Version = 0;
if (auto Err = EHFrameReader.readInteger(Version)) if (auto Err = EHFrameReader.readInteger(Version))
@ -179,7 +179,7 @@ Error EHFrameParser::processCIE() {
// Skip the EH Data field if present. // Skip the EH Data field if present.
if (AugInfo->EHDataFieldPresent) if (AugInfo->EHDataFieldPresent)
if (auto Err = EHFrameReader.skip(G.getPointerSize())) if (auto Err = EHFrameReader.skip(PointerSize))
return Err; return Err;
// Read and sanity check the code alignment factor. // Read and sanity check the code alignment factor.
@ -226,7 +226,7 @@ Error EHFrameParser::processCIE() {
return make_error<JITLinkError>( return make_error<JITLinkError>(
"Unsupported LSDA pointer encoding " + "Unsupported LSDA pointer encoding " +
formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " + formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " +
formatv("{0:x16}", CurRecordAtom->getAddress())); formatv("{0:x16}", CIESymbol.getAddress()));
break; break;
} }
case 'P': { case 'P': {
@ -239,7 +239,7 @@ Error EHFrameParser::processCIE() {
"Unspported personality pointer " "Unspported personality pointer "
"encoding " + "encoding " +
formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " + formatv("{0:x2}", PersonalityPointerEncoding) + " in CIE at " +
formatv("{0:x16}", CurRecordAtom->getAddress())); formatv("{0:x16}", CIESymbol.getAddress()));
uint32_t PersonalityPointerAddress; uint32_t PersonalityPointerAddress;
if (auto Err = EHFrameReader.readInteger(PersonalityPointerAddress)) if (auto Err = EHFrameReader.readInteger(PersonalityPointerAddress))
return Err; return Err;
@ -254,7 +254,7 @@ Error EHFrameParser::processCIE() {
"Unsupported FDE address pointer " "Unsupported FDE address pointer "
"encoding " + "encoding " +
formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " + formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " +
formatv("{0:x16}", CurRecordAtom->getAddress())); formatv("{0:x16}", CIESymbol.getAddress()));
break; break;
} }
default: default:
@ -267,14 +267,15 @@ Error EHFrameParser::processCIE() {
return make_error<JITLinkError>("Read past the end of the augmentation " return make_error<JITLinkError>("Read past the end of the augmentation "
"data while parsing fields"); "data while parsing fields");
assert(!CIEInfos.count(CurRecordAtom->getAddress()) && assert(!CIEInfos.count(CIESymbol.getAddress()) &&
"Multiple CIEs recorded at the same address?"); "Multiple CIEs recorded at the same address?");
CIEInfos[CurRecordAtom->getAddress()] = std::move(CIEInfo); CIEInfos[CIESymbol.getAddress()] = std::move(CIEInfo);
return Error::success(); return Error::success();
} }
Error EHFrameParser::processFDE(JITTargetAddress CIEPointerAddress, Error EHFrameBinaryParser::processFDE(size_t RecordOffset, size_t RecordLength,
JITTargetAddress CIEPointerAddress,
uint32_t CIEPointer) { uint32_t CIEPointer) {
LLVM_DEBUG(dbgs() << " Record is FDE\n"); LLVM_DEBUG(dbgs() << " Record is FDE\n");
@ -286,16 +287,11 @@ Error EHFrameParser::processFDE(JITTargetAddress CIEPointerAddress,
auto CIEInfoItr = CIEInfos.find(CIEPointerAddress - CIEPointer); auto CIEInfoItr = CIEInfos.find(CIEPointerAddress - CIEPointer);
if (CIEInfoItr == CIEInfos.end()) if (CIEInfoItr == CIEInfos.end())
return make_error<JITLinkError>( return make_error<JITLinkError>(
"FDE at " + formatv("{0:x16}", CurRecordAtom->getAddress()) + "FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset) +
" points to non-existant CIE at " + " points to non-existant CIE at " +
formatv("{0:x16}", CIEPointerAddress - CIEPointer)); formatv("{0:x16}", CIEPointerAddress - CIEPointer));
auto &CIEInfo = CIEInfoItr->second; auto &CIEInfo = CIEInfoItr->second;
// The CIEPointer looks good. Add a relocation.
CurRecordAtom->addEdge(FDEToCIERelocKind,
CIEPointerAddress - CurRecordAtom->getAddress(),
*CIEInfo.CIEAtom, 0);
// Read and sanity check the PC-start pointer and size. // Read and sanity check the PC-start pointer and size.
JITTargetAddress PCBeginAddress = EHFrameAddress + EHFrameReader.getOffset(); JITTargetAddress PCBeginAddress = EHFrameAddress + EHFrameReader.getOffset();
@ -308,80 +304,65 @@ Error EHFrameParser::processFDE(JITTargetAddress CIEPointerAddress,
dbgs() << " PC begin: " << format("0x%016" PRIx64, PCBegin) << "\n"; dbgs() << " PC begin: " << format("0x%016" PRIx64, PCBegin) << "\n";
}); });
auto *TargetAtom = G.getAtomByAddress(PCBegin); auto *TargetSymbol = getSymbolAtAddress(PCBegin);
if (!TargetAtom) if (!TargetSymbol)
return make_error<JITLinkError>("FDE PC-begin " + return make_error<JITLinkError>("FDE PC-begin " +
formatv("{0:x16}", PCBegin) + formatv("{0:x16}", PCBegin) +
" does not point at atom"); " does not point at symbol");
if (TargetAtom->getAddress() != PCBegin) if (TargetSymbol->getAddress() != PCBegin)
return make_error<JITLinkError>( return make_error<JITLinkError>(
"FDE PC-begin " + formatv("{0:x16}", PCBegin) + "FDE PC-begin " + formatv("{0:x16}", PCBegin) +
" does not point to start of atom at " + " does not point to start of symbol at " +
formatv("{0:x16}", TargetAtom->getAddress())); formatv("{0:x16}", TargetSymbol->getAddress()));
LLVM_DEBUG(dbgs() << " FDE target: " << *TargetAtom << "\n"); LLVM_DEBUG(dbgs() << " FDE target: " << *TargetSymbol << "\n");
// The PC-start pointer and size look good. Add relocations.
CurRecordAtom->addEdge(FDEToTargetRelocKind,
PCBeginAddress - CurRecordAtom->getAddress(),
*TargetAtom, 0);
// Add a keep-alive relocation from the function to the FDE to ensure it is
// not dead stripped.
TargetAtom->addEdge(Edge::KeepAlive, 0, *CurRecordAtom, 0);
// Skip over the PC range size field. // Skip over the PC range size field.
if (auto Err = EHFrameReader.skip(G.getPointerSize())) if (auto Err = EHFrameReader.skip(PointerSize))
return Err; return Err;
Symbol *LSDASymbol = nullptr;
JITTargetAddress LSDAAddress = 0;
if (CIEInfo.FDEsHaveLSDAField) { if (CIEInfo.FDEsHaveLSDAField) {
uint64_t AugmentationDataSize; uint64_t AugmentationDataSize;
if (auto Err = EHFrameReader.readULEB128(AugmentationDataSize)) if (auto Err = EHFrameReader.readULEB128(AugmentationDataSize))
return Err; return Err;
if (AugmentationDataSize != G.getPointerSize()) if (AugmentationDataSize != PointerSize)
return make_error<JITLinkError>( return make_error<JITLinkError>(
"Unexpected FDE augmentation data size (expected " + "Unexpected FDE augmentation data size (expected " +
Twine(G.getPointerSize()) + ", got " + Twine(AugmentationDataSize) + Twine(PointerSize) + ", got " + Twine(AugmentationDataSize) +
") for FDE at " + formatv("{0:x16}", CurRecordAtom->getAddress())); ") for FDE at " + formatv("{0:x16}", EHFrameAddress + RecordOffset));
JITTargetAddress LSDAAddress = EHFrameAddress + EHFrameReader.getOffset(); LSDAAddress = EHFrameAddress + EHFrameReader.getOffset();
auto LSDADelta = readAbsolutePointer(); auto LSDADelta = readAbsolutePointer();
if (!LSDADelta) if (!LSDADelta)
return LSDADelta.takeError(); return LSDADelta.takeError();
JITTargetAddress LSDA = LSDAAddress + *LSDADelta; JITTargetAddress LSDA = LSDAAddress + *LSDADelta;
auto *LSDAAtom = G.getAtomByAddress(LSDA); LSDASymbol = getSymbolAtAddress(LSDA);
if (!LSDAAtom) if (!LSDASymbol)
return make_error<JITLinkError>("FDE LSDA " + formatv("{0:x16}", LSDA) + return make_error<JITLinkError>("FDE LSDA " + formatv("{0:x16}", LSDA) +
" does not point at atom"); " does not point at symbol");
if (LSDAAtom->getAddress() != LSDA) if (LSDASymbol->getAddress() != LSDA)
return make_error<JITLinkError>( return make_error<JITLinkError>(
"FDE LSDA " + formatv("{0:x16}", LSDA) + "FDE LSDA " + formatv("{0:x16}", LSDA) +
" does not point to start of atom at " + " does not point to start of symbol at " +
formatv("{0:x16}", LSDAAtom->getAddress())); formatv("{0:x16}", LSDASymbol->getAddress()));
LLVM_DEBUG(dbgs() << " FDE LSDA: " << *LSDAAtom << "\n"); LLVM_DEBUG(dbgs() << " FDE LSDA: " << *LSDASymbol << "\n");
// LSDA looks good. Add relocations.
CurRecordAtom->addEdge(FDEToTargetRelocKind,
LSDAAddress - CurRecordAtom->getAddress(), *LSDAAtom,
0);
} }
return Error::success(); JITTargetAddress RecordAddress = EHFrameAddress + RecordOffset;
} auto FDESymbol = createFDERecord(
RecordAddress, EHFrameContent.substr(RecordOffset, RecordLength),
*CIEInfo.CIESymbol, CIEPointerAddress - RecordAddress, *TargetSymbol,
PCBeginAddress - RecordAddress, LSDASymbol, LSDAAddress - RecordAddress);
Error addEHFrame(AtomGraph &G, Section &EHFrameSection, return FDESymbol.takeError();
StringRef EHFrameContent, JITTargetAddress EHFrameAddress,
Edge::Kind FDEToCIERelocKind,
Edge::Kind FDEToTargetRelocKind) {
return EHFrameParser(G, EHFrameSection, EHFrameContent, EHFrameAddress,
FDEToCIERelocKind, FDEToTargetRelocKind)
.atomize();
} }
// Determine whether we can register EH tables. // Determine whether we can register EH tables.
@ -523,7 +504,7 @@ InProcessEHFrameRegistrar &InProcessEHFrameRegistrar::getInstance() {
InProcessEHFrameRegistrar::InProcessEHFrameRegistrar() {} InProcessEHFrameRegistrar::InProcessEHFrameRegistrar() {}
AtomGraphPassFunction LinkGraphPassFunction
createEHFrameRecorderPass(const Triple &TT, createEHFrameRecorderPass(const Triple &TT,
StoreFrameRangeFunction StoreRangeAddress) { StoreFrameRangeFunction StoreRangeAddress) {
const char *EHFrameSectionName = nullptr; const char *EHFrameSectionName = nullptr;
@ -534,13 +515,13 @@ createEHFrameRecorderPass(const Triple &TT,
auto RecordEHFrame = auto RecordEHFrame =
[EHFrameSectionName, [EHFrameSectionName,
StoreFrameRange = std::move(StoreRangeAddress)](AtomGraph &G) -> Error { StoreFrameRange = std::move(StoreRangeAddress)](LinkGraph &G) -> Error {
// Search for a non-empty eh-frame and record the address of the first atom // Search for a non-empty eh-frame and record the address of the first
// in it. // symbol in it.
JITTargetAddress Addr = 0; JITTargetAddress Addr = 0;
size_t Size = 0; size_t Size = 0;
if (auto *S = G.findSectionByName(EHFrameSectionName)) { if (auto *S = G.findSectionByName(EHFrameSectionName)) {
auto R = S->getRange(); auto R = SectionRange(*S);
Addr = R.getStart(); Addr = R.getStart();
Size = R.getSize(); Size = R.getSize();
} }

View File

@ -21,18 +21,31 @@
namespace llvm { namespace llvm {
namespace jitlink { namespace jitlink {
/// A generic parser for eh-frame sections. /// A generic binary parser for eh-frame sections.
/// ///
/// Adds atoms representing CIE and FDE entries, using the given FDE-to-CIE and /// Adds blocks and symbols representing CIE and FDE entries to a JITLink graph.
/// FDEToTarget relocation kinds. ///
class EHFrameParser { /// 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 {
public: public:
EHFrameParser(AtomGraph &G, Section &EHFrameSection, StringRef EHFrameContent, EHFrameBinaryParser(JITTargetAddress EHFrameAddress, StringRef EHFrameContent,
JITTargetAddress EHFrameAddress, Edge::Kind FDEToCIERelocKind, unsigned PointerSize, support::endianness Endianness);
Edge::Kind FDEToTargetRelocKind); virtual ~EHFrameBinaryParser() {}
Error atomize();
Error addToGraph();
private: 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 { struct AugmentationInfo {
bool AugmentationDataPresent = false; bool AugmentationDataPresent = false;
bool EHDataFieldPresent = false; bool EHDataFieldPresent = false;
@ -41,31 +54,24 @@ private:
Expected<AugmentationInfo> parseAugmentationString(); Expected<AugmentationInfo> parseAugmentationString();
Expected<JITTargetAddress> readAbsolutePointer(); Expected<JITTargetAddress> readAbsolutePointer();
Error processCIE(); Error processCIE(size_t RecordOffset, size_t RecordLength);
Error processFDE(JITTargetAddress CIEPointerAddress, uint32_t CIEPointer); Error processFDE(size_t RecordOffset, size_t RecordLength,
JITTargetAddress CIEPointerOffset, uint32_t CIEPointer);
struct CIEInformation { struct CIEInformation {
CIEInformation() = default; CIEInformation() = default;
CIEInformation(DefinedAtom &CIEAtom) : CIEAtom(&CIEAtom) {} CIEInformation(Symbol &CIESymbol) : CIESymbol(&CIESymbol) {}
DefinedAtom *CIEAtom = nullptr; Symbol *CIESymbol = nullptr;
bool FDEsHaveLSDAField = false; bool FDEsHaveLSDAField = false;
}; };
AtomGraph &G;
Section &EHFrameSection;
StringRef EHFrameContent;
JITTargetAddress EHFrameAddress; JITTargetAddress EHFrameAddress;
StringRef EHFrameContent;
unsigned PointerSize;
BinaryStreamReader EHFrameReader; BinaryStreamReader EHFrameReader;
DefinedAtom *CurRecordAtom = nullptr;
DenseMap<JITTargetAddress, CIEInformation> CIEInfos; DenseMap<JITTargetAddress, CIEInformation> CIEInfos;
Edge::Kind FDEToCIERelocKind;
Edge::Kind FDEToTargetRelocKind;
}; };
Error addEHFrame(AtomGraph &G, Section &EHFrameSection,
StringRef EHFrameContent, JITTargetAddress EHFrameAddress,
Edge::Kind FDEToCIERelocKind, Edge::Kind FDEToTargetRelocKind);
} // end namespace jitlink } // end namespace jitlink
} // end namespace llvm } // end namespace llvm

View File

@ -56,95 +56,143 @@ std::error_code JITLinkError::convertToErrorCode() const {
return std::error_code(GenericJITLinkError, *JITLinkerErrorCategory); return std::error_code(GenericJITLinkError, *JITLinkerErrorCategory);
} }
const StringRef getGenericEdgeKindName(Edge::Kind K) { const char *getGenericEdgeKindName(Edge::Kind K) {
switch (K) { switch (K) {
case Edge::Invalid: case Edge::Invalid:
return "INVALID RELOCATION"; return "INVALID RELOCATION";
case Edge::KeepAlive: case Edge::KeepAlive:
return "Keep-Alive"; return "Keep-Alive";
case Edge::LayoutNext:
return "Layout-Next";
default: default:
llvm_unreachable("Unrecognized relocation kind"); llvm_unreachable("Unrecognized relocation kind");
} }
} }
raw_ostream &operator<<(raw_ostream &OS, const Atom &A) { const char *getLinkageName(Linkage L) {
switch (L) {
case Linkage::Strong:
return "strong";
case Linkage::Weak:
return "weak";
}
}
const char *getScopeName(Scope S) {
switch (S) {
case Scope::Default:
return "default";
case Scope::Hidden:
return "hidden";
case Scope::Local:
return "local";
}
}
raw_ostream &operator<<(raw_ostream &OS, const Block &B) {
return OS << formatv("{0:x16}", B.getAddress()) << " -- "
<< formatv("{0:x16}", B.getAddress() + B.getSize()) << ": "
<< (B.isZeroFill() ? "zero-fill" : "content")
<< ", align = " << B.getAlignment()
<< ", align-ofs = " << B.getAlignmentOffset()
<< ", section = " << B.getSection().getName();
}
raw_ostream &operator<<(raw_ostream &OS, const Symbol &Sym) {
OS << "<"; OS << "<";
if (A.getName().empty()) if (Sym.getName().empty())
OS << "anon@" << format("0x%016" PRIx64, A.getAddress()); OS << "*anon*";
else else
OS << A.getName(); OS << Sym.getName();
OS << " ["; OS << ": flags = ";
if (A.isDefined()) { switch (Sym.getLinkage()) {
auto &DA = static_cast<const DefinedAtom &>(A); case Linkage::Strong:
OS << " section=" << DA.getSection().getName(); OS << 'S';
if (DA.isLive()) break;
OS << " live"; case Linkage::Weak:
if (DA.shouldDiscard()) OS << 'W';
OS << " should-discard"; break;
} else }
OS << " external"; switch (Sym.getScope()) {
OS << " ]>"; case Scope::Default:
OS << 'D';
break;
case Scope::Hidden:
OS << 'H';
break;
case Scope::Local:
OS << 'L';
break;
}
OS << (Sym.isLive() ? '+' : '-')
<< ", size = " << formatv("{0:x8}", Sym.getSize())
<< ", addr = " << formatv("{0:x16}", Sym.getAddress()) << " ("
<< formatv("{0:x16}", Sym.getAddressable().getAddress()) << " + "
<< formatv("{0:x8}", Sym.getOffset());
if (Sym.isDefined())
OS << " " << Sym.getBlock().getSection().getName();
OS << ")>";
return OS; return OS;
} }
void printEdge(raw_ostream &OS, const Atom &FixupAtom, const Edge &E, void printEdge(raw_ostream &OS, const Block &B, const Edge &E,
StringRef EdgeKindName) { StringRef EdgeKindName) {
OS << "edge@" << formatv("{0:x16}", FixupAtom.getAddress() + E.getOffset()) OS << "edge@" << formatv("{0:x16}", B.getAddress() + E.getOffset()) << ": "
<< ": " << FixupAtom << " + " << E.getOffset() << " -- " << EdgeKindName << formatv("{0:x16}", B.getAddress()) << " + " << E.getOffset() << " -- "
<< " -> " << E.getTarget() << " + " << E.getAddend(); << EdgeKindName << " -> " << E.getTarget() << " + " << E.getAddend();
} }
Section::~Section() { Section::~Section() {
for (auto *DA : DefinedAtoms) for (auto *Sym : Symbols)
DA->~DefinedAtom(); Sym->~Symbol();
} }
void AtomGraph::dump(raw_ostream &OS, void LinkGraph::dump(raw_ostream &OS,
std::function<StringRef(Edge::Kind)> EdgeKindToName) { std::function<StringRef(Edge::Kind)> EdgeKindToName) {
if (!EdgeKindToName) if (!EdgeKindToName)
EdgeKindToName = [](Edge::Kind K) { return StringRef(); }; EdgeKindToName = [](Edge::Kind K) { return StringRef(); };
OS << "Defined atoms:\n"; OS << "Symbols:\n";
for (auto *DA : defined_atoms()) { for (auto *Sym : defined_symbols()) {
OS << " " << format("0x%016" PRIx64, DA->getAddress()) << ": " << *DA OS << " " << format("0x%016" PRIx64, Sym->getAddress()) << ": " << *Sym
<< "\n"; << "\n";
for (auto &E : DA->edges()) { if (Sym->isDefined()) {
for (auto &E : Sym->getBlock().edges()) {
OS << " "; OS << " ";
StringRef EdgeName = (E.getKind() < Edge::FirstRelocation StringRef EdgeName = (E.getKind() < Edge::FirstRelocation
? getGenericEdgeKindName(E.getKind()) ? getGenericEdgeKindName(E.getKind())
: EdgeKindToName(E.getKind())); : EdgeKindToName(E.getKind()));
if (!EdgeName.empty()) if (!EdgeName.empty())
printEdge(OS, *DA, E, EdgeName); printEdge(OS, Sym->getBlock(), E, EdgeName);
else { else {
auto EdgeNumberString = std::to_string(E.getKind()); auto EdgeNumberString = std::to_string(E.getKind());
printEdge(OS, *DA, E, EdgeNumberString); printEdge(OS, Sym->getBlock(), E, EdgeNumberString);
} }
OS << "\n"; OS << "\n";
} }
} }
}
OS << "Absolute atoms:\n"; OS << "Absolute symbols:\n";
for (auto *A : absolute_atoms()) for (auto *Sym : absolute_symbols())
OS << " " << format("0x%016" PRIx64, A->getAddress()) << ": " << *A OS << " " << format("0x%016" PRIx64, Sym->getAddress()) << ": " << *Sym
<< "\n"; << "\n";
OS << "External atoms:\n"; OS << "External symbols:\n";
for (auto *A : external_atoms()) for (auto *Sym : external_symbols())
OS << " " << format("0x%016" PRIx64, A->getAddress()) << ": " << *A OS << " " << format("0x%016" PRIx64, Sym->getAddress()) << ": " << *Sym
<< "\n"; << "\n";
} }
void JITLinkAsyncLookupContinuation::anchor() {}
JITLinkContext::~JITLinkContext() {} JITLinkContext::~JITLinkContext() {}
bool JITLinkContext::shouldAddDefaultTargetPasses(const Triple &TT) const { bool JITLinkContext::shouldAddDefaultTargetPasses(const Triple &TT) const {
return true; return true;
} }
AtomGraphPassFunction JITLinkContext::getMarkLivePass(const Triple &TT) const { LinkGraphPassFunction JITLinkContext::getMarkLivePass(const Triple &TT) const {
return AtomGraphPassFunction(); return LinkGraphPassFunction();
} }
Error JITLinkContext::modifyPassConfig(const Triple &TT, Error JITLinkContext::modifyPassConfig(const Triple &TT,
@ -152,9 +200,9 @@ Error JITLinkContext::modifyPassConfig(const Triple &TT,
return Error::success(); return Error::success();
} }
Error markAllAtomsLive(AtomGraph &G) { Error markAllSymbolsLive(LinkGraph &G) {
for (auto *DA : G.defined_atoms()) for (auto *Sym : G.defined_symbols())
DA->setLive(true); Sym->setLive(true);
return Error::success(); return Error::success();
} }

View File

@ -11,7 +11,6 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "JITLinkGeneric.h" #include "JITLinkGeneric.h"
#include "EHFrameSupportImpl.h"
#include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/BinaryStreamReader.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
@ -25,7 +24,7 @@ JITLinkerBase::~JITLinkerBase() {}
void JITLinkerBase::linkPhase1(std::unique_ptr<JITLinkerBase> Self) { void JITLinkerBase::linkPhase1(std::unique_ptr<JITLinkerBase> Self) {
// Build the atom graph. // Build the link graph.
if (auto GraphOrErr = buildGraph(Ctx->getObjectBuffer())) if (auto GraphOrErr = buildGraph(Ctx->getObjectBuffer()))
G = std::move(*GraphOrErr); G = std::move(*GraphOrErr);
else else
@ -33,33 +32,33 @@ void JITLinkerBase::linkPhase1(std::unique_ptr<JITLinkerBase> Self) {
assert(G && "Graph should have been created by buildGraph above"); assert(G && "Graph should have been created by buildGraph above");
// Prune and optimize the graph. // Prune and optimize the graph.
if (auto Err = runPasses(Passes.PrePrunePasses, *G)) if (auto Err = runPasses(Passes.PrePrunePasses))
return Ctx->notifyFailed(std::move(Err)); return Ctx->notifyFailed(std::move(Err));
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << "Atom graph \"" << G->getName() << "\" pre-pruning:\n"; dbgs() << "Link graph \"" << G->getName() << "\" pre-pruning:\n";
dumpGraph(dbgs()); dumpGraph(dbgs());
}); });
prune(*G); prune(*G);
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << "Atom graph \"" << G->getName() << "\" post-pruning:\n"; dbgs() << "Link graph \"" << G->getName() << "\" post-pruning:\n";
dumpGraph(dbgs()); dumpGraph(dbgs());
}); });
// Run post-pruning passes. // Run post-pruning passes.
if (auto Err = runPasses(Passes.PostPrunePasses, *G)) if (auto Err = runPasses(Passes.PostPrunePasses))
return Ctx->notifyFailed(std::move(Err)); return Ctx->notifyFailed(std::move(Err));
// Sort atoms into segments. // Sort blocks into segments.
layOutAtoms(); auto Layout = layOutBlocks();
// Allocate memory for segments. // Allocate memory for segments.
if (auto Err = allocateSegments(Layout)) if (auto Err = allocateSegments(Layout))
return Ctx->notifyFailed(std::move(Err)); return Ctx->notifyFailed(std::move(Err));
// Notify client that the defined atoms have been assigned addresses. // Notify client that the defined symbols have been assigned addresses.
Ctx->notifyResolved(*G); Ctx->notifyResolved(*G);
auto ExternalSymbols = getExternalSymbolNames(); auto ExternalSymbols = getExternalSymbolNames();
@ -74,42 +73,42 @@ void JITLinkerBase::linkPhase1(std::unique_ptr<JITLinkerBase> Self) {
// [Self=std::move(Self)](Expected<AsyncLookupResult> Result) { // [Self=std::move(Self)](Expected<AsyncLookupResult> Result) {
// Self->linkPhase2(std::move(Self), std::move(Result)); // Self->linkPhase2(std::move(Self), std::move(Result));
// }); // });
//
// FIXME: Use move capture once we have c++14.
auto *TmpCtx = Ctx.get(); auto *TmpCtx = Ctx.get();
auto *UnownedSelf = Self.release(); TmpCtx->lookup(std::move(ExternalSymbols),
auto Phase2Continuation = createLookupContinuation(
[UnownedSelf](Expected<AsyncLookupResult> LookupResult) { [S = std::move(Self), L = std::move(Layout)](
std::unique_ptr<JITLinkerBase> Self(UnownedSelf); Expected<AsyncLookupResult> LookupResult) mutable {
UnownedSelf->linkPhase2(std::move(Self), std::move(LookupResult)); auto &TmpSelf = *S;
}; TmpSelf.linkPhase2(std::move(S), std::move(LookupResult),
TmpCtx->lookup(std::move(ExternalSymbols), std::move(Phase2Continuation)); std::move(L));
}));
} }
void JITLinkerBase::linkPhase2(std::unique_ptr<JITLinkerBase> Self, void JITLinkerBase::linkPhase2(std::unique_ptr<JITLinkerBase> Self,
Expected<AsyncLookupResult> LR) { Expected<AsyncLookupResult> LR,
SegmentLayoutMap Layout) {
// If the lookup failed, bail out. // If the lookup failed, bail out.
if (!LR) if (!LR)
return deallocateAndBailOut(LR.takeError()); return deallocateAndBailOut(LR.takeError());
// Assign addresses to external atoms. // Assign addresses to external addressables.
applyLookupResult(*LR); applyLookupResult(*LR);
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << "Atom graph \"" << G->getName() << "\" before copy-and-fixup:\n"; dbgs() << "Link graph \"" << G->getName() << "\" before copy-and-fixup:\n";
dumpGraph(dbgs()); dumpGraph(dbgs());
}); });
// Copy atom content to working memory and fix up. // Copy block content to working memory and fix up.
if (auto Err = copyAndFixUpAllAtoms(Layout, *Alloc)) if (auto Err = copyAndFixUpBlocks(Layout, *Alloc))
return deallocateAndBailOut(std::move(Err)); return deallocateAndBailOut(std::move(Err));
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << "Atom graph \"" << G->getName() << "\" after copy-and-fixup:\n"; dbgs() << "Link graph \"" << G->getName() << "\" after copy-and-fixup:\n";
dumpGraph(dbgs()); dumpGraph(dbgs());
}); });
if (auto Err = runPasses(Passes.PostFixupPasses, *G)) if (auto Err = runPasses(Passes.PostFixupPasses))
return deallocateAndBailOut(std::move(Err)); return deallocateAndBailOut(std::move(Err));
// FIXME: Use move capture once we have c++14. // FIXME: Use move capture once we have c++14.
@ -128,82 +127,38 @@ void JITLinkerBase::linkPhase3(std::unique_ptr<JITLinkerBase> Self, Error Err) {
Ctx->notifyFinalized(std::move(Alloc)); Ctx->notifyFinalized(std::move(Alloc));
} }
Error JITLinkerBase::runPasses(AtomGraphPassList &Passes, AtomGraph &G) { Error JITLinkerBase::runPasses(LinkGraphPassList &Passes) {
for (auto &P : Passes) for (auto &P : Passes)
if (auto Err = P(G)) if (auto Err = P(*G))
return Err; return Err;
return Error::success(); return Error::success();
} }
void JITLinkerBase::layOutAtoms() { JITLinkerBase::SegmentLayoutMap JITLinkerBase::layOutBlocks() {
// Group sections by protections, and whether or not they're zero-fill.
for (auto &S : G->sections()) {
// Skip empty sections. SegmentLayoutMap Layout;
if (S.atoms_empty())
continue;
auto &SL = Layout[S.getProtectionFlags()]; /// Partition blocks based on permissions and content vs. zero-fill.
if (S.isZeroFill()) for (auto *B : G->blocks()) {
SL.ZeroFillSections.push_back(SegmentLayout::SectionLayout(S)); auto &SegLists = Layout[B->getSection().getProtectionFlags()];
if (!B->isZeroFill())
SegLists.ContentBlocks.push_back(B);
else else
SL.ContentSections.push_back(SegmentLayout::SectionLayout(S)); SegLists.ZeroFillBlocks.push_back(B);
} }
// Sort sections within the layout by ordinal. /// Sort blocks within each list.
{ for (auto &KV : Layout) {
auto CompareByOrdinal = [](const SegmentLayout::SectionLayout &LHS,
const SegmentLayout::SectionLayout &RHS) { auto CompareBlocks = [](const Block *LHS, const Block *RHS) {
return LHS.S->getSectionOrdinal() < RHS.S->getSectionOrdinal(); if (LHS->getSection().getOrdinal() != RHS->getSection().getOrdinal())
return LHS->getSection().getOrdinal() < RHS->getSection().getOrdinal();
return LHS->getOrdinal() < RHS->getOrdinal();
}; };
for (auto &KV : Layout) {
auto &SL = KV.second;
std::sort(SL.ContentSections.begin(), SL.ContentSections.end(),
CompareByOrdinal);
std::sort(SL.ZeroFillSections.begin(), SL.ZeroFillSections.end(),
CompareByOrdinal);
}
}
// Add atoms to the sections. auto &SegLists = KV.second;
for (auto &KV : Layout) { llvm::sort(SegLists.ContentBlocks, CompareBlocks);
auto &SL = KV.second; llvm::sort(SegLists.ZeroFillBlocks, CompareBlocks);
for (auto *SIList : {&SL.ContentSections, &SL.ZeroFillSections}) {
for (auto &SI : *SIList) {
// First build the set of layout-heads (i.e. "heads" of layout-next
// chains) by copying the section atoms, then eliminating any that
// appear as layout-next targets.
DenseSet<DefinedAtom *> LayoutHeads;
for (auto *DA : SI.S->atoms())
LayoutHeads.insert(DA);
for (auto *DA : SI.S->atoms())
if (DA->hasLayoutNext())
LayoutHeads.erase(&DA->getLayoutNext());
// Next, sort the layout heads by address order.
std::vector<DefinedAtom *> OrderedLayoutHeads;
OrderedLayoutHeads.reserve(LayoutHeads.size());
for (auto *DA : LayoutHeads)
OrderedLayoutHeads.push_back(DA);
// Now sort the list of layout heads by address.
std::sort(OrderedLayoutHeads.begin(), OrderedLayoutHeads.end(),
[](const DefinedAtom *LHS, const DefinedAtom *RHS) {
return LHS->getAddress() < RHS->getAddress();
});
// Now populate the SI.Atoms field by appending each of the chains.
for (auto *DA : OrderedLayoutHeads) {
SI.Atoms.push_back(DA);
while (DA->hasLayoutNext()) {
auto &Next = DA->getLayoutNext();
SI.Atoms.push_back(&Next);
DA = &Next;
}
}
}
}
} }
LLVM_DEBUG({ LLVM_DEBUG({
@ -213,18 +168,16 @@ void JITLinkerBase::layOutAtoms() {
<< static_cast<sys::Memory::ProtectionFlags>(KV.first) << ":\n"; << static_cast<sys::Memory::ProtectionFlags>(KV.first) << ":\n";
auto &SL = KV.second; auto &SL = KV.second;
for (auto &SIEntry : for (auto &SIEntry :
{std::make_pair(&SL.ContentSections, "content sections"), {std::make_pair(&SL.ContentBlocks, "content block"),
std::make_pair(&SL.ZeroFillSections, "zero-fill sections")}) { std::make_pair(&SL.ZeroFillBlocks, "zero-fill block")}) {
auto &SIList = *SIEntry.first;
dbgs() << " " << SIEntry.second << ":\n"; dbgs() << " " << SIEntry.second << ":\n";
for (auto &SI : SIList) { for (auto *B : *SIEntry.first)
dbgs() << " " << SI.S->getName() << ":\n"; dbgs() << " " << *B << "\n";
for (auto *DA : SI.Atoms)
dbgs() << " " << *DA << "\n";
}
} }
} }
}); });
return Layout;
} }
Error JITLinkerBase::allocateSegments(const SegmentLayoutMap &Layout) { Error JITLinkerBase::allocateSegments(const SegmentLayoutMap &Layout) {
@ -234,61 +187,36 @@ Error JITLinkerBase::allocateSegments(const SegmentLayoutMap &Layout) {
JITLinkMemoryManager::SegmentsRequestMap Segments; JITLinkMemoryManager::SegmentsRequestMap Segments;
for (auto &KV : Layout) { for (auto &KV : Layout) {
auto &Prot = KV.first; auto &Prot = KV.first;
auto &SegLayout = KV.second; auto &SegLists = KV.second;
uint64_t SegAlign = 1;
// Calculate segment content size. // Calculate segment content size.
size_t SegContentSize = 0; size_t SegContentSize = 0;
uint32_t SegContentAlign = 1; for (auto *B : SegLists.ContentBlocks) {
for (auto &SI : SegLayout.ContentSections) { SegAlign = std::max(SegAlign, B->getAlignment());
assert(!SI.S->atoms_empty() && "Sections in layout must not be empty"); SegContentSize = alignToBlock(SegContentSize, *B);
assert(!SI.Atoms.empty() && "Section layouts must not be empty"); SegContentSize += B->getSize();
// Bump to section alignment before processing atoms.
SegContentSize = alignTo(SegContentSize, SI.S->getAlignment());
SegContentAlign = std::max(SegContentAlign, SI.S->getAlignment());
for (auto *DA : SI.Atoms) {
SegContentSize = alignTo(SegContentSize, DA->getAlignment());
SegContentSize += DA->getSize();
SegContentAlign = std::max(SegContentAlign, DA->getAlignment());
}
} }
// Calculate segment zero-fill size. uint64_t SegZeroFillStart = SegContentSize;
uint64_t SegZeroFillSize = 0; uint64_t SegZeroFillEnd = SegZeroFillStart;
uint32_t SegZeroFillAlign = 1;
for (auto &SI : SegLayout.ZeroFillSections) { for (auto *B : SegLists.ZeroFillBlocks) {
assert(!SI.S->atoms_empty() && "Sections in layout must not be empty"); SegAlign = std::max(SegAlign, B->getAlignment());
assert(!SI.Atoms.empty() && "Section layouts must not be empty"); SegZeroFillEnd = alignToBlock(SegZeroFillEnd, *B);
SegZeroFillEnd += B->getSize();
// Bump to section alignment before processing atoms.
SegZeroFillSize = alignTo(SegZeroFillSize, SI.S->getAlignment());
SegZeroFillAlign = std::max(SegZeroFillAlign, SI.S->getAlignment());
for (auto *DA : SI.Atoms) {
SegZeroFillSize = alignTo(SegZeroFillSize, DA->getAlignment());
SegZeroFillSize += DA->getSize();
SegZeroFillAlign = std::max(SegZeroFillAlign, SI.S->getAlignment());
}
} }
assert(isPowerOf2_32(SegContentAlign) && Segments[Prot] = {SegAlign, SegContentSize,
"Expected content alignment to be power of 2"); SegZeroFillEnd - SegZeroFillStart};
assert(isPowerOf2_32(SegZeroFillAlign) &&
"Expected zero-fill alignment to be power of 2");
// Round content alignment up to segment alignment.
SegContentAlign = std::max(SegContentAlign, SegZeroFillAlign);
Segments[Prot] = {SegContentSize, SegContentAlign, SegZeroFillSize,
SegZeroFillAlign};
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << (&KV == &*Layout.begin() ? "" : "; ") dbgs() << (&KV == &*Layout.begin() ? "" : "; ")
<< static_cast<sys::Memory::ProtectionFlags>(Prot) << ": " << static_cast<sys::Memory::ProtectionFlags>(Prot)
<< SegContentSize << " content bytes (alignment " << ": alignment = " << SegAlign
<< SegContentAlign << ") + " << SegZeroFillSize << ", content size = " << SegContentSize
<< " zero-fill bytes (alignment " << SegZeroFillAlign << ")"; << ", zero-fill size = " << (SegZeroFillEnd - SegZeroFillStart);
}); });
} }
LLVM_DEBUG(dbgs() << " }\n"); LLVM_DEBUG(dbgs() << " }\n");
@ -307,22 +235,19 @@ Error JITLinkerBase::allocateSegments(const SegmentLayoutMap &Layout) {
} }
}); });
// Update atom target addresses. // Update block target addresses.
for (auto &KV : Layout) { for (auto &KV : Layout) {
auto &Prot = KV.first; auto &Prot = KV.first;
auto &SL = KV.second; auto &SL = KV.second;
JITTargetAddress AtomTargetAddr = JITTargetAddress NextBlockAddr =
Alloc->getTargetMemory(static_cast<sys::Memory::ProtectionFlags>(Prot)); Alloc->getTargetMemory(static_cast<sys::Memory::ProtectionFlags>(Prot));
for (auto *SIList : {&SL.ContentSections, &SL.ZeroFillSections}) for (auto *SIList : {&SL.ContentBlocks, &SL.ZeroFillBlocks})
for (auto &SI : *SIList) { for (auto *B : *SIList) {
AtomTargetAddr = alignTo(AtomTargetAddr, SI.S->getAlignment()); NextBlockAddr = alignToBlock(NextBlockAddr, *B);
for (auto *DA : SI.Atoms) { B->setAddress(NextBlockAddr);
AtomTargetAddr = alignTo(AtomTargetAddr, DA->getAlignment()); NextBlockAddr += B->getSize();
DA->setAddress(AtomTargetAddr);
AtomTargetAddr += DA->getSize();
}
} }
} }
@ -330,34 +255,35 @@ Error JITLinkerBase::allocateSegments(const SegmentLayoutMap &Layout) {
} }
DenseSet<StringRef> JITLinkerBase::getExternalSymbolNames() const { DenseSet<StringRef> JITLinkerBase::getExternalSymbolNames() const {
// Identify unresolved external atoms. // Identify unresolved external symbols.
DenseSet<StringRef> UnresolvedExternals; DenseSet<StringRef> UnresolvedExternals;
for (auto *DA : G->external_atoms()) { for (auto *Sym : G->external_symbols()) {
assert(DA->getAddress() == 0 && assert(Sym->getAddress() == 0 &&
"External has already been assigned an address"); "External has already been assigned an address");
assert(DA->getName() != StringRef() && DA->getName() != "" && assert(Sym->getName() != StringRef() && Sym->getName() != "" &&
"Externals must be named"); "Externals must be named");
UnresolvedExternals.insert(DA->getName()); UnresolvedExternals.insert(Sym->getName());
} }
return UnresolvedExternals; return UnresolvedExternals;
} }
void JITLinkerBase::applyLookupResult(AsyncLookupResult Result) { void JITLinkerBase::applyLookupResult(AsyncLookupResult Result) {
for (auto &KV : Result) { for (auto *Sym : G->external_symbols()) {
Atom &A = G->getAtomByName(KV.first); assert(Sym->getAddress() == 0 && "Symbol already resolved");
assert(A.getAddress() == 0 && "Atom already resolved"); assert(!Sym->isDefined() && "Symbol being resolved is already defined");
A.setAddress(KV.second.getAddress()); assert(Result.count(Sym->getName()) && "Missing resolution for symbol");
Sym->getAddressable().setAddress(Result[Sym->getName()].getAddress());
} }
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << "Externals after applying lookup result:\n"; dbgs() << "Externals after applying lookup result:\n";
for (auto *A : G->external_atoms()) for (auto *Sym : G->external_symbols())
dbgs() << " " << A->getName() << ": " dbgs() << " " << Sym->getName() << ": "
<< formatv("{0:x16}", A->getAddress()) << "\n"; << formatv("{0:x16}", Sym->getAddress()) << "\n";
}); });
assert(llvm::all_of(G->external_atoms(), assert(llvm::all_of(G->external_symbols(),
[](Atom *A) { return A->getAddress() != 0; }) && [](Symbol *Sym) { return Sym->getAddress() != 0; }) &&
"All atoms should have been resolved by this point"); "All symbols should have been resolved by this point");
} }
void JITLinkerBase::deallocateAndBailOut(Error Err) { void JITLinkerBase::deallocateAndBailOut(Error Err) {
@ -371,96 +297,60 @@ void JITLinkerBase::dumpGraph(raw_ostream &OS) {
G->dump(dbgs(), [this](Edge::Kind K) { return getEdgeKindName(K); }); G->dump(dbgs(), [this](Edge::Kind K) { return getEdgeKindName(K); });
} }
void prune(AtomGraph &G) { void prune(LinkGraph &G) {
std::vector<DefinedAtom *> Worklist; std::vector<Symbol *> Worklist;
DenseMap<DefinedAtom *, std::vector<Edge *>> EdgesToUpdate; DenseSet<Block *> VisitedBlocks;
// Build the initial worklist from all atoms initially live. // Build the initial worklist from all symbols initially live.
for (auto *DA : G.defined_atoms()) { for (auto *Sym : G.defined_symbols())
if (!DA->isLive() || DA->shouldDiscard()) if (Sym->isLive())
continue; Worklist.push_back(Sym);
for (auto &E : DA->edges()) { // Propagate live flags to all symbols reachable from the initial live set.
if (!E.getTarget().isDefined())
continue;
auto &EDT = static_cast<DefinedAtom &>(E.getTarget());
if (EDT.shouldDiscard())
EdgesToUpdate[&EDT].push_back(&E);
else if (E.isKeepAlive() && !EDT.isLive())
Worklist.push_back(&EDT);
}
}
// Propagate live flags to all atoms reachable from the initial live set.
while (!Worklist.empty()) { while (!Worklist.empty()) {
DefinedAtom &NextLive = *Worklist.back(); auto *Sym = Worklist.back();
Worklist.pop_back(); Worklist.pop_back();
assert(!NextLive.shouldDiscard() && auto &B = Sym->getBlock();
"should-discard nodes should never make it into the worklist");
// If this atom has already been marked as live, or is marked to be // Skip addressables that we've visited before.
// discarded, then skip it. if (VisitedBlocks.count(&B))
if (NextLive.isLive())
continue; continue;
// Otherwise set it as live and add any non-live atoms that it points to VisitedBlocks.insert(&B);
// to the worklist.
NextLive.setLive(true);
for (auto &E : NextLive.edges()) { for (auto &E : Sym->getBlock().edges()) {
if (!E.getTarget().isDefined()) if (E.getTarget().isDefined() && !E.getTarget().isLive()) {
continue; E.getTarget().setLive(true);
Worklist.push_back(&E.getTarget());
auto &EDT = static_cast<DefinedAtom &>(E.getTarget()); }
if (EDT.shouldDiscard())
EdgesToUpdate[&EDT].push_back(&E);
else if (E.isKeepAlive() && !EDT.isLive())
Worklist.push_back(&EDT);
} }
} }
// Collect atoms to remove, then remove them from the graph. // Collect all the symbols to remove, then remove them.
std::vector<DefinedAtom *> AtomsToRemove;
for (auto *DA : G.defined_atoms())
if (DA->shouldDiscard() || !DA->isLive())
AtomsToRemove.push_back(DA);
LLVM_DEBUG(dbgs() << "Pruning atoms:\n");
for (auto *DA : AtomsToRemove) {
LLVM_DEBUG(dbgs() << " " << *DA << "... ");
// Check whether we need to replace this atom with an external atom.
//
// We replace if all of the following hold:
// (1) The atom is marked should-discard,
// (2) it has live edges (i.e. edges from live atoms) pointing to it.
//
// Otherwise we simply delete the atom.
G.removeDefinedAtom(*DA);
auto EdgesToUpdateItr = EdgesToUpdate.find(DA);
if (EdgesToUpdateItr != EdgesToUpdate.end()) {
auto &ExternalReplacement = G.addExternalAtom(DA->getName());
for (auto *EdgeToUpdate : EdgesToUpdateItr->second)
EdgeToUpdate->setTarget(ExternalReplacement);
LLVM_DEBUG(dbgs() << "replaced with " << ExternalReplacement << "\n");
} else
LLVM_DEBUG(dbgs() << "deleted\n");
}
// Finally, discard any absolute symbols that were marked should-discard.
{ {
std::vector<Atom *> AbsoluteAtomsToRemove; LLVM_DEBUG(dbgs() << "Dead-stripping symbols:\n");
for (auto *A : G.absolute_atoms()) std::vector<Symbol *> SymbolsToRemove;
if (A->shouldDiscard() || A->isLive()) for (auto *Sym : G.defined_symbols())
AbsoluteAtomsToRemove.push_back(A); if (!Sym->isLive())
for (auto *A : AbsoluteAtomsToRemove) SymbolsToRemove.push_back(Sym);
G.removeAbsoluteAtom(*A); for (auto *Sym : SymbolsToRemove) {
LLVM_DEBUG(dbgs() << " " << *Sym << "...\n");
G.removeDefinedSymbol(*Sym);
}
}
// Delete any unused blocks.
{
LLVM_DEBUG(dbgs() << "Dead-stripping blocks:\n");
std::vector<Block *> BlocksToRemove;
for (auto *B : G.blocks())
if (!VisitedBlocks.count(B))
BlocksToRemove.push_back(B);
for (auto *B : BlocksToRemove) {
LLVM_DEBUG(dbgs() << " " << *B << "...\n");
G.removeBlock(*B);
}
} }
} }

View File

@ -41,39 +41,32 @@ public:
protected: protected:
struct SegmentLayout { struct SegmentLayout {
using SectionAtomsList = std::vector<DefinedAtom *>; using BlocksList = std::vector<Block *>;
struct SectionLayout {
SectionLayout(Section &S) : S(&S) {}
Section *S; BlocksList ContentBlocks;
SectionAtomsList Atoms; BlocksList ZeroFillBlocks;
};
using SectionLayoutList = std::vector<SectionLayout>;
SectionLayoutList ContentSections;
SectionLayoutList ZeroFillSections;
}; };
using SegmentLayoutMap = DenseMap<unsigned, SegmentLayout>; using SegmentLayoutMap = DenseMap<unsigned, SegmentLayout>;
// Phase 1: // Phase 1:
// 1.1: Build atom graph // 1.1: Build link graph
// 1.2: Run pre-prune passes // 1.2: Run pre-prune passes
// 1.2: Prune graph // 1.2: Prune graph
// 1.3: Run post-prune passes // 1.3: Run post-prune passes
// 1.4: Sort atoms into segments // 1.4: Sort blocks into segments
// 1.5: Allocate segment memory // 1.5: Allocate segment memory
// 1.6: Identify externals and make an async call to resolve function // 1.6: Identify externals and make an async call to resolve function
void linkPhase1(std::unique_ptr<JITLinkerBase> Self); void linkPhase1(std::unique_ptr<JITLinkerBase> Self);
// Phase 2: // Phase 2:
// 2.1: Apply resolution results // 2.1: Apply resolution results
// 2.2: Fix up atom contents // 2.2: Fix up block contents
// 2.3: Call OnResolved callback // 2.3: Call OnResolved callback
// 2.3: Make an async call to transfer and finalize memory. // 2.3: Make an async call to transfer and finalize memory.
void linkPhase2(std::unique_ptr<JITLinkerBase> Self, void linkPhase2(std::unique_ptr<JITLinkerBase> Self,
Expected<AsyncLookupResult> LookupResult); Expected<AsyncLookupResult> LookupResult,
SegmentLayoutMap Layout);
// Phase 3: // Phase 3:
// 3.1: Call OnFinalized callback, handing off allocation. // 3.1: Call OnFinalized callback, handing off allocation.
@ -81,24 +74,37 @@ protected:
// Build a graph from the given object buffer. // Build a graph from the given object buffer.
// To be implemented by the client. // To be implemented by the client.
virtual Expected<std::unique_ptr<AtomGraph>> virtual Expected<std::unique_ptr<LinkGraph>>
buildGraph(MemoryBufferRef ObjBuffer) = 0; buildGraph(MemoryBufferRef ObjBuffer) = 0;
// For debug dumping of the atom graph. // For debug dumping of the link graph.
virtual StringRef getEdgeKindName(Edge::Kind K) const = 0; virtual StringRef getEdgeKindName(Edge::Kind K) const = 0;
// Alight a JITTargetAddress to conform with block alignment requirements.
static JITTargetAddress alignToBlock(JITTargetAddress Addr, Block &B) {
uint64_t Delta = (B.getAlignmentOffset() - Addr) % B.getAlignment();
return Addr + Delta;
}
// Alight a pointer to conform with block alignment requirements.
static char *alignToBlock(char *P, Block &B) {
uint64_t PAddr = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(P));
uint64_t Delta = (B.getAlignmentOffset() - PAddr) % B.getAlignment();
return P + Delta;
}
private: private:
// Run all passes in the given pass list, bailing out immediately if any pass // Run all passes in the given pass list, bailing out immediately if any pass
// returns an error. // returns an error.
Error runPasses(AtomGraphPassList &Passes, AtomGraph &G); Error runPasses(LinkGraphPassList &Passes);
// Copy atom contents and apply relocations. // Copy block contents and apply relocations.
// Implemented in JITLinker. // Implemented in JITLinker.
virtual Error virtual Error
copyAndFixUpAllAtoms(const SegmentLayoutMap &Layout, copyAndFixUpBlocks(const SegmentLayoutMap &Layout,
JITLinkMemoryManager::Allocation &Alloc) const = 0; JITLinkMemoryManager::Allocation &Alloc) const = 0;
void layOutAtoms(); SegmentLayoutMap layOutBlocks();
Error allocateSegments(const SegmentLayoutMap &Layout); Error allocateSegments(const SegmentLayoutMap &Layout);
DenseSet<StringRef> getExternalSymbolNames() const; DenseSet<StringRef> getExternalSymbolNames() const;
void applyLookupResult(AsyncLookupResult LR); void applyLookupResult(AsyncLookupResult LR);
@ -108,8 +114,7 @@ private:
std::unique_ptr<JITLinkContext> Ctx; std::unique_ptr<JITLinkContext> Ctx;
PassConfiguration Passes; PassConfiguration Passes;
std::unique_ptr<AtomGraph> G; std::unique_ptr<LinkGraph> G;
SegmentLayoutMap Layout;
std::unique_ptr<JITLinkMemoryManager::Allocation> Alloc; std::unique_ptr<JITLinkMemoryManager::Allocation> Alloc;
}; };
@ -140,17 +145,17 @@ private:
} }
Error Error
copyAndFixUpAllAtoms(const SegmentLayoutMap &Layout, copyAndFixUpBlocks(const SegmentLayoutMap &Layout,
JITLinkMemoryManager::Allocation &Alloc) const override { JITLinkMemoryManager::Allocation &Alloc) const override {
LLVM_DEBUG(dbgs() << "Copying and fixing up atoms:\n"); LLVM_DEBUG(dbgs() << "Copying and fixing up blocks:\n");
for (auto &KV : Layout) { for (auto &KV : Layout) {
auto &Prot = KV.first; auto &Prot = KV.first;
auto &SegLayout = KV.second; auto &SegLayout = KV.second;
auto SegMem = Alloc.getWorkingMemory( auto SegMem = Alloc.getWorkingMemory(
static_cast<sys::Memory::ProtectionFlags>(Prot)); static_cast<sys::Memory::ProtectionFlags>(Prot));
char *LastAtomEnd = SegMem.data(); char *LastBlockEnd = SegMem.data();
char *AtomDataPtr = LastAtomEnd; char *BlockDataPtr = LastBlockEnd;
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << " Processing segment " dbgs() << " Processing segment "
@ -160,93 +165,79 @@ private:
<< " ]\n Processing content sections:\n"; << " ]\n Processing content sections:\n";
}); });
for (auto &SI : SegLayout.ContentSections) { for (auto *B : SegLayout.ContentBlocks) {
LLVM_DEBUG(dbgs() << " " << SI.S->getName() << ":\n"); LLVM_DEBUG(dbgs() << " " << *B << ":\n");
AtomDataPtr += alignmentAdjustment(AtomDataPtr, SI.S->getAlignment()); // Pad to alignment/alignment-offset.
BlockDataPtr = alignToBlock(BlockDataPtr, *B);
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << " Bumped atom pointer to " << (const void *)AtomDataPtr dbgs() << " Bumped block pointer to "
<< " to meet section alignment " << (const void *)BlockDataPtr << " to meet block alignment "
<< " of " << SI.S->getAlignment() << "\n"; << B->getAlignment() << " and alignment offset "
}); << B->getAlignmentOffset() << "\n";
for (auto *DA : SI.Atoms) {
// Align.
AtomDataPtr += alignmentAdjustment(AtomDataPtr, DA->getAlignment());
LLVM_DEBUG({
dbgs() << " Bumped atom pointer to "
<< (const void *)AtomDataPtr << " to meet alignment of "
<< DA->getAlignment() << "\n";
}); });
// Zero pad up to alignment. // Zero pad up to alignment.
LLVM_DEBUG({ LLVM_DEBUG({
if (LastAtomEnd != AtomDataPtr) if (LastBlockEnd != BlockDataPtr)
dbgs() << " Zero padding from " << (const void *)LastAtomEnd dbgs() << " Zero padding from " << (const void *)LastBlockEnd
<< " to " << (const void *)AtomDataPtr << "\n"; << " to " << (const void *)BlockDataPtr << "\n";
}); });
while (LastAtomEnd != AtomDataPtr)
*LastAtomEnd++ = 0;
// Copy initial atom content. while (LastBlockEnd != BlockDataPtr)
*LastBlockEnd++ = 0;
// Copy initial block content.
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << " Copying atom " << *DA << " content, " dbgs() << " Copying block " << *B << " content, "
<< DA->getContent().size() << " bytes, from " << B->getContent().size() << " bytes, from "
<< (const void *)DA->getContent().data() << " to " << (const void *)B->getContent().data() << " to "
<< (const void *)AtomDataPtr << "\n"; << (const void *)BlockDataPtr << "\n";
}); });
memcpy(AtomDataPtr, DA->getContent().data(), DA->getContent().size()); memcpy(BlockDataPtr, B->getContent().data(), B->getContent().size());
// Copy atom data and apply fixups. // Copy Block data and apply fixups.
LLVM_DEBUG(dbgs() << " Applying fixups.\n"); LLVM_DEBUG(dbgs() << " Applying fixups.\n");
for (auto &E : DA->edges()) { for (auto &E : B->edges()) {
// Skip non-relocation edges. // Skip non-relocation edges.
if (!E.isRelocation()) if (!E.isRelocation())
continue; continue;
// Dispatch to LinkerImpl for fixup. // Dispatch to LinkerImpl for fixup.
if (auto Err = impl().applyFixup(*DA, E, AtomDataPtr)) if (auto Err = impl().applyFixup(*B, E, BlockDataPtr))
return Err; return Err;
} }
// Point the atom's content to the fixed up buffer. // Point the block's content to the fixed up buffer.
DA->setContent(StringRef(AtomDataPtr, DA->getContent().size())); B->setContent(StringRef(BlockDataPtr, B->getContent().size()));
// Update atom end pointer. // Update block end pointer.
LastAtomEnd = AtomDataPtr + DA->getContent().size(); LastBlockEnd = BlockDataPtr + B->getContent().size();
AtomDataPtr = LastAtomEnd; BlockDataPtr = LastBlockEnd;
}
} }
// Zero pad the rest of the segment. // Zero pad the rest of the segment.
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << " Zero padding end of segment from " dbgs() << " Zero padding end of segment from "
<< (const void *)LastAtomEnd << " to " << (const void *)LastBlockEnd << " to "
<< (const void *)((char *)SegMem.data() + SegMem.size()) << "\n"; << (const void *)((char *)SegMem.data() + SegMem.size()) << "\n";
}); });
while (LastAtomEnd != SegMem.data() + SegMem.size()) while (LastBlockEnd != SegMem.data() + SegMem.size())
*LastAtomEnd++ = 0; *LastBlockEnd++ = 0;
} }
return Error::success(); return Error::success();
} }
}; };
/// Dead strips and replaces discarded definitions with external atoms. /// Removes dead symbols/blocks/addressables.
/// ///
/// Finds the set of nodes reachable from any node initially marked live /// Finds the set of symbols and addressables reachable from any symbol
/// (nodes marked should-discard are treated as not live, even if they are /// initially marked live. All symbols/addressables not marked live at the end
/// reachable). All nodes not marked as live at the end of this process, /// of this process are removed.
/// are deleted. Nodes that are live, but marked should-discard are replaced void prune(LinkGraph &G);
/// with external atoms and all edges to them are re-written.
void prune(AtomGraph &G);
Error addEHFrame(AtomGraph &G, Section &EHFrameSection,
StringRef EHFrameContent, JITTargetAddress EHFrameAddress,
Edge::Kind FDEToCIERelocKind, Edge::Kind FDEToTargetRelocKind);
} // end namespace jitlink } // end namespace jitlink
} // end namespace llvm } // end namespace llvm

View File

@ -61,6 +61,10 @@ InProcessMemoryManager::allocate(const SegmentsRequestMap &Request) {
AllocationMap SegBlocks; AllocationMap SegBlocks;
}; };
if (!isPowerOf2_64((uint64_t)sys::Process::getPageSizeEstimate()))
return make_error<StringError>("Page size is not a power of 2",
inconvertibleErrorCode());
AllocationMap Blocks; AllocationMap Blocks;
const sys::Memory::ProtectionFlags ReadWrite = const sys::Memory::ProtectionFlags ReadWrite =
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ | static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
@ -69,19 +73,12 @@ InProcessMemoryManager::allocate(const SegmentsRequestMap &Request) {
for (auto &KV : Request) { for (auto &KV : Request) {
auto &Seg = KV.second; auto &Seg = KV.second;
if (Seg.getContentAlignment() > sys::Process::getPageSizeEstimate()) if (Seg.getAlignment() > sys::Process::getPageSizeEstimate())
return make_error<StringError>("Cannot request higher than page " return make_error<StringError>("Cannot request higher than page "
"alignment", "alignment",
inconvertibleErrorCode()); inconvertibleErrorCode());
if (sys::Process::getPageSizeEstimate() % Seg.getContentAlignment() != 0) uint64_t SegmentSize = Seg.getContentSize() + Seg.getZeroFillSize();
return make_error<StringError>("Page size is not a multiple of "
"alignment",
inconvertibleErrorCode());
uint64_t ZeroFillStart =
alignTo(Seg.getContentSize(), Seg.getZeroFillAlignment());
uint64_t SegmentSize = ZeroFillStart + Seg.getZeroFillSize();
std::error_code EC; std::error_code EC;
auto SegMem = auto SegMem =
@ -91,7 +88,7 @@ InProcessMemoryManager::allocate(const SegmentsRequestMap &Request) {
return errorCodeToError(EC); return errorCodeToError(EC);
// Zero out the zero-fill memory. // Zero out the zero-fill memory.
memset(static_cast<char *>(SegMem.base()) + ZeroFillStart, 0, memset(static_cast<char *>(SegMem.base()) + Seg.getContentSize(), 0,
Seg.getZeroFillSize()); Seg.getZeroFillSize());
// Record the block for this segment. // Record the block for this segment.

View File

@ -1,412 +0,0 @@
//=--------- MachOAtomGraphBuilder.cpp - MachO AtomGraph builder ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Generic MachO AtomGraph buliding code.
//
//===----------------------------------------------------------------------===//
#include "MachOAtomGraphBuilder.h"
#define DEBUG_TYPE "jitlink"
namespace llvm {
namespace jitlink {
MachOAtomGraphBuilder::~MachOAtomGraphBuilder() {}
Expected<std::unique_ptr<AtomGraph>> MachOAtomGraphBuilder::buildGraph() {
if (auto Err = parseSections())
return std::move(Err);
if (auto Err = addAtoms())
return std::move(Err);
if (auto Err = addRelocations())
return std::move(Err);
return std::move(G);
}
MachOAtomGraphBuilder::MachOAtomGraphBuilder(const object::MachOObjectFile &Obj)
: Obj(Obj),
G(std::make_unique<AtomGraph>(Obj.getFileName(), getPointerSize(Obj),
getEndianness(Obj))) {}
void MachOAtomGraphBuilder::addCustomAtomizer(StringRef SectionName,
CustomAtomizeFunction Atomizer) {
assert(!CustomAtomizeFunctions.count(SectionName) &&
"Custom atomizer for this section already exists");
CustomAtomizeFunctions[SectionName] = std::move(Atomizer);
}
bool MachOAtomGraphBuilder::areLayoutLocked(const Atom &A, const Atom &B) {
// If these atoms are the same then they're trivially "locked".
if (&A == &B)
return true;
// If A and B are different, check whether either is undefined. (in which
// case they are not locked).
if (!A.isDefined() || !B.isDefined())
return false;
// A and B are different, but they're both defined atoms. We need to check
// whether they're part of the same alt_entry chain.
auto &DA = static_cast<const DefinedAtom &>(A);
auto &DB = static_cast<const DefinedAtom &>(B);
auto AStartItr = AltEntryStarts.find(&DA);
if (AStartItr == AltEntryStarts.end()) // If A is not in a chain bail out.
return false;
auto BStartItr = AltEntryStarts.find(&DB);
if (BStartItr == AltEntryStarts.end()) // If B is not in a chain bail out.
return false;
// A and B are layout locked if they're in the same chain.
return AStartItr->second == BStartItr->second;
}
unsigned
MachOAtomGraphBuilder::getPointerSize(const object::MachOObjectFile &Obj) {
return Obj.is64Bit() ? 8 : 4;
}
support::endianness
MachOAtomGraphBuilder::getEndianness(const object::MachOObjectFile &Obj) {
return Obj.isLittleEndian() ? support::little : support::big;
}
MachOAtomGraphBuilder::MachOSection &MachOAtomGraphBuilder::getCommonSection() {
if (!CommonSymbolsSection) {
auto Prot = static_cast<sys::Memory::ProtectionFlags>(
sys::Memory::MF_READ | sys::Memory::MF_WRITE);
auto &GenericSection = G->createSection("<common>", 1, Prot, true);
CommonSymbolsSection = MachOSection(GenericSection);
}
return *CommonSymbolsSection;
}
Error MachOAtomGraphBuilder::parseSections() {
for (auto &SecRef : Obj.sections()) {
assert((SecRef.getAlignment() <= std::numeric_limits<uint32_t>::max()) &&
"Section alignment does not fit in 32 bits");
Expected<StringRef> NameOrErr = SecRef.getName();
if (!NameOrErr)
return NameOrErr.takeError();
StringRef Name = *NameOrErr;
unsigned SectionIndex = SecRef.getIndex() + 1;
uint32_t Align = SecRef.getAlignment();
if (!isPowerOf2_32(Align))
return make_error<JITLinkError>("Section " + Name +
" has non-power-of-2 "
"alignment");
// FIXME: Get real section permissions
// How, exactly, on MachO?
sys::Memory::ProtectionFlags Prot;
if (SecRef.isText())
Prot = static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_EXEC);
else
Prot = static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_WRITE);
auto &GenericSection = G->createSection(Name, Align, Prot, SecRef.isBSS());
LLVM_DEBUG({
dbgs() << "Adding section " << Name << ": "
<< format("0x%016" PRIx64, SecRef.getAddress())
<< ", align: " << SecRef.getAlignment() << "\n";
});
assert(!Sections.count(SectionIndex) && "Section index already in use");
auto &MachOSec =
Sections
.try_emplace(SectionIndex, GenericSection, SecRef.getAddress(),
SecRef.getAlignment())
.first->second;
if (!SecRef.isVirtual()) {
// If this section has content then record it.
Expected<StringRef> Content = SecRef.getContents();
if (!Content)
return Content.takeError();
if (Content->size() != SecRef.getSize())
return make_error<JITLinkError>("Section content size does not match "
"declared size for " +
Name);
MachOSec.setContent(*Content);
} else {
// If this is a zero-fill section then just record the size.
MachOSec.setZeroFill(SecRef.getSize());
}
uint32_t SectionFlags =
Obj.is64Bit() ? Obj.getSection64(SecRef.getRawDataRefImpl()).flags
: Obj.getSection(SecRef.getRawDataRefImpl()).flags;
MachOSec.setNoDeadStrip(SectionFlags & MachO::S_ATTR_NO_DEAD_STRIP);
}
return Error::success();
}
// Adds atoms with identified start addresses (but not lengths) for all named
// atoms.
// Also, for every section that contains named atoms, but does not have an
// atom at offset zero of that section, constructs an anonymous atom covering
// that range.
Error MachOAtomGraphBuilder::addNonCustomAtoms() {
using AddrToAtomMap = std::map<JITTargetAddress, DefinedAtom *>;
DenseMap<MachOSection *, AddrToAtomMap> SecToAtoms;
DenseMap<MachOSection *, unsigned> FirstOrdinal;
std::vector<DefinedAtom *> AltEntryAtoms;
DenseSet<StringRef> ProcessedSymbols; // Used to check for duplicate defs.
for (auto SymI = Obj.symbol_begin(), SymE = Obj.symbol_end(); SymI != SymE;
++SymI) {
object::SymbolRef Sym(SymI->getRawDataRefImpl(), &Obj);
auto Name = Sym.getName();
if (!Name)
return Name.takeError();
// Bail out on duplicate definitions: There should never be more than one
// definition for a symbol in a given object file.
if (ProcessedSymbols.count(*Name))
return make_error<JITLinkError>("Duplicate definition within object: " +
*Name);
else
ProcessedSymbols.insert(*Name);
auto Addr = Sym.getAddress();
if (!Addr)
return Addr.takeError();
auto SymType = Sym.getType();
if (!SymType)
return SymType.takeError();
auto Flags = Sym.getFlags();
if (Flags & object::SymbolRef::SF_Undefined) {
LLVM_DEBUG(dbgs() << "Adding undef atom \"" << *Name << "\"\n");
G->addExternalAtom(*Name);
continue;
} else if (Flags & object::SymbolRef::SF_Absolute) {
LLVM_DEBUG(dbgs() << "Adding absolute \"" << *Name << "\" addr: "
<< format("0x%016" PRIx64, *Addr) << "\n");
auto &A = G->addAbsoluteAtom(*Name, *Addr);
A.setGlobal(Flags & object::SymbolRef::SF_Global);
A.setExported(Flags & object::SymbolRef::SF_Exported);
A.setWeak(Flags & object::SymbolRef::SF_Weak);
continue;
} else if (Flags & object::SymbolRef::SF_Common) {
LLVM_DEBUG({
dbgs() << "Adding common \"" << *Name
<< "\" addr: " << format("0x%016" PRIx64, *Addr) << "\n";
});
auto &A =
G->addCommonAtom(getCommonSection().getGenericSection(), *Name, *Addr,
std::max(Sym.getAlignment(), 1U),
Obj.getCommonSymbolSize(Sym.getRawDataRefImpl()));
A.setGlobal(Flags & object::SymbolRef::SF_Global);
A.setExported(Flags & object::SymbolRef::SF_Exported);
continue;
}
LLVM_DEBUG(dbgs() << "Adding defined atom \"" << *Name << "\"\n");
// This atom is neither undefined nor absolute, so it must be defined in
// this object. Get its section index.
auto SecItr = Sym.getSection();
if (!SecItr)
return SecItr.takeError();
uint64_t SectionIndex = (*SecItr)->getIndex() + 1;
LLVM_DEBUG(dbgs() << " to section index " << SectionIndex << "\n");
auto SecByIndexItr = Sections.find(SectionIndex);
if (SecByIndexItr == Sections.end())
return make_error<JITLinkError>("Unrecognized section index in macho");
auto &Sec = SecByIndexItr->second;
auto &DA = G->addDefinedAtom(Sec.getGenericSection(), *Name, *Addr,
std::max(Sym.getAlignment(), 1U));
DA.setGlobal(Flags & object::SymbolRef::SF_Global);
DA.setExported(Flags & object::SymbolRef::SF_Exported);
DA.setWeak(Flags & object::SymbolRef::SF_Weak);
DA.setCallable(*SymType & object::SymbolRef::ST_Function);
// Check NDesc flags.
{
uint16_t NDesc = 0;
if (Obj.is64Bit())
NDesc = Obj.getSymbol64TableEntry(SymI->getRawDataRefImpl()).n_desc;
else
NDesc = Obj.getSymbolTableEntry(SymI->getRawDataRefImpl()).n_desc;
// Record atom for alt-entry post-processing (where the layout-next
// constraints will be added).
if (NDesc & MachO::N_ALT_ENTRY)
AltEntryAtoms.push_back(&DA);
// If this atom has a no-dead-strip attr attached then mark it live.
if (NDesc & MachO::N_NO_DEAD_STRIP)
DA.setLive(true);
}
LLVM_DEBUG({
dbgs() << " Added " << *Name
<< " addr: " << format("0x%016" PRIx64, *Addr)
<< ", align: " << DA.getAlignment()
<< ", section: " << Sec.getGenericSection().getName() << "\n";
});
auto &SecAtoms = SecToAtoms[&Sec];
SecAtoms[DA.getAddress() - Sec.getAddress()] = &DA;
}
// Add anonymous atoms.
for (auto &KV : Sections) {
auto &S = KV.second;
// Skip empty sections.
if (S.empty())
continue;
// Skip sections with custom handling.
if (CustomAtomizeFunctions.count(S.getName()))
continue;
auto SAI = SecToAtoms.find(&S);
// If S is not in the SecToAtoms map then it contained no named atom. Add
// one anonymous atom to cover the whole section.
if (SAI == SecToAtoms.end()) {
SecToAtoms[&S][0] = &G->addAnonymousAtom(
S.getGenericSection(), S.getAddress(), S.getAlignment());
continue;
}
// Otherwise, check whether this section had an atom covering offset zero.
// If not, add one.
auto &SecAtoms = SAI->second;
if (!SecAtoms.count(0))
SecAtoms[0] = &G->addAnonymousAtom(S.getGenericSection(), S.getAddress(),
S.getAlignment());
}
LLVM_DEBUG(dbgs() << "MachOGraphBuilder setting atom content\n");
// Set atom contents and any section-based flags.
for (auto &KV : SecToAtoms) {
auto &S = *KV.first;
auto &SecAtoms = KV.second;
// Iterate the atoms in reverse order and set up their contents.
JITTargetAddress LastAtomAddr = S.getSize();
for (auto I = SecAtoms.rbegin(), E = SecAtoms.rend(); I != E; ++I) {
auto Offset = I->first;
auto &A = *I->second;
LLVM_DEBUG({
dbgs() << " " << A << " to [ " << S.getAddress() + Offset << " .. "
<< S.getAddress() + LastAtomAddr << " ]\n";
});
if (S.isZeroFill())
A.setZeroFill(LastAtomAddr - Offset);
else
A.setContent(S.getContent().substr(Offset, LastAtomAddr - Offset));
// If the section has no-dead-strip set then mark the atom as live.
if (S.isNoDeadStrip())
A.setLive(true);
LastAtomAddr = Offset;
}
}
LLVM_DEBUG(dbgs() << "Adding alt-entry starts\n");
// Sort alt-entry atoms by address in ascending order.
llvm::sort(AltEntryAtoms.begin(), AltEntryAtoms.end(),
[](const DefinedAtom *LHS, const DefinedAtom *RHS) {
return LHS->getAddress() < RHS->getAddress();
});
// Process alt-entry atoms in address order to build the table of alt-entry
// atoms to alt-entry chain starts.
for (auto *DA : AltEntryAtoms) {
assert(!AltEntryStarts.count(DA) && "Duplicate entry in AltEntryStarts");
// DA is an alt-entry atom. Look for the predecessor atom that it is locked
// to, bailing out if we do not find one.
auto AltEntryPred = G->findAtomByAddress(DA->getAddress() - 1);
if (!AltEntryPred)
return AltEntryPred.takeError();
// Add a LayoutNext edge from the predecessor to this atom.
AltEntryPred->setLayoutNext(*DA);
// Check to see whether the predecessor itself is an alt-entry atom.
auto AltEntryStartItr = AltEntryStarts.find(&*AltEntryPred);
if (AltEntryStartItr != AltEntryStarts.end()) {
// If the predecessor was an alt-entry atom then re-use its value.
LLVM_DEBUG({
dbgs() << " " << *DA << " -> " << *AltEntryStartItr->second
<< " (based on existing entry for " << *AltEntryPred << ")\n";
});
AltEntryStarts[DA] = AltEntryStartItr->second;
} else {
// If the predecessor does not have an entry then add an entry for this
// atom (i.e. the alt_entry atom) and a self-reference entry for the
/// predecessory atom that is the start of this chain.
LLVM_DEBUG({
dbgs() << " " << *AltEntryPred << " -> " << *AltEntryPred << "\n"
<< " " << *DA << " -> " << *AltEntryPred << "\n";
});
AltEntryStarts[&*AltEntryPred] = &*AltEntryPred;
AltEntryStarts[DA] = &*AltEntryPred;
}
}
return Error::success();
}
Error MachOAtomGraphBuilder::addAtoms() {
// Add all named atoms.
if (auto Err = addNonCustomAtoms())
return Err;
// Process special sections.
for (auto &KV : Sections) {
auto &S = KV.second;
auto HI = CustomAtomizeFunctions.find(S.getGenericSection().getName());
if (HI != CustomAtomizeFunctions.end()) {
auto &Atomize = HI->second;
if (auto Err = Atomize(S))
return Err;
}
}
return Error::success();
}
} // end namespace jitlink
} // end namespace llvm

View File

@ -1,138 +0,0 @@
//===----- MachOAtomGraphBuilder.h - MachO AtomGraph builder ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Generic MachO AtomGraph building code.
//
//===----------------------------------------------------------------------===//
#ifndef LIB_EXECUTIONENGINE_JITLINK_MACHOATOMGRAPHBUILDER_H
#define LIB_EXECUTIONENGINE_JITLINK_MACHOATOMGRAPHBUILDER_H
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "JITLinkGeneric.h"
#include "llvm/Object/MachO.h"
namespace llvm {
namespace jitlink {
class MachOAtomGraphBuilder {
public:
virtual ~MachOAtomGraphBuilder();
Expected<std::unique_ptr<AtomGraph>> buildGraph();
protected:
using OffsetToAtomMap = std::map<JITTargetAddress, DefinedAtom *>;
class MachOSection {
public:
MachOSection() = default;
/// Create a MachO section with the given address and alignment.
MachOSection(Section &GenericSection, JITTargetAddress Address,
unsigned Alignment)
: Address(Address), GenericSection(&GenericSection),
Alignment(Alignment) {}
/// Create a section without address, content or size (used for common
/// symbol sections).
MachOSection(Section &GenericSection) : GenericSection(&GenericSection) {}
Section &getGenericSection() const {
assert(GenericSection && "Section is null");
return *GenericSection;
}
StringRef getName() const {
assert(GenericSection && "No generic section attached");
return GenericSection->getName();
}
MachOSection &setContent(StringRef Content) {
assert(!ContentPtr && !Size && "Content/zeroFill already set");
ContentPtr = Content.data();
Size = Content.size();
return *this;
}
MachOSection &setZeroFill(uint64_t Size) {
assert(!ContentPtr && !this->Size && "Content/zeroFill already set");
this->Size = Size;
return *this;
}
bool isZeroFill() const { return !ContentPtr; }
bool empty() const { return getSize() == 0; }
size_t getSize() const { return Size; }
StringRef getContent() const {
assert(ContentPtr && "getContent() called on zero-fill section");
return {ContentPtr, static_cast<size_t>(Size)};
}
JITTargetAddress getAddress() const { return Address; }
unsigned getAlignment() const { return Alignment; }
MachOSection &setNoDeadStrip(bool NoDeadStrip) {
this->NoDeadStrip = NoDeadStrip;
return *this;
}
bool isNoDeadStrip() const { return NoDeadStrip; }
private:
JITTargetAddress Address = 0;
Section *GenericSection = nullptr;
const char *ContentPtr = nullptr;
uint64_t Size = 0;
unsigned Alignment = 0;
bool NoDeadStrip = false;
};
using CustomAtomizeFunction = std::function<Error(MachOSection &S)>;
MachOAtomGraphBuilder(const object::MachOObjectFile &Obj);
AtomGraph &getGraph() const { return *G; }
const object::MachOObjectFile &getObject() const { return Obj; }
void addCustomAtomizer(StringRef SectionName, CustomAtomizeFunction Atomizer);
virtual Error addRelocations() = 0;
/// Returns true if Atom A and Atom B are at a fixed offset from one another
/// (i.e. if they're part of the same alt-entry chain).
bool areLayoutLocked(const Atom &A, const Atom &B);
private:
static unsigned getPointerSize(const object::MachOObjectFile &Obj);
static support::endianness getEndianness(const object::MachOObjectFile &Obj);
MachOSection &getCommonSection();
Error parseSections();
Error addNonCustomAtoms();
Error addAtoms();
const object::MachOObjectFile &Obj;
std::unique_ptr<AtomGraph> G;
DenseMap<const DefinedAtom *, const DefinedAtom *> AltEntryStarts;
DenseMap<unsigned, MachOSection> Sections;
StringMap<CustomAtomizeFunction> CustomAtomizeFunctions;
Optional<MachOSection> CommonSymbolsSection;
};
} // end namespace jitlink
} // end namespace llvm
#endif // LIB_EXECUTIONENGINE_JITLINK_MACHOATOMGRAPHBUILDER_H

View File

@ -0,0 +1,535 @@
//=--------- MachOLinkGraphBuilder.cpp - MachO LinkGraph builder ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Generic MachO LinkGraph buliding code.
//
//===----------------------------------------------------------------------===//
#include "MachOLinkGraphBuilder.h"
#define DEBUG_TYPE "jitlink"
static const char *CommonSectionName = "__common";
namespace llvm {
namespace jitlink {
MachOLinkGraphBuilder::~MachOLinkGraphBuilder() {}
Expected<std::unique_ptr<LinkGraph>> MachOLinkGraphBuilder::buildGraph() {
// Sanity check: we only operate on relocatable objects.
if (!Obj.isRelocatableObject())
return make_error<JITLinkError>("Object is not a relocatable MachO");
if (auto Err = createNormalizedSections())
return std::move(Err);
if (auto Err = createNormalizedSymbols())
return std::move(Err);
if (auto Err = graphifyRegularSymbols())
return std::move(Err);
if (auto Err = graphifySectionsWithCustomParsers())
return std::move(Err);
if (auto Err = addRelocations())
return std::move(Err);
return std::move(G);
}
MachOLinkGraphBuilder::MachOLinkGraphBuilder(const object::MachOObjectFile &Obj)
: Obj(Obj),
G(std::make_unique<LinkGraph>(Obj.getFileName(), getPointerSize(Obj),
getEndianness(Obj))) {}
void MachOLinkGraphBuilder::addCustomSectionParser(
StringRef SectionName, SectionParserFunction Parser) {
assert(!CustomSectionParserFunctions.count(SectionName) &&
"Custom parser for this section already exists");
CustomSectionParserFunctions[SectionName] = std::move(Parser);
}
Linkage MachOLinkGraphBuilder::getLinkage(uint16_t Desc) {
if ((Desc & MachO::N_WEAK_DEF) || (Desc & MachO::N_WEAK_REF))
return Linkage::Weak;
return Linkage::Strong;
}
Scope MachOLinkGraphBuilder::getScope(StringRef Name, uint8_t Type) {
if (Name.startswith("l"))
return Scope::Local;
if (Type & MachO::N_PEXT)
return Scope::Hidden;
if (Type & MachO::N_EXT)
return Scope::Default;
return Scope::Local;
}
bool MachOLinkGraphBuilder::isAltEntry(const NormalizedSymbol &NSym) {
return NSym.Desc & MachO::N_ALT_ENTRY;
}
unsigned
MachOLinkGraphBuilder::getPointerSize(const object::MachOObjectFile &Obj) {
return Obj.is64Bit() ? 8 : 4;
}
support::endianness
MachOLinkGraphBuilder::getEndianness(const object::MachOObjectFile &Obj) {
return Obj.isLittleEndian() ? support::little : support::big;
}
Section &MachOLinkGraphBuilder::getCommonSection() {
if (!CommonSection) {
auto Prot = static_cast<sys::Memory::ProtectionFlags>(
sys::Memory::MF_READ | sys::Memory::MF_WRITE);
CommonSection = &G->createSection(CommonSectionName, Prot);
}
return *CommonSection;
}
Error MachOLinkGraphBuilder::createNormalizedSections() {
// Build normalized sections. Verifies that section data is in-range (for
// sections with content) and that address ranges are non-overlapping.
LLVM_DEBUG(dbgs() << "Creating normalized sections...\n");
for (auto &SecRef : Obj.sections()) {
NormalizedSection NSec;
uint32_t DataOffset = 0;
auto SecIndex = Obj.getSectionIndex(SecRef.getRawDataRefImpl());
auto Name = SecRef.getName();
if (!Name)
return Name.takeError();
if (Obj.is64Bit()) {
const MachO::section_64 &Sec64 =
Obj.getSection64(SecRef.getRawDataRefImpl());
NSec.Address = Sec64.addr;
NSec.Size = Sec64.size;
NSec.Alignment = 1ULL << Sec64.align;
NSec.Flags = Sec64.flags;
DataOffset = Sec64.offset;
} else {
const MachO::section &Sec32 = Obj.getSection(SecRef.getRawDataRefImpl());
NSec.Address = Sec32.addr;
NSec.Size = Sec32.size;
NSec.Alignment = 1ULL << Sec32.align;
NSec.Flags = Sec32.flags;
DataOffset = Sec32.offset;
}
LLVM_DEBUG({
dbgs() << " " << *Name << ": " << formatv("{0:x16}", NSec.Address)
<< " -- " << formatv("{0:x16}", NSec.Address + NSec.Size)
<< ", align: " << NSec.Alignment << ", index: " << SecIndex
<< "\n";
});
// Get the section data if any.
{
unsigned SectionType = NSec.Flags & MachO::SECTION_TYPE;
if (SectionType != MachO::S_ZEROFILL &&
SectionType != MachO::S_GB_ZEROFILL) {
if (DataOffset + NSec.Size > Obj.getData().size())
return make_error<JITLinkError>(
"Section data extends past end of file");
NSec.Data = Obj.getData().data() + DataOffset;
}
}
// Get prot flags.
// FIXME: Make sure this test is correct (it's probably missing cases
// as-is).
sys::Memory::ProtectionFlags Prot;
if (NSec.Flags & MachO::S_ATTR_PURE_INSTRUCTIONS)
Prot = static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_EXEC);
else
Prot = static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_WRITE);
NSec.GraphSection = &G->createSection(*Name, Prot);
IndexToSection.insert(std::make_pair(SecIndex, std::move(NSec)));
}
std::vector<NormalizedSection *> Sections;
Sections.reserve(IndexToSection.size());
for (auto &KV : IndexToSection)
Sections.push_back(&KV.second);
// If we didn't end up creating any sections then bail out. The code below
// assumes that we have at least one section.
if (Sections.empty())
return Error::success();
llvm::sort(Sections,
[](const NormalizedSection *LHS, const NormalizedSection *RHS) {
assert(LHS && RHS && "Null section?");
return LHS->Address < RHS->Address;
});
for (unsigned I = 0, E = Sections.size() - 1; I != E; ++I) {
auto &Cur = *Sections[I];
auto &Next = *Sections[I + 1];
if (Next.Address < Cur.Address + Cur.Size)
return make_error<JITLinkError>(
"Address range for section " + Cur.GraphSection->getName() +
formatv(" [ {0:x16} -- {1:x16} ] ", Cur.Address,
Cur.Address + Cur.Size) +
"overlaps " +
formatv(" [ {0:x16} -- {1:x16} ] ", Next.Address,
Next.Address + Next.Size));
}
return Error::success();
}
Error MachOLinkGraphBuilder::createNormalizedSymbols() {
LLVM_DEBUG(dbgs() << "Creating normalized symbols...\n");
for (auto &SymRef : Obj.symbols()) {
unsigned SymbolIndex = Obj.getSymbolIndex(SymRef.getRawDataRefImpl());
uint64_t Value;
uint32_t NStrX;
uint8_t Type;
uint8_t Sect;
uint16_t Desc;
if (Obj.is64Bit()) {
const MachO::nlist_64 &NL64 =
Obj.getSymbol64TableEntry(SymRef.getRawDataRefImpl());
Value = NL64.n_value;
NStrX = NL64.n_strx;
Type = NL64.n_type;
Sect = NL64.n_sect;
Desc = NL64.n_desc;
} else {
const MachO::nlist &NL32 =
Obj.getSymbolTableEntry(SymRef.getRawDataRefImpl());
Value = NL32.n_value;
NStrX = NL32.n_strx;
Type = NL32.n_type;
Sect = NL32.n_sect;
Desc = NL32.n_desc;
}
// Skip stabs.
// FIXME: Are there other symbols we should be skipping?
if (Type & MachO::N_STAB)
continue;
Optional<StringRef> Name;
if (NStrX) {
if (auto NameOrErr = SymRef.getName())
Name = *NameOrErr;
else
return NameOrErr.takeError();
}
LLVM_DEBUG({
dbgs() << " ";
if (!Name)
dbgs() << "<anonymous symbol>";
else
dbgs() << *Name;
dbgs() << ": value = " << formatv("{0:x16}", Value)
<< ", type = " << formatv("{0:x2}", Type)
<< ", desc = " << formatv("{0:x4}", Desc) << ", sect = ";
if (Sect)
dbgs() << static_cast<unsigned>(Sect - 1);
else
dbgs() << "none";
dbgs() << "\n";
});
// If this symbol has a section, sanity check that the addresses line up.
NormalizedSection *NSec = nullptr;
if (Sect != 0) {
if (auto NSecOrErr = findSectionByIndex(Sect - 1))
NSec = &*NSecOrErr;
else
return NSecOrErr.takeError();
if (Value < NSec->Address || Value > NSec->Address + NSec->Size)
return make_error<JITLinkError>("Symbol address does not fall within "
"section");
}
IndexToSymbol[SymbolIndex] =
&createNormalizedSymbol(*Name, Value, Type, Sect, Desc,
getLinkage(Type), getScope(*Name, Type));
}
return Error::success();
}
void MachOLinkGraphBuilder::addSectionStartSymAndBlock(
Section &GraphSec, uint64_t Address, const char *Data, uint64_t Size,
uint32_t Alignment, bool IsLive) {
Block &B =
Data ? G->createContentBlock(GraphSec, StringRef(Data, Size), Address,
Alignment, 0)
: G->createZeroFillBlock(GraphSec, Size, Address, Alignment, 0);
auto &Sym = G->addAnonymousSymbol(B, 0, Size, false, IsLive);
assert(!AddrToCanonicalSymbol.count(Sym.getAddress()) &&
"Anonymous block start symbol clashes with existing symbol address");
AddrToCanonicalSymbol[Sym.getAddress()] = &Sym;
}
Error MachOLinkGraphBuilder::graphifyRegularSymbols() {
LLVM_DEBUG(dbgs() << "Creating graph symbols...\n");
/// We only have 256 section indexes: Use a vector rather than a map.
std::vector<std::vector<NormalizedSymbol *>> SecIndexToSymbols;
SecIndexToSymbols.resize(256);
// Create commons, externs, and absolutes, and partition all other symbols by
// section.
for (auto &KV : IndexToSymbol) {
auto &NSym = *KV.second;
switch (NSym.Type & MachO::N_TYPE) {
case MachO::N_UNDF:
if (NSym.Value) {
if (!NSym.Name)
return make_error<JITLinkError>("Anonymous common symbol at index " +
Twine(KV.first));
NSym.GraphSymbol = &G->addCommonSymbol(
*NSym.Name, NSym.S, getCommonSection(), NSym.Value, 0,
1U << MachO::GET_COMM_ALIGN(NSym.Desc),
NSym.Desc & MachO::N_NO_DEAD_STRIP);
} else {
if (!NSym.Name)
return make_error<JITLinkError>("Anonymous external symbol at "
"index " +
Twine(KV.first));
NSym.GraphSymbol = &G->addExternalSymbol(*NSym.Name, 0);
}
break;
case MachO::N_ABS:
if (!NSym.Name)
return make_error<JITLinkError>("Anonymous absolute symbol at index " +
Twine(KV.first));
NSym.GraphSymbol = &G->addAbsoluteSymbol(
*NSym.Name, NSym.Value, 0, Linkage::Strong, Scope::Default,
NSym.Desc & MachO::N_NO_DEAD_STRIP);
break;
case MachO::N_SECT:
SecIndexToSymbols[NSym.Sect - 1].push_back(&NSym);
break;
case MachO::N_PBUD:
return make_error<JITLinkError>(
"Unupported N_PBUD symbol " +
(NSym.Name ? ("\"" + *NSym.Name + "\"") : Twine("<anon>")) +
" at index " + Twine(KV.first));
case MachO::N_INDR:
return make_error<JITLinkError>(
"Unupported N_INDR symbol " +
(NSym.Name ? ("\"" + *NSym.Name + "\"") : Twine("<anon>")) +
" at index " + Twine(KV.first));
default:
return make_error<JITLinkError>(
"Unrecognized symbol type " + Twine(NSym.Type & MachO::N_TYPE) +
" for symbol " +
(NSym.Name ? ("\"" + *NSym.Name + "\"") : Twine("<anon>")) +
" at index " + Twine(KV.first));
}
}
// Loop over sections performing regular graphification for those that
// don't have custom parsers.
for (auto &KV : IndexToSection) {
auto SecIndex = KV.first;
auto &NSec = KV.second;
// Skip sections with custom parsers.
if (CustomSectionParserFunctions.count(NSec.GraphSection->getName())) {
LLVM_DEBUG({
dbgs() << " Skipping section " << NSec.GraphSection->getName()
<< " as it has a custom parser.\n";
});
continue;
} else
LLVM_DEBUG({
dbgs() << " Processing section " << NSec.GraphSection->getName()
<< "...\n";
});
bool SectionIsNoDeadStrip = NSec.Flags & MachO::S_ATTR_NO_DEAD_STRIP;
bool SectionIsText = NSec.Flags & MachO::S_ATTR_PURE_INSTRUCTIONS;
auto &SecNSymStack = SecIndexToSymbols[SecIndex];
// If this section is non-empty but there are no symbols covering it then
// create one block and anonymous symbol to cover the entire section.
if (SecNSymStack.empty()) {
if (NSec.Size > 0) {
LLVM_DEBUG({
dbgs() << " Section non-empty, but contains no symbols. "
"Creating anonymous block to cover "
<< formatv("{0:x16}", NSec.Address) << " -- "
<< formatv("{0:x16}", NSec.Address + NSec.Size) << "\n";
});
addSectionStartSymAndBlock(*NSec.GraphSection, NSec.Address, NSec.Data,
NSec.Size, NSec.Alignment,
SectionIsNoDeadStrip);
} else
LLVM_DEBUG({
dbgs() << " Section empty and contains no symbols. Skipping.\n";
});
continue;
}
// Sort the symbol stack in by address, alt-entry status, scope, and name.
// We sort in reverse order so that symbols will be visited in the right
// order when we pop off the stack below.
llvm::sort(SecNSymStack, [](const NormalizedSymbol *LHS,
const NormalizedSymbol *RHS) {
if (LHS->Value != RHS->Value)
return LHS->Value > RHS->Value;
if (isAltEntry(*LHS) != isAltEntry(*RHS))
return isAltEntry(*RHS);
if (LHS->S != RHS->S)
return static_cast<uint8_t>(LHS->S) < static_cast<uint8_t>(RHS->S);
return LHS->Name < RHS->Name;
});
// The first symbol in a section can not be an alt-entry symbol.
if (!SecNSymStack.empty() && isAltEntry(*SecNSymStack.back()))
return make_error<JITLinkError>(
"First symbol in " + NSec.GraphSection->getName() + " is alt-entry");
// If the section is non-empty but there is no symbol covering the start
// address then add an anonymous one.
if (SecNSymStack.back()->Value != NSec.Address) {
auto AnonBlockSize = SecNSymStack.back()->Value - NSec.Address;
LLVM_DEBUG({
dbgs() << " Section start not covered by symbol. "
<< "Creating anonymous block to cover [ "
<< formatv("{0:x16}", NSec.Address) << " -- "
<< formatv("{0:x16}", NSec.Address + AnonBlockSize) << " ]\n";
});
addSectionStartSymAndBlock(*NSec.GraphSection, NSec.Address, NSec.Data,
AnonBlockSize, NSec.Alignment,
SectionIsNoDeadStrip);
}
// Visit section symbols in order by popping off the reverse-sorted stack,
// building blocks for each alt-entry chain and creating symbols as we go.
while (!SecNSymStack.empty()) {
SmallVector<NormalizedSymbol *, 8> BlockSyms;
BlockSyms.push_back(SecNSymStack.back());
SecNSymStack.pop_back();
while (!SecNSymStack.empty() &&
(isAltEntry(*SecNSymStack.back()) ||
SecNSymStack.back()->Value == BlockSyms.back()->Value)) {
BlockSyms.push_back(SecNSymStack.back());
SecNSymStack.pop_back();
}
// BlockNSyms now contains the block symbols in reverse canonical order.
JITTargetAddress BlockStart = BlockSyms.front()->Value;
JITTargetAddress BlockEnd = SecNSymStack.empty()
? NSec.Address + NSec.Size
: SecNSymStack.back()->Value;
JITTargetAddress BlockOffset = BlockStart - NSec.Address;
JITTargetAddress BlockSize = BlockEnd - BlockStart;
LLVM_DEBUG({
dbgs() << " Creating block for " << formatv("{0:x16}", BlockStart)
<< " -- " << formatv("{0:x16}", BlockEnd) << ": "
<< NSec.GraphSection->getName() << " + "
<< formatv("{0:x16}", BlockOffset) << " with "
<< BlockSyms.size() << " symbol(s)...\n";
});
Block &B =
NSec.Data
? G->createContentBlock(
*NSec.GraphSection,
StringRef(NSec.Data + BlockOffset, BlockSize), BlockStart,
NSec.Alignment, BlockStart % NSec.Alignment)
: G->createZeroFillBlock(*NSec.GraphSection, BlockSize,
BlockStart, NSec.Alignment,
BlockStart % NSec.Alignment);
Optional<JITTargetAddress> LastCanonicalAddr;
JITTargetAddress SymEnd = BlockEnd;
while (!BlockSyms.empty()) {
auto &NSym = *BlockSyms.back();
BlockSyms.pop_back();
bool SymLive =
(NSym.Desc & MachO::N_NO_DEAD_STRIP) || SectionIsNoDeadStrip;
LLVM_DEBUG({
dbgs() << " " << formatv("{0:x16}", NSym.Value) << " -- "
<< formatv("{0:x16}", SymEnd) << ": ";
if (!NSym.Name)
dbgs() << "<anonymous symbol>";
else
dbgs() << NSym.Name;
if (SymLive)
dbgs() << " [no-dead-strip]";
if (LastCanonicalAddr == NSym.Value)
dbgs() << " [non-canonical]";
dbgs() << "\n";
});
auto &Sym =
NSym.Name
? G->addDefinedSymbol(B, NSym.Value - BlockStart, *NSym.Name,
SymEnd - NSym.Value, NSym.L, NSym.S,
SectionIsText, SymLive)
: G->addAnonymousSymbol(B, NSym.Value - BlockStart,
SymEnd - NSym.Value, SectionIsText,
SymLive);
NSym.GraphSymbol = &Sym;
if (LastCanonicalAddr != Sym.getAddress()) {
if (LastCanonicalAddr)
SymEnd = *LastCanonicalAddr;
LastCanonicalAddr = Sym.getAddress();
setCanonicalSymbol(Sym);
}
}
}
}
return Error::success();
}
Error MachOLinkGraphBuilder::graphifySectionsWithCustomParsers() {
// Graphify special sections.
for (auto &KV : IndexToSection) {
auto &NSec = KV.second;
auto HI = CustomSectionParserFunctions.find(NSec.GraphSection->getName());
if (HI != CustomSectionParserFunctions.end()) {
auto &Parse = HI->second;
if (auto Err = Parse(NSec))
return Err;
}
}
return Error::success();
}
} // end namespace jitlink
} // end namespace llvm

View File

@ -0,0 +1,269 @@
//===----- MachOLinkGraphBuilder.h - MachO LinkGraph builder ----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Generic MachO LinkGraph building code.
//
//===----------------------------------------------------------------------===//
#ifndef LIB_EXECUTIONENGINE_JITLINK_MACHOLINKGRAPHBUILDER_H
#define LIB_EXECUTIONENGINE_JITLINK_MACHOLINKGRAPHBUILDER_H
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "EHFrameSupportImpl.h"
#include "JITLinkGeneric.h"
#include "llvm/Object/MachO.h"
#include <list>
namespace llvm {
namespace jitlink {
class MachOLinkGraphBuilder {
public:
virtual ~MachOLinkGraphBuilder();
Expected<std::unique_ptr<LinkGraph>> buildGraph();
protected:
class MachOEHFrameBinaryParser : public EHFrameBinaryParser {
public:
MachOEHFrameBinaryParser(MachOLinkGraphBuilder &Builder,
JITTargetAddress EHFrameAddress,
StringRef EHFrameContent, Section &EHFrameSection,
uint64_t CIEAlignment, uint64_t FDEAlignment,
Edge::Kind FDEToCIERelocKind,
Edge::Kind FDEToTargetRelocKind)
: EHFrameBinaryParser(EHFrameAddress, EHFrameContent,
Builder.getGraph().getPointerSize(),
Builder.getGraph().getEndianness()),
Builder(Builder), EHFrameSection(EHFrameSection),
CIEAlignment(CIEAlignment), FDEAlignment(FDEAlignment),
FDEToCIERelocKind(FDEToCIERelocKind),
FDEToTargetRelocKind(FDEToTargetRelocKind) {}
Symbol *getSymbolAtAddress(JITTargetAddress Address) override {
if (auto *Sym = Builder.getSymbolByAddress(Address))
if (Sym->getAddress() == Address)
return Sym;
return nullptr;
}
Symbol &createCIERecord(JITTargetAddress RecordAddr,
StringRef RecordContent) override {
auto &G = Builder.getGraph();
auto &B = G.createContentBlock(EHFrameSection, RecordContent, RecordAddr,
CIEAlignment, 0);
auto &CIESymbol =
G.addAnonymousSymbol(B, 0, RecordContent.size(), false, false);
Builder.setCanonicalSymbol(CIESymbol);
return CIESymbol;
}
Expected<Symbol &> createFDERecord(JITTargetAddress RecordAddr,
StringRef RecordContent, Symbol &CIE,
size_t CIEOffset, Symbol &Func,
size_t FuncOffset, Symbol *LSDA,
size_t LSDAOffset) override {
auto &G = Builder.getGraph();
auto &B = G.createContentBlock(EHFrameSection, RecordContent, RecordAddr,
FDEAlignment, 0);
// Add edges to CIE, Func, and (conditionally) LSDA.
B.addEdge(FDEToCIERelocKind, CIEOffset, CIE, 0);
B.addEdge(FDEToTargetRelocKind, FuncOffset, Func, 0);
if (LSDA)
B.addEdge(FDEToTargetRelocKind, LSDAOffset, *LSDA, 0);
auto &FDESymbol =
G.addAnonymousSymbol(B, 0, RecordContent.size(), false, false);
// Add a keep-alive relocation from the function to the FDE to ensure it
// is not dead stripped.
Func.getBlock().addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
return FDESymbol;
}
private:
MachOLinkGraphBuilder &Builder;
Section &EHFrameSection;
uint64_t CIEAlignment;
uint64_t FDEAlignment;
Edge::Kind FDEToCIERelocKind;
Edge::Kind FDEToTargetRelocKind;
};
struct NormalizedSymbol {
friend class MachOLinkGraphBuilder;
private:
NormalizedSymbol(Optional<StringRef> Name, uint64_t Value, uint8_t Type,
uint8_t Sect, uint16_t Desc, Linkage L, Scope S)
: Name(Name), Value(Value), Type(Type), Sect(Sect), Desc(Desc), L(L),
S(S) {
assert(!Name || !Name->empty() && "Name must be none or non-empty");
}
public:
NormalizedSymbol(const NormalizedSymbol &) = delete;
NormalizedSymbol &operator=(const NormalizedSymbol &) = delete;
NormalizedSymbol(NormalizedSymbol &&) = delete;
NormalizedSymbol &operator=(NormalizedSymbol &&) = delete;
Optional<StringRef> Name;
uint64_t Value = 0;
uint8_t Type = 0;
uint8_t Sect = 0;
uint16_t Desc = 0;
Linkage L = Linkage::Strong;
Scope S = Scope::Default;
Symbol *GraphSymbol = nullptr;
};
class NormalizedSection {
friend class MachOLinkGraphBuilder;
private:
NormalizedSection() = default;
public:
Section *GraphSection = nullptr;
uint64_t Address = 0;
uint64_t Size = 0;
uint64_t Alignment = 0;
uint32_t Flags = 0;
const char *Data = nullptr;
};
using SectionParserFunction = std::function<Error(NormalizedSection &S)>;
MachOLinkGraphBuilder(const object::MachOObjectFile &Obj);
LinkGraph &getGraph() const { return *G; }
const object::MachOObjectFile &getObject() const { return Obj; }
void addCustomSectionParser(StringRef SectionName,
SectionParserFunction Parse);
virtual Error addRelocations() = 0;
/// Create a symbol.
template <typename... ArgTs>
NormalizedSymbol &createNormalizedSymbol(ArgTs &&... Args) {
NormalizedSymbol *Sym = reinterpret_cast<NormalizedSymbol *>(
Allocator.Allocate<NormalizedSymbol>());
new (Sym) NormalizedSymbol(std::forward<ArgTs>(Args)...);
return *Sym;
}
/// Index is zero-based (MachO section indexes are usually one-based) and
/// assumed to be in-range. Client is responsible for checking.
NormalizedSection &getSectionByIndex(unsigned Index) {
auto I = IndexToSection.find(Index);
assert(I != IndexToSection.end() && "No section recorded at index");
return I->second;
}
/// Try to get the section at the given index. Will return an error if the
/// given index is out of range, or if no section has been added for the given
/// index.
Expected<NormalizedSection &> findSectionByIndex(unsigned Index) {
auto I = IndexToSection.find(Index);
if (I == IndexToSection.end())
return make_error<JITLinkError>("No section recorded for index " +
formatv("{0:u}", Index));
return I->second;
}
/// Try to get the symbol at the given index. Will return an error if the
/// given index is out of range, or if no symbol has been added for the given
/// index.
Expected<NormalizedSymbol &> findSymbolByIndex(uint64_t Index) {
if (Index >= IndexToSymbol.size())
return make_error<JITLinkError>("Symbol index out of range");
auto *Sym = IndexToSymbol[Index];
if (!Sym)
return make_error<JITLinkError>("No symbol at index " +
formatv("{0:u}", Index));
return *Sym;
}
/// Returns the symbol with the highest address not greater than the search
/// address, or null if no such symbol exists.
Symbol *getSymbolByAddress(JITTargetAddress Address) {
auto I = AddrToCanonicalSymbol.upper_bound(Address);
if (I == AddrToCanonicalSymbol.begin())
return nullptr;
return std::prev(I)->second;
}
/// Returns the symbol with the highest address not greater than the search
/// address, or an error if no such symbol exists.
Expected<Symbol &> findSymbolByAddress(JITTargetAddress Address) {
auto *Sym = getSymbolByAddress(Address);
if (Sym)
if (Address < Sym->getAddress() + Sym->getSize())
return *Sym;
return make_error<JITLinkError>("No symbol covering address " +
formatv("{0:x16}", Address));
}
static Linkage getLinkage(uint16_t Desc);
static Scope getScope(StringRef Name, uint8_t Type);
static bool isAltEntry(const NormalizedSymbol &NSym);
private:
static unsigned getPointerSize(const object::MachOObjectFile &Obj);
static support::endianness getEndianness(const object::MachOObjectFile &Obj);
void setCanonicalSymbol(Symbol &Sym) {
auto *&CanonicalSymEntry = AddrToCanonicalSymbol[Sym.getAddress()];
// There should be no symbol at this address, or, if there is,
// it should be a zero-sized symbol from an empty section (which
// we can safely override).
assert((!CanonicalSymEntry || CanonicalSymEntry->getSize() == 0) &&
"Duplicate canonical symbol at address");
CanonicalSymEntry = &Sym;
}
Section &getCommonSection();
void addSectionStartSymAndBlock(Section &GraphSec, uint64_t Address,
const char *Data, uint64_t Size,
uint32_t Alignment, bool IsLive);
Error createNormalizedSections();
Error createNormalizedSymbols();
/// Create graph blocks and symbols for externals, absolutes, commons and
/// all defined symbols in sections without custom parsers.
Error graphifyRegularSymbols();
/// Create graph blocks and symbols for all sections.
Error graphifySectionsWithCustomParsers();
// Put the BumpPtrAllocator first so that we don't free any of the underlying
// memory until the Symbol/Addressable destructors have been run.
BumpPtrAllocator Allocator;
const object::MachOObjectFile &Obj;
std::unique_ptr<LinkGraph> G;
DenseMap<unsigned, NormalizedSection> IndexToSection;
Section *CommonSection = nullptr;
DenseMap<uint32_t, NormalizedSymbol *> IndexToSymbol;
std::map<JITTargetAddress, Symbol *> AddrToCanonicalSymbol;
StringMap<SectionParserFunction> CustomSectionParserFunctions;
};
} // end namespace jitlink
} // end namespace llvm
#endif // LIB_EXECUTIONENGINE_JITLINK_MACHOLINKGRAPHBUILDER_H

View File

@ -13,7 +13,7 @@
#include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h" #include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h"
#include "BasicGOTAndStubsBuilder.h" #include "BasicGOTAndStubsBuilder.h"
#include "MachOAtomGraphBuilder.h" #include "MachOLinkGraphBuilder.h"
#define DEBUG_TYPE "jitlink" #define DEBUG_TYPE "jitlink"
@ -23,15 +23,20 @@ using namespace llvm::jitlink::MachO_x86_64_Edges;
namespace { namespace {
class MachOAtomGraphBuilder_x86_64 : public MachOAtomGraphBuilder { class MachOLinkGraphBuilder_x86_64 : public MachOLinkGraphBuilder {
public: public:
MachOAtomGraphBuilder_x86_64(const object::MachOObjectFile &Obj) MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile &Obj)
: MachOAtomGraphBuilder(Obj), : MachOLinkGraphBuilder(Obj) {
NumSymbols(Obj.getSymtabLoadCommand().nsyms) { addCustomSectionParser(
addCustomAtomizer("__eh_frame", [this](MachOSection &EHFrameSection) { "__eh_frame", [this](NormalizedSection &EHFrameSection) {
return addEHFrame(getGraph(), EHFrameSection.getGenericSection(), if (!EHFrameSection.Data)
EHFrameSection.getContent(), return make_error<JITLinkError>(
EHFrameSection.getAddress(), NegDelta32, Delta64); "__eh_frame section is marked zero-fill");
return MachOEHFrameBinaryParser(
*this, EHFrameSection.Address,
StringRef(EHFrameSection.Data, EHFrameSection.Size),
*EHFrameSection.GraphSection, 8, 4, NegDelta32, Delta64)
.addToGraph();
}); });
} }
@ -102,17 +107,6 @@ private:
", length=" + formatv("{0:d}", RI.r_length)); ", length=" + formatv("{0:d}", RI.r_length));
} }
Expected<Atom &> findAtomBySymbolIndex(const MachO::relocation_info &RI) {
auto &Obj = getObject();
if (RI.r_symbolnum >= NumSymbols)
return make_error<JITLinkError>("Symbol index out of range");
auto SymI = Obj.getSymbolByIndex(RI.r_symbolnum);
auto Name = SymI->getName();
if (!Name)
return Name.takeError();
return getGraph().getAtomByName(*Name);
}
MachO::relocation_info MachO::relocation_info
getRelocationInfo(const object::relocation_iterator RelItr) { getRelocationInfo(const object::relocation_iterator RelItr) {
MachO::any_relocation_info ARI = MachO::any_relocation_info ARI =
@ -122,12 +116,12 @@ private:
return RI; return RI;
} }
using PairRelocInfo = std::tuple<MachOX86RelocationKind, Atom *, uint64_t>; using PairRelocInfo = std::tuple<MachOX86RelocationKind, Symbol *, uint64_t>;
// Parses paired SUBTRACTOR/UNSIGNED relocations and, on success, // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,
// returns the edge kind and addend to be used. // returns the edge kind and addend to be used.
Expected<PairRelocInfo> Expected<PairRelocInfo>
parsePairRelocation(DefinedAtom &AtomToFix, Edge::Kind SubtractorKind, parsePairRelocation(Block &BlockToFix, Edge::Kind SubtractorKind,
const MachO::relocation_info &SubRI, const MachO::relocation_info &SubRI,
JITTargetAddress FixupAddress, const char *FixupContent, JITTargetAddress FixupAddress, const char *FixupContent,
object::relocation_iterator &UnsignedRelItr, object::relocation_iterator &UnsignedRelItr,
@ -154,9 +148,11 @@ private:
return make_error<JITLinkError>("length of x86_64 SUBTRACTOR and paired " return make_error<JITLinkError>("length of x86_64 SUBTRACTOR and paired "
"UNSIGNED reloc must match"); "UNSIGNED reloc must match");
auto FromAtom = findAtomBySymbolIndex(SubRI); Symbol *FromSymbol;
if (!FromAtom) if (auto FromSymbolOrErr = findSymbolByIndex(SubRI.r_symbolnum))
return FromAtom.takeError(); FromSymbol = FromSymbolOrErr->GraphSymbol;
else
return FromSymbolOrErr.takeError();
// Read the current fixup value. // Read the current fixup value.
uint64_t FixupValue = 0; uint64_t FixupValue = 0;
@ -165,54 +161,60 @@ private:
else else
FixupValue = *(const little32_t *)FixupContent; FixupValue = *(const little32_t *)FixupContent;
// Find 'ToAtom' using symbol number or address, depending on whether the // Find 'ToSymbol' using symbol number or address, depending on whether the
// paired UNSIGNED relocation is extern. // paired UNSIGNED relocation is extern.
Atom *ToAtom = nullptr; Symbol *ToSymbol = nullptr;
if (UnsignedRI.r_extern) { if (UnsignedRI.r_extern) {
// Find target atom by symbol index. // Find target symbol by symbol index.
if (auto ToAtomOrErr = findAtomBySymbolIndex(UnsignedRI)) if (auto ToSymbolOrErr = findSymbolByIndex(UnsignedRI.r_symbolnum))
ToAtom = &*ToAtomOrErr; ToSymbol = ToSymbolOrErr->GraphSymbol;
else else
return ToAtomOrErr.takeError(); return ToSymbolOrErr.takeError();
} else { } else {
if (auto ToAtomOrErr = getGraph().findAtomByAddress(FixupValue)) if (auto ToSymbolOrErr = findSymbolByAddress(FixupValue))
ToAtom = &*ToAtomOrErr; ToSymbol = &*ToSymbolOrErr;
else else
return ToAtomOrErr.takeError(); return ToSymbolOrErr.takeError();
FixupValue -= ToAtom->getAddress(); FixupValue -= ToSymbol->getAddress();
} }
MachOX86RelocationKind DeltaKind; MachOX86RelocationKind DeltaKind;
Atom *TargetAtom; Symbol *TargetSymbol;
uint64_t Addend; uint64_t Addend;
if (areLayoutLocked(AtomToFix, *FromAtom)) { if (&BlockToFix == &FromSymbol->getAddressable()) {
TargetAtom = ToAtom; TargetSymbol = ToSymbol;
DeltaKind = (SubRI.r_length == 3) ? Delta64 : Delta32; DeltaKind = (SubRI.r_length == 3) ? Delta64 : Delta32;
Addend = FixupValue + (FixupAddress - FromAtom->getAddress()); Addend = FixupValue + (FixupAddress - FromSymbol->getAddress());
// FIXME: handle extern 'from'. // FIXME: handle extern 'from'.
} else if (areLayoutLocked(AtomToFix, *ToAtom)) { } else if (&BlockToFix == &ToSymbol->getAddressable()) {
TargetAtom = &*FromAtom; TargetSymbol = FromSymbol;
DeltaKind = (SubRI.r_length == 3) ? NegDelta64 : NegDelta32; DeltaKind = (SubRI.r_length == 3) ? NegDelta64 : NegDelta32;
Addend = FixupValue - (FixupAddress - ToAtom->getAddress()); Addend = FixupValue - (FixupAddress - ToSymbol->getAddress());
} else { } else {
// AtomToFix was neither FromAtom nor ToAtom. // BlockToFix was neither FromSymbol nor ToSymbol.
return make_error<JITLinkError>("SUBTRACTOR relocation must fix up " return make_error<JITLinkError>("SUBTRACTOR relocation must fix up "
"either 'A' or 'B' (or an atom in one " "either 'A' or 'B' (or a symbol in one "
"of their alt-entry groups)"); "of their alt-entry chains)");
} }
return PairRelocInfo(DeltaKind, TargetAtom, Addend); return PairRelocInfo(DeltaKind, TargetSymbol, Addend);
} }
Error addRelocations() override { Error addRelocations() override {
using namespace support; using namespace support;
auto &G = getGraph();
auto &Obj = getObject(); auto &Obj = getObject();
for (auto &S : Obj.sections()) { for (auto &S : Obj.sections()) {
JITTargetAddress SectionAddress = S.getAddress(); JITTargetAddress SectionAddress = S.getAddress();
if (S.isVirtual()) {
if (S.relocation_begin() != S.relocation_end())
return make_error<JITLinkError>("Virtual section contains "
"relocations");
continue;
}
for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end(); for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end();
RelItr != RelEnd; ++RelItr) { RelItr != RelEnd; ++RelItr) {
@ -231,26 +233,26 @@ private:
<< format("0x%016" PRIx64, FixupAddress) << "\n"; << format("0x%016" PRIx64, FixupAddress) << "\n";
}); });
// Find the atom that the fixup points to. // Find the block that the fixup points to.
DefinedAtom *AtomToFix = nullptr; Block *BlockToFix = nullptr;
{ {
auto AtomToFixOrErr = G.findAtomByAddress(FixupAddress); auto SymbolToFixOrErr = findSymbolByAddress(FixupAddress);
if (!AtomToFixOrErr) if (!SymbolToFixOrErr)
return AtomToFixOrErr.takeError(); return SymbolToFixOrErr.takeError();
AtomToFix = &*AtomToFixOrErr; BlockToFix = &SymbolToFixOrErr->getBlock();
} }
if (FixupAddress + static_cast<JITTargetAddress>(1ULL << RI.r_length) > if (FixupAddress + static_cast<JITTargetAddress>(1ULL << RI.r_length) >
AtomToFix->getAddress() + AtomToFix->getContent().size()) BlockToFix->getAddress() + BlockToFix->getContent().size())
return make_error<JITLinkError>( return make_error<JITLinkError>(
"Relocation content extends past end of fixup atom"); "Relocation extends past end of fixup block");
// Get a pointer to the fixup content. // Get a pointer to the fixup content.
const char *FixupContent = AtomToFix->getContent().data() + const char *FixupContent = BlockToFix->getContent().data() +
(FixupAddress - AtomToFix->getAddress()); (FixupAddress - BlockToFix->getAddress());
// The target atom and addend will be populated by the switch below. // The target symbol and addend will be populated by the switch below.
Atom *TargetAtom = nullptr; Symbol *TargetSymbol = nullptr;
uint64_t Addend = 0; uint64_t Addend = 0;
switch (*Kind) { switch (*Kind) {
@ -258,53 +260,53 @@ private:
case PCRel32: case PCRel32:
case PCRel32GOTLoad: case PCRel32GOTLoad:
case PCRel32GOT: case PCRel32GOT:
if (auto TargetAtomOrErr = findAtomBySymbolIndex(RI)) if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
TargetAtom = &*TargetAtomOrErr; TargetSymbol = TargetSymbolOrErr->GraphSymbol;
else else
return TargetAtomOrErr.takeError(); return TargetSymbolOrErr.takeError();
Addend = *(const ulittle32_t *)FixupContent; Addend = *(const ulittle32_t *)FixupContent;
break; break;
case Pointer32: case Pointer32:
if (auto TargetAtomOrErr = findAtomBySymbolIndex(RI)) if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
TargetAtom = &*TargetAtomOrErr; TargetSymbol = TargetSymbolOrErr->GraphSymbol;
else else
return TargetAtomOrErr.takeError(); return TargetSymbolOrErr.takeError();
Addend = *(const ulittle32_t *)FixupContent; Addend = *(const ulittle32_t *)FixupContent;
break; break;
case Pointer64: case Pointer64:
if (auto TargetAtomOrErr = findAtomBySymbolIndex(RI)) if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
TargetAtom = &*TargetAtomOrErr; TargetSymbol = TargetSymbolOrErr->GraphSymbol;
else else
return TargetAtomOrErr.takeError(); return TargetSymbolOrErr.takeError();
Addend = *(const ulittle64_t *)FixupContent; Addend = *(const ulittle64_t *)FixupContent;
break; break;
case Pointer64Anon: { case Pointer64Anon: {
JITTargetAddress TargetAddress = *(const ulittle64_t *)FixupContent; JITTargetAddress TargetAddress = *(const ulittle64_t *)FixupContent;
if (auto TargetAtomOrErr = G.findAtomByAddress(TargetAddress)) if (auto TargetSymbolOrErr = findSymbolByAddress(TargetAddress))
TargetAtom = &*TargetAtomOrErr; TargetSymbol = &*TargetSymbolOrErr;
else else
return TargetAtomOrErr.takeError(); return TargetSymbolOrErr.takeError();
Addend = TargetAddress - TargetAtom->getAddress(); Addend = TargetAddress - TargetSymbol->getAddress();
break; break;
} }
case PCRel32Minus1: case PCRel32Minus1:
case PCRel32Minus2: case PCRel32Minus2:
case PCRel32Minus4: case PCRel32Minus4:
if (auto TargetAtomOrErr = findAtomBySymbolIndex(RI)) if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
TargetAtom = &*TargetAtomOrErr; TargetSymbol = TargetSymbolOrErr->GraphSymbol;
else else
return TargetAtomOrErr.takeError(); return TargetSymbolOrErr.takeError();
Addend = *(const ulittle32_t *)FixupContent + Addend = *(const ulittle32_t *)FixupContent +
(1 << (*Kind - PCRel32Minus1)); (1 << (*Kind - PCRel32Minus1));
break; break;
case PCRel32Anon: { case PCRel32Anon: {
JITTargetAddress TargetAddress = JITTargetAddress TargetAddress =
FixupAddress + 4 + *(const ulittle32_t *)FixupContent; FixupAddress + 4 + *(const ulittle32_t *)FixupContent;
if (auto TargetAtomOrErr = G.findAtomByAddress(TargetAddress)) if (auto TargetSymbolOrErr = findSymbolByAddress(TargetAddress))
TargetAtom = &*TargetAtomOrErr; TargetSymbol = &*TargetSymbolOrErr;
else else
return TargetAtomOrErr.takeError(); return TargetSymbolOrErr.takeError();
Addend = TargetAddress - TargetAtom->getAddress(); Addend = TargetAddress - TargetSymbol->getAddress();
break; break;
} }
case PCRel32Minus1Anon: case PCRel32Minus1Anon:
@ -314,11 +316,11 @@ private:
static_cast<JITTargetAddress>(1ULL << (*Kind - PCRel32Minus1Anon)); static_cast<JITTargetAddress>(1ULL << (*Kind - PCRel32Minus1Anon));
JITTargetAddress TargetAddress = JITTargetAddress TargetAddress =
FixupAddress + 4 + Delta + *(const ulittle32_t *)FixupContent; FixupAddress + 4 + Delta + *(const ulittle32_t *)FixupContent;
if (auto TargetAtomOrErr = G.findAtomByAddress(TargetAddress)) if (auto TargetSymbolOrErr = findSymbolByAddress(TargetAddress))
TargetAtom = &*TargetAtomOrErr; TargetSymbol = &*TargetSymbolOrErr;
else else
return TargetAtomOrErr.takeError(); return TargetSymbolOrErr.takeError();
Addend = TargetAddress - TargetAtom->getAddress(); Addend = TargetAddress - TargetSymbol->getAddress();
break; break;
} }
case Delta32: case Delta32:
@ -329,12 +331,12 @@ private:
// NegDelta32/NegDelta64, depending on the direction of the // NegDelta32/NegDelta64, depending on the direction of the
// subtraction) along with the addend. // subtraction) along with the addend.
auto PairInfo = auto PairInfo =
parsePairRelocation(*AtomToFix, *Kind, RI, FixupAddress, parsePairRelocation(*BlockToFix, *Kind, RI, FixupAddress,
FixupContent, ++RelItr, RelEnd); FixupContent, ++RelItr, RelEnd);
if (!PairInfo) if (!PairInfo)
return PairInfo.takeError(); return PairInfo.takeError();
std::tie(*Kind, TargetAtom, Addend) = *PairInfo; std::tie(*Kind, TargetSymbol, Addend) = *PairInfo;
assert(TargetAtom && "No target atom from parsePairRelocation?"); assert(TargetSymbol && "No target symbol from parsePairRelocation?");
break; break;
} }
default: default:
@ -343,41 +345,38 @@ private:
} }
LLVM_DEBUG({ LLVM_DEBUG({
Edge GE(*Kind, FixupAddress - AtomToFix->getAddress(), *TargetAtom, Edge GE(*Kind, FixupAddress - BlockToFix->getAddress(), *TargetSymbol,
Addend); Addend);
printEdge(dbgs(), *AtomToFix, GE, printEdge(dbgs(), *BlockToFix, GE,
getMachOX86RelocationKindName(*Kind)); getMachOX86RelocationKindName(*Kind));
dbgs() << "\n"; dbgs() << "\n";
}); });
AtomToFix->addEdge(*Kind, FixupAddress - AtomToFix->getAddress(), BlockToFix->addEdge(*Kind, FixupAddress - BlockToFix->getAddress(),
*TargetAtom, Addend); *TargetSymbol, Addend);
} }
} }
return Error::success(); return Error::success();
} }
unsigned NumSymbols = 0;
}; };
class MachO_x86_64_GOTAndStubsBuilder class MachO_x86_64_GOTAndStubsBuilder
: public BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder> { : public BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder> {
public: public:
MachO_x86_64_GOTAndStubsBuilder(AtomGraph &G) MachO_x86_64_GOTAndStubsBuilder(LinkGraph &G)
: BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder>(G) {} : BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder>(G) {}
bool isGOTEdge(Edge &E) const { bool isGOTEdge(Edge &E) const {
return E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad; return E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad;
} }
DefinedAtom &createGOTEntry(Atom &Target) { Symbol &createGOTEntry(Symbol &Target) {
auto &GOTEntryAtom = G.addAnonymousAtom(getGOTSection(), 0x0, 8); auto &GOTEntryBlock = G.createContentBlock(
GOTEntryAtom.setContent( getGOTSection(), getGOTEntryBlockContent(), 0, 8, 0);
StringRef(reinterpret_cast<const char *>(NullGOTEntryContent), 8)); GOTEntryBlock.addEdge(Pointer64, 0, Target, 0);
GOTEntryAtom.addEdge(Pointer64, 0, Target, 0); return G.addAnonymousSymbol(GOTEntryBlock, 0, 8, false, false);
return GOTEntryAtom;
} }
void fixGOTEdge(Edge &E, Atom &GOTEntry) { void fixGOTEdge(Edge &E, Symbol &GOTEntry) {
assert((E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad) && assert((E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad) &&
"Not a GOT edge?"); "Not a GOT edge?");
E.setKind(PCRel32); E.setKind(PCRel32);
@ -389,19 +388,16 @@ public:
return E.getKind() == Branch32 && !E.getTarget().isDefined(); return E.getKind() == Branch32 && !E.getTarget().isDefined();
} }
DefinedAtom &createStub(Atom &Target) { Symbol &createStub(Symbol &Target) {
auto &StubAtom = G.addAnonymousAtom(getStubsSection(), 0x0, 2); auto &StubContentBlock =
StubAtom.setContent( G.createContentBlock(getStubsSection(), getStubBlockContent(), 0, 1, 0);
StringRef(reinterpret_cast<const char *>(StubContent), 6));
// Re-use GOT entries for stub targets. // Re-use GOT entries for stub targets.
auto &GOTEntryAtom = getGOTEntryAtom(Target); auto &GOTEntrySymbol = getGOTEntrySymbol(Target);
StubAtom.addEdge(PCRel32, 2, GOTEntryAtom, 0); StubContentBlock.addEdge(PCRel32, 2, GOTEntrySymbol, 0);
return G.addAnonymousSymbol(StubContentBlock, 0, 6, true, false);
return StubAtom;
} }
void fixExternalBranchEdge(Edge &E, Atom &Stub) { void fixExternalBranchEdge(Edge &E, Symbol &Stub) {
assert(E.getKind() == Branch32 && "Not a Branch32 edge?"); assert(E.getKind() == Branch32 && "Not a Branch32 edge?");
assert(E.getAddend() == 0 && "Branch32 edge has non-zero addend?"); assert(E.getAddend() == 0 && "Branch32 edge has non-zero addend?");
E.setTarget(Stub); E.setTarget(Stub);
@ -410,7 +406,7 @@ public:
private: private:
Section &getGOTSection() { Section &getGOTSection() {
if (!GOTSection) if (!GOTSection)
GOTSection = &G.createSection("$__GOT", 8, sys::Memory::MF_READ, false); GOTSection = &G.createSection("$__GOT", sys::Memory::MF_READ);
return *GOTSection; return *GOTSection;
} }
@ -418,11 +414,21 @@ private:
if (!StubsSection) { if (!StubsSection) {
auto StubsProt = static_cast<sys::Memory::ProtectionFlags>( auto StubsProt = static_cast<sys::Memory::ProtectionFlags>(
sys::Memory::MF_READ | sys::Memory::MF_EXEC); sys::Memory::MF_READ | sys::Memory::MF_EXEC);
StubsSection = &G.createSection("$__STUBS", 8, StubsProt, false); StubsSection = &G.createSection("$__STUBS", StubsProt);
} }
return *StubsSection; return *StubsSection;
} }
StringRef getGOTEntryBlockContent() {
return StringRef(reinterpret_cast<const char *>(NullGOTEntryContent),
sizeof(NullGOTEntryContent));
}
StringRef getStubBlockContent() {
return StringRef(reinterpret_cast<const char *>(StubContent),
sizeof(StubContent));
}
static const uint8_t NullGOTEntryContent[8]; static const uint8_t NullGOTEntryContent[8];
static const uint8_t StubContent[6]; static const uint8_t StubContent[6];
Section *GOTSection = nullptr; Section *GOTSection = nullptr;
@ -451,30 +457,31 @@ private:
return getMachOX86RelocationKindName(R); return getMachOX86RelocationKindName(R);
} }
Expected<std::unique_ptr<AtomGraph>> Expected<std::unique_ptr<LinkGraph>>
buildGraph(MemoryBufferRef ObjBuffer) override { buildGraph(MemoryBufferRef ObjBuffer) override {
auto MachOObj = object::ObjectFile::createMachOObjectFile(ObjBuffer); auto MachOObj = object::ObjectFile::createMachOObjectFile(ObjBuffer);
if (!MachOObj) if (!MachOObj)
return MachOObj.takeError(); return MachOObj.takeError();
return MachOAtomGraphBuilder_x86_64(**MachOObj).buildGraph(); return MachOLinkGraphBuilder_x86_64(**MachOObj).buildGraph();
} }
static Error targetOutOfRangeError(const Atom &A, const Edge &E) { static Error targetOutOfRangeError(const Block &B, const Edge &E) {
std::string ErrMsg; std::string ErrMsg;
{ {
raw_string_ostream ErrStream(ErrMsg); raw_string_ostream ErrStream(ErrMsg);
ErrStream << "Relocation target out of range: "; ErrStream << "Relocation target out of range: ";
printEdge(ErrStream, A, E, getMachOX86RelocationKindName(E.getKind())); printEdge(ErrStream, B, E, getMachOX86RelocationKindName(E.getKind()));
ErrStream << "\n"; ErrStream << "\n";
} }
return make_error<JITLinkError>(std::move(ErrMsg)); return make_error<JITLinkError>(std::move(ErrMsg));
} }
Error applyFixup(DefinedAtom &A, const Edge &E, char *AtomWorkingMem) const { Error applyFixup(Block &B, const Edge &E, char *BlockWorkingMem) const {
using namespace support; using namespace support;
char *FixupPtr = AtomWorkingMem + E.getOffset(); char *FixupPtr = BlockWorkingMem + E.getOffset();
JITTargetAddress FixupAddress = A.getAddress() + E.getOffset(); JITTargetAddress FixupAddress = B.getAddress() + E.getOffset();
switch (E.getKind()) { switch (E.getKind()) {
case Branch32: case Branch32:
@ -484,7 +491,7 @@ private:
E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
if (Value < std::numeric_limits<int32_t>::min() || if (Value < std::numeric_limits<int32_t>::min() ||
Value > std::numeric_limits<int32_t>::max()) Value > std::numeric_limits<int32_t>::max())
return targetOutOfRangeError(A, E); return targetOutOfRangeError(B, E);
*(little32_t *)FixupPtr = Value; *(little32_t *)FixupPtr = Value;
break; break;
} }
@ -502,7 +509,7 @@ private:
E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend(); E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend();
if (Value < std::numeric_limits<int32_t>::min() || if (Value < std::numeric_limits<int32_t>::min() ||
Value > std::numeric_limits<int32_t>::max()) Value > std::numeric_limits<int32_t>::max())
return targetOutOfRangeError(A, E); return targetOutOfRangeError(B, E);
*(little32_t *)FixupPtr = Value; *(little32_t *)FixupPtr = Value;
break; break;
} }
@ -514,7 +521,7 @@ private:
E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend(); E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend();
if (Value < std::numeric_limits<int32_t>::min() || if (Value < std::numeric_limits<int32_t>::min() ||
Value > std::numeric_limits<int32_t>::max()) Value > std::numeric_limits<int32_t>::max())
return targetOutOfRangeError(A, E); return targetOutOfRangeError(B, E);
*(little32_t *)FixupPtr = Value; *(little32_t *)FixupPtr = Value;
break; break;
} }
@ -531,7 +538,7 @@ private:
if (E.getKind() == Delta32 || E.getKind() == NegDelta32) { if (E.getKind() == Delta32 || E.getKind() == NegDelta32) {
if (Value < std::numeric_limits<int32_t>::min() || if (Value < std::numeric_limits<int32_t>::min() ||
Value > std::numeric_limits<int32_t>::max()) Value > std::numeric_limits<int32_t>::max())
return targetOutOfRangeError(A, E); return targetOutOfRangeError(B, E);
*(little32_t *)FixupPtr = Value; *(little32_t *)FixupPtr = Value;
} else } else
*(little64_t *)FixupPtr = Value; *(little64_t *)FixupPtr = Value;
@ -540,7 +547,7 @@ private:
case Pointer32: { case Pointer32: {
uint64_t Value = E.getTarget().getAddress() + E.getAddend(); uint64_t Value = E.getTarget().getAddress() + E.getAddend();
if (Value > std::numeric_limits<uint32_t>::max()) if (Value > std::numeric_limits<uint32_t>::max())
return targetOutOfRangeError(A, E); return targetOutOfRangeError(B, E);
*(ulittle32_t *)FixupPtr = Value; *(ulittle32_t *)FixupPtr = Value;
break; break;
} }
@ -563,10 +570,10 @@ void jitLink_MachO_x86_64(std::unique_ptr<JITLinkContext> Ctx) {
if (auto MarkLive = Ctx->getMarkLivePass(TT)) if (auto MarkLive = Ctx->getMarkLivePass(TT))
Config.PrePrunePasses.push_back(std::move(MarkLive)); Config.PrePrunePasses.push_back(std::move(MarkLive));
else else
Config.PrePrunePasses.push_back(markAllAtomsLive); Config.PrePrunePasses.push_back(markAllSymbolsLive);
// Add an in-place GOT/Stubs pass. // Add an in-place GOT/Stubs pass.
Config.PostPrunePasses.push_back([](AtomGraph &G) -> Error { Config.PostPrunePasses.push_back([](LinkGraph &G) -> Error {
MachO_x86_64_GOTAndStubsBuilder(G).run(); MachO_x86_64_GOTAndStubsBuilder(G).run();
return Error::success(); return Error::success();
}); });

View File

@ -226,7 +226,7 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolAliasMap &Aliases) {
for (auto &KV : Aliases) for (auto &KV : Aliases)
OS << " " << *KV.first << ": " << KV.second.Aliasee << " " OS << " " << *KV.first << ": " << KV.second.Aliasee << " "
<< KV.second.AliasFlags; << KV.second.AliasFlags;
OS << " }\n"; OS << " }";
return OS; return OS;
} }
@ -378,14 +378,11 @@ Error MaterializationResponsibility::notifyResolved(const SymbolMap &Symbols) {
}); });
#ifndef NDEBUG #ifndef NDEBUG
for (auto &KV : Symbols) { for (auto &KV : Symbols) {
auto WeakFlags = JITSymbolFlags::Weak | JITSymbolFlags::Common;
auto I = SymbolFlags.find(KV.first); auto I = SymbolFlags.find(KV.first);
assert(I != SymbolFlags.end() && assert(I != SymbolFlags.end() &&
"Resolving symbol outside this responsibility set"); "Resolving symbol outside this responsibility set");
if (I->second.isWeak()) assert((KV.second.getFlags() & ~WeakFlags) == (I->second & ~WeakFlags) &&
assert(I->second == (KV.second.getFlags() | JITSymbolFlags::Weak) &&
"Resolving symbol with incorrect flags");
else
assert(I->second == KV.second.getFlags() &&
"Resolving symbol with incorrect flags"); "Resolving symbol with incorrect flags");
} }
#endif #endif
@ -949,11 +946,14 @@ Error JITDylib::resolve(const SymbolMap &Resolved) {
if (SymI->second.getFlags().hasError()) if (SymI->second.getFlags().hasError())
SymbolsInErrorState.insert(KV.first); SymbolsInErrorState.insert(KV.first);
else { else {
assert((KV.second.getFlags() & ~JITSymbolFlags::Weak) == auto Flags = KV.second.getFlags();
(SymI->second.getFlags() & ~JITSymbolFlags::Weak) && Flags &= ~(JITSymbolFlags::Weak | JITSymbolFlags::Common);
assert(Flags == (SymI->second.getFlags() &
~(JITSymbolFlags::Weak | JITSymbolFlags::Common)) &&
"Resolved flags should match the declared flags"); "Resolved flags should match the declared flags");
Worklist.push_back({SymI, KV.second}); Worklist.push_back(
{SymI, JITEvaluatedSymbol(KV.second.getAddress(), Flags)});
} }
} }
@ -970,7 +970,6 @@ Error JITDylib::resolve(const SymbolMap &Resolved) {
// Resolved symbols can not be weak: discard the weak flag. // Resolved symbols can not be weak: discard the weak flag.
JITSymbolFlags ResolvedFlags = ResolvedSym.getFlags(); JITSymbolFlags ResolvedFlags = ResolvedSym.getFlags();
ResolvedFlags &= ~JITSymbolFlags::Weak;
SymI->second.setAddress(ResolvedSym.getAddress()); SymI->second.setAddress(ResolvedSym.getAddress());
SymI->second.setFlags(ResolvedFlags); SymI->second.setFlags(ResolvedFlags);
SymI->second.setState(SymbolState::Resolved); SymI->second.setState(SymbolState::Resolved);

View File

@ -41,7 +41,7 @@ public:
} }
void lookup(const DenseSet<StringRef> &Symbols, void lookup(const DenseSet<StringRef> &Symbols,
JITLinkAsyncLookupContinuation LookupContinuation) override { std::unique_ptr<JITLinkAsyncLookupContinuation> LC) override {
JITDylibSearchList SearchOrder; JITDylibSearchList SearchOrder;
MR.getTargetJITDylib().withSearchOrderDo( MR.getTargetJITDylib().withSearchOrderDo(
@ -54,19 +54,16 @@ public:
InternedSymbols.insert(ES.intern(S)); InternedSymbols.insert(ES.intern(S));
// OnResolve -- De-intern the symbols and pass the result to the linker. // OnResolve -- De-intern the symbols and pass the result to the linker.
// FIXME: Capture LookupContinuation by move once we have c++14. auto OnResolve = [this, LookupContinuation = std::move(LC)](
auto SharedLookupContinuation = Expected<SymbolMap> Result) mutable {
std::make_shared<JITLinkAsyncLookupContinuation>(
std::move(LookupContinuation));
auto OnResolve = [this, SharedLookupContinuation](Expected<SymbolMap> Result) {
auto Main = Layer.getExecutionSession().intern("_main"); auto Main = Layer.getExecutionSession().intern("_main");
if (!Result) if (!Result)
(*SharedLookupContinuation)(Result.takeError()); LookupContinuation->run(Result.takeError());
else { else {
AsyncLookupResult LR; AsyncLookupResult LR;
for (auto &KV : *Result) for (auto &KV : *Result)
LR[*KV.first] = KV.second; LR[*KV.first] = KV.second;
(*SharedLookupContinuation)(std::move(LR)); LookupContinuation->run(std::move(LR));
} }
}; };
@ -76,29 +73,25 @@ public:
}); });
} }
void notifyResolved(AtomGraph &G) override { void notifyResolved(LinkGraph &G) override {
auto &ES = Layer.getExecutionSession(); auto &ES = Layer.getExecutionSession();
SymbolFlagsMap ExtraSymbolsToClaim; SymbolFlagsMap ExtraSymbolsToClaim;
bool AutoClaim = Layer.AutoClaimObjectSymbols; bool AutoClaim = Layer.AutoClaimObjectSymbols;
SymbolMap InternedResult; SymbolMap InternedResult;
for (auto *DA : G.defined_atoms()) for (auto *Sym : G.defined_symbols())
if (DA->hasName() && DA->isGlobal()) { if (Sym->hasName() && Sym->getScope() != Scope::Local) {
auto InternedName = ES.intern(DA->getName()); auto InternedName = ES.intern(Sym->getName());
JITSymbolFlags Flags; JITSymbolFlags Flags;
if (DA->isExported()) if (Sym->isCallable())
Flags |= JITSymbolFlags::Exported;
if (DA->isWeak())
Flags |= JITSymbolFlags::Weak;
if (DA->isCallable())
Flags |= JITSymbolFlags::Callable; Flags |= JITSymbolFlags::Callable;
if (DA->isCommon()) if (Sym->getScope() == Scope::Default)
Flags |= JITSymbolFlags::Common; Flags |= JITSymbolFlags::Exported;
InternedResult[InternedName] = InternedResult[InternedName] =
JITEvaluatedSymbol(DA->getAddress(), Flags); JITEvaluatedSymbol(Sym->getAddress(), Flags);
if (AutoClaim && !MR.getSymbols().count(InternedName)) { if (AutoClaim && !MR.getSymbols().count(InternedName)) {
assert(!ExtraSymbolsToClaim.count(InternedName) && assert(!ExtraSymbolsToClaim.count(InternedName) &&
"Duplicate symbol to claim?"); "Duplicate symbol to claim?");
@ -106,17 +99,17 @@ public:
} }
} }
for (auto *A : G.absolute_atoms()) for (auto *Sym : G.absolute_symbols())
if (A->hasName()) { if (Sym->hasName()) {
auto InternedName = ES.intern(A->getName()); auto InternedName = ES.intern(Sym->getName());
JITSymbolFlags Flags; JITSymbolFlags Flags;
Flags |= JITSymbolFlags::Absolute; Flags |= JITSymbolFlags::Absolute;
if (A->isWeak()) if (Sym->isCallable())
Flags |= JITSymbolFlags::Weak;
if (A->isCallable())
Flags |= JITSymbolFlags::Callable; Flags |= JITSymbolFlags::Callable;
if (Sym->getLinkage() == Linkage::Weak)
Flags |= JITSymbolFlags::Weak;
InternedResult[InternedName] = InternedResult[InternedName] =
JITEvaluatedSymbol(A->getAddress(), Flags); JITEvaluatedSymbol(Sym->getAddress(), Flags);
if (AutoClaim && !MR.getSymbols().count(InternedName)) { if (AutoClaim && !MR.getSymbols().count(InternedName)) {
assert(!ExtraSymbolsToClaim.count(InternedName) && assert(!ExtraSymbolsToClaim.count(InternedName) &&
"Duplicate symbol to claim?"); "Duplicate symbol to claim?");
@ -148,17 +141,17 @@ public:
} }
} }
AtomGraphPassFunction getMarkLivePass(const Triple &TT) const override { LinkGraphPassFunction getMarkLivePass(const Triple &TT) const override {
return [this](AtomGraph &G) { return markResponsibilitySymbolsLive(G); }; return [this](LinkGraph &G) { return markResponsibilitySymbolsLive(G); };
} }
Error modifyPassConfig(const Triple &TT, PassConfiguration &Config) override { Error modifyPassConfig(const Triple &TT, PassConfiguration &Config) override {
// Add passes to mark duplicate defs as should-discard, and to walk the // Add passes to mark duplicate defs as should-discard, and to walk the
// atom graph to build the symbol dependence graph. // link graph to build the symbol dependence graph.
Config.PrePrunePasses.push_back( Config.PrePrunePasses.push_back(
[this](AtomGraph &G) { return markSymbolsToDiscard(G); }); [this](LinkGraph &G) { return externalizeWeakAndCommonSymbols(G); });
Config.PostPrunePasses.push_back( Config.PostPrunePasses.push_back(
[this](AtomGraph &G) { return computeNamedSymbolDependencies(G); }); [this](LinkGraph &G) { return computeNamedSymbolDependencies(G); });
Layer.modifyPassConfig(MR, TT, Config); Layer.modifyPassConfig(MR, TT, Config);
@ -166,65 +159,59 @@ public:
} }
private: private:
using AnonAtomNamedDependenciesMap = using AnonToNamedDependenciesMap = DenseMap<const Symbol *, SymbolNameSet>;
DenseMap<const DefinedAtom *, SymbolNameSet>;
Error markSymbolsToDiscard(AtomGraph &G) { Error externalizeWeakAndCommonSymbols(LinkGraph &G) {
auto &ES = Layer.getExecutionSession(); auto &ES = Layer.getExecutionSession();
for (auto *DA : G.defined_atoms()) for (auto *Sym : G.defined_symbols())
if (DA->isWeak() && DA->hasName()) { if (Sym->hasName() && Sym->getLinkage() == Linkage::Weak) {
auto S = ES.intern(DA->getName()); if (!MR.getSymbols().count(ES.intern(Sym->getName())))
auto I = MR.getSymbols().find(S); G.makeExternal(*Sym);
if (I == MR.getSymbols().end())
DA->setShouldDiscard(true);
} }
for (auto *A : G.absolute_atoms()) for (auto *Sym : G.absolute_symbols())
if (A->isWeak() && A->hasName()) { if (Sym->hasName() && Sym->getLinkage() == Linkage::Weak) {
auto S = ES.intern(A->getName()); if (!MR.getSymbols().count(ES.intern(Sym->getName())))
auto I = MR.getSymbols().find(S); G.makeExternal(*Sym);
if (I == MR.getSymbols().end())
A->setShouldDiscard(true);
} }
return Error::success(); return Error::success();
} }
Error markResponsibilitySymbolsLive(AtomGraph &G) const { Error markResponsibilitySymbolsLive(LinkGraph &G) const {
auto &ES = Layer.getExecutionSession(); auto &ES = Layer.getExecutionSession();
for (auto *DA : G.defined_atoms()) for (auto *Sym : G.defined_symbols())
if (DA->hasName() && if (Sym->hasName() && MR.getSymbols().count(ES.intern(Sym->getName())))
MR.getSymbols().count(ES.intern(DA->getName()))) Sym->setLive(true);
DA->setLive(true);
return Error::success(); return Error::success();
} }
Error computeNamedSymbolDependencies(AtomGraph &G) { Error computeNamedSymbolDependencies(LinkGraph &G) {
auto &ES = MR.getTargetJITDylib().getExecutionSession(); auto &ES = MR.getTargetJITDylib().getExecutionSession();
auto AnonDeps = computeAnonDeps(G); auto AnonDeps = computeAnonDeps(G);
for (auto *DA : G.defined_atoms()) { for (auto *Sym : G.defined_symbols()) {
// Skip anonymous and non-global atoms: we do not need dependencies for // Skip anonymous and non-global atoms: we do not need dependencies for
// these. // these.
if (!DA->hasName() || !DA->isGlobal()) if (Sym->getScope() == Scope::Local)
continue; continue;
auto DAName = ES.intern(DA->getName()); auto SymName = ES.intern(Sym->getName());
SymbolNameSet &DADeps = NamedSymbolDeps[DAName]; SymbolNameSet &SymDeps = NamedSymbolDeps[SymName];
for (auto &E : DA->edges()) { for (auto &E : Sym->getBlock().edges()) {
auto &TA = E.getTarget(); auto &TargetSym = E.getTarget();
if (TA.hasName()) if (TargetSym.getScope() != Scope::Local)
DADeps.insert(ES.intern(TA.getName())); SymDeps.insert(ES.intern(TargetSym.getName()));
else { else {
assert(TA.isDefined() && "Anonymous atoms must be defined"); assert(TargetSym.isDefined() &&
auto &DTA = static_cast<DefinedAtom &>(TA); "Anonymous/local symbols must be defined");
auto I = AnonDeps.find(&DTA); auto I = AnonDeps.find(&TargetSym);
if (I != AnonDeps.end()) if (I != AnonDeps.end())
for (auto &S : I->second) for (auto &S : I->second)
DADeps.insert(S); SymDeps.insert(S);
} }
} }
} }
@ -232,58 +219,59 @@ private:
return Error::success(); return Error::success();
} }
AnonAtomNamedDependenciesMap computeAnonDeps(AtomGraph &G) { AnonToNamedDependenciesMap computeAnonDeps(LinkGraph &G) {
auto &ES = MR.getTargetJITDylib().getExecutionSession(); auto &ES = MR.getTargetJITDylib().getExecutionSession();
AnonAtomNamedDependenciesMap DepMap; AnonToNamedDependenciesMap DepMap;
// For all anonymous atoms: // For all anonymous symbols:
// (1) Add their named dependencies. // (1) Add their named dependencies.
// (2) Add them to the worklist for further iteration if they have any // (2) Add them to the worklist for further iteration if they have any
// depend on any other anonymous atoms. // depend on any other anonymous symbols.
struct WorklistEntry { struct WorklistEntry {
WorklistEntry(DefinedAtom *DA, DenseSet<DefinedAtom *> DAAnonDeps) WorklistEntry(Symbol *Sym, DenseSet<Symbol *> SymAnonDeps)
: DA(DA), DAAnonDeps(std::move(DAAnonDeps)) {} : Sym(Sym), SymAnonDeps(std::move(SymAnonDeps)) {}
DefinedAtom *DA = nullptr; Symbol *Sym = nullptr;
DenseSet<DefinedAtom *> DAAnonDeps; DenseSet<Symbol *> SymAnonDeps;
}; };
std::vector<WorklistEntry> Worklist; std::vector<WorklistEntry> Worklist;
for (auto *DA : G.defined_atoms()) for (auto *Sym : G.defined_symbols())
if (!DA->hasName()) { if (!Sym->hasName()) {
auto &DANamedDeps = DepMap[DA]; auto &SymNamedDeps = DepMap[Sym];
DenseSet<DefinedAtom *> DAAnonDeps; DenseSet<Symbol *> SymAnonDeps;
for (auto &E : DA->edges()) { for (auto &E : Sym->getBlock().edges()) {
auto &TA = E.getTarget(); auto &TargetSym = E.getTarget();
if (TA.hasName()) if (TargetSym.hasName())
DANamedDeps.insert(ES.intern(TA.getName())); SymNamedDeps.insert(ES.intern(TargetSym.getName()));
else { else {
assert(TA.isDefined() && "Anonymous atoms must be defined"); assert(TargetSym.isDefined() &&
DAAnonDeps.insert(static_cast<DefinedAtom *>(&TA)); "Anonymous symbols must be defined");
SymAnonDeps.insert(&TargetSym);
} }
} }
if (!DAAnonDeps.empty()) if (!SymAnonDeps.empty())
Worklist.push_back(WorklistEntry(DA, std::move(DAAnonDeps))); Worklist.push_back(WorklistEntry(Sym, std::move(SymAnonDeps)));
} }
// Loop over all anonymous atoms with anonymous dependencies, propagating // Loop over all anonymous symbols with anonymous dependencies, propagating
// their respective *named* dependencies. Iterate until we hit a stable // their respective *named* dependencies. Iterate until we hit a stable
// state. // state.
bool Changed; bool Changed;
do { do {
Changed = false; Changed = false;
for (auto &WLEntry : Worklist) { for (auto &WLEntry : Worklist) {
auto *DA = WLEntry.DA; auto *Sym = WLEntry.Sym;
auto &DANamedDeps = DepMap[DA]; auto &SymNamedDeps = DepMap[Sym];
auto &DAAnonDeps = WLEntry.DAAnonDeps; auto &SymAnonDeps = WLEntry.SymAnonDeps;
for (auto *TA : DAAnonDeps) { for (auto *TargetSym : SymAnonDeps) {
auto I = DepMap.find(TA); auto I = DepMap.find(TargetSym);
if (I != DepMap.end()) if (I != DepMap.end())
for (const auto &S : I->second) for (const auto &S : I->second)
Changed |= DANamedDeps.insert(S).second; Changed |= SymNamedDeps.insert(S).second;
} }
} }
} while (Changed); } while (Changed);
@ -414,7 +402,7 @@ Error ObjectLinkingLayer::removeAllModules() {
} }
EHFrameRegistrationPlugin::EHFrameRegistrationPlugin( EHFrameRegistrationPlugin::EHFrameRegistrationPlugin(
jitlink::EHFrameRegistrar &Registrar) EHFrameRegistrar &Registrar)
: Registrar(Registrar) {} : Registrar(Registrar) {}
void EHFrameRegistrationPlugin::modifyPassConfig( void EHFrameRegistrationPlugin::modifyPassConfig(

View File

@ -1,14 +1,14 @@
# RUN: rm -rf %t && mkdir -p %t # RUN: rm -rf %t && mkdir -p %t
# RUN: llvm-mc -triple=x86_64-apple-macosx10.9 -filetype=obj -o %t/macho_zero_fill_align.o %s # RUN: llvm-mc -triple=x86_64-apple-macosx10.9 -filetype=obj -o %t/macho_zero_fill_align.o %s
# RUN: llvm-jitlink -noexec %t/macho_zero_fill_align.o -entry higher_zero_fill_align # RUN: llvm-jitlink -noexec %t/macho_zero_fill_align.o -entry _higher_zero_fill_align
.section __DATA,__data .section __DATA,__data
.globl low_aligned_data .globl _low_aligned_data
.p2align 0 .p2align 0
low_aligned_data: _low_aligned_data:
.byte 42 .byte 42
.globl higher_zero_fill_align .globl _higher_zero_fill_align
.zerofill __DATA,__zero_fill,higher_zero_fill_align,8,3 .zerofill __DATA,__zero_fill,_higher_zero_fill_align,8,3
.subsections_via_symbols .subsections_via_symbols

View File

@ -26,53 +26,55 @@ static bool isMachOStubsSection(Section &S) {
return S.getName() == "$__STUBS"; return S.getName() == "$__STUBS";
} }
static Expected<Edge &> getFirstRelocationEdge(AtomGraph &G, DefinedAtom &DA) { static Expected<Edge &> getFirstRelocationEdge(LinkGraph &G, Block &B) {
auto EItr = std::find_if(DA.edges().begin(), DA.edges().end(), auto EItr = std::find_if(B.edges().begin(), B.edges().end(),
[](Edge &E) { return E.isRelocation(); }); [](Edge &E) { return E.isRelocation(); });
if (EItr == DA.edges().end()) if (EItr == B.edges().end())
return make_error<StringError>("GOT entry in " + G.getName() + ", \"" + return make_error<StringError>("GOT entry in " + G.getName() + ", \"" +
DA.getSection().getName() + B.getSection().getName() +
"\" has no relocations", "\" has no relocations",
inconvertibleErrorCode()); inconvertibleErrorCode());
return *EItr; return *EItr;
} }
static Expected<Atom &> getMachOGOTTarget(AtomGraph &G, DefinedAtom &DA) { static Expected<Symbol &> getMachOGOTTarget(LinkGraph &G, Block &B) {
auto E = getFirstRelocationEdge(G, DA); auto E = getFirstRelocationEdge(G, B);
if (!E) if (!E)
return E.takeError(); return E.takeError();
auto &TA = E->getTarget(); auto &TargetSym = E->getTarget();
if (!TA.hasName()) if (!TargetSym.hasName())
return make_error<StringError>("GOT entry in " + G.getName() + ", \"" +
DA.getSection().getName() +
"\" points to anonymous "
"atom",
inconvertibleErrorCode());
if (TA.isDefined() || TA.isAbsolute())
return make_error<StringError>( return make_error<StringError>(
"GOT entry \"" + TA.getName() + "\" in " + G.getName() + ", \"" + "GOT entry in " + G.getName() + ", \"" +
DA.getSection().getName() + "\" does not point to an external atom", TargetSym.getBlock().getSection().getName() +
"\" points to anonymous "
"symbol",
inconvertibleErrorCode()); inconvertibleErrorCode());
return TA; if (TargetSym.isDefined() || TargetSym.isAbsolute())
return make_error<StringError>(
"GOT entry \"" + TargetSym.getName() + "\" in " + G.getName() + ", \"" +
TargetSym.getBlock().getSection().getName() +
"\" does not point to an external symbol",
inconvertibleErrorCode());
return TargetSym;
} }
static Expected<Atom &> getMachOStubTarget(AtomGraph &G, DefinedAtom &DA) { static Expected<Symbol &> getMachOStubTarget(LinkGraph &G, Block &B) {
auto E = getFirstRelocationEdge(G, DA); auto E = getFirstRelocationEdge(G, B);
if (!E) if (!E)
return E.takeError(); return E.takeError();
auto &GOTA = E->getTarget(); auto &GOTSym = E->getTarget();
if (!GOTA.isDefined() || if (!GOTSym.isDefined() || !isMachOGOTSection(GOTSym.getBlock().getSection()))
!isMachOGOTSection(static_cast<DefinedAtom &>(GOTA).getSection())) return make_error<StringError>(
return make_error<StringError>("Stubs entry in " + G.getName() + ", \"" + "Stubs entry in " + G.getName() + ", \"" +
DA.getSection().getName() + GOTSym.getBlock().getSection().getName() +
"\" does not point to GOT entry", "\" does not point to GOT entry",
inconvertibleErrorCode()); inconvertibleErrorCode());
return getMachOGOTTarget(G, static_cast<DefinedAtom &>(GOTA)); return getMachOGOTTarget(G, GOTSym.getBlock());
} }
namespace llvm { namespace llvm {
Error registerMachOStubsAndGOT(Session &S, AtomGraph &G) { Error registerMachOStubsAndGOT(Session &S, LinkGraph &G) {
auto FileName = sys::path::filename(G.getName()); auto FileName = sys::path::filename(G.getName());
if (S.FileInfos.count(FileName)) { if (S.FileInfos.count(FileName)) {
return make_error<StringError>("When -check is passed, file names must be " return make_error<StringError>("When -check is passed, file names must be "
@ -88,12 +90,12 @@ Error registerMachOStubsAndGOT(Session &S, AtomGraph &G) {
for (auto &Sec : G.sections()) { for (auto &Sec : G.sections()) {
LLVM_DEBUG({ LLVM_DEBUG({
dbgs() << " Section \"" << Sec.getName() << "\": " dbgs() << " Section \"" << Sec.getName() << "\": "
<< (Sec.atoms_empty() ? "empty. skipping." : "processing...") << (Sec.symbols_empty() ? "empty. skipping." : "processing...")
<< "\n"; << "\n";
}); });
// Skip empty sections. // Skip empty sections.
if (Sec.atoms_empty()) if (Sec.symbols_empty())
continue; continue;
if (FileInfo.SectionInfos.count(Sec.getName())) if (FileInfo.SectionInfos.count(Sec.getName()))
@ -105,54 +107,65 @@ Error registerMachOStubsAndGOT(Session &S, AtomGraph &G) {
bool isGOTSection = isMachOGOTSection(Sec); bool isGOTSection = isMachOGOTSection(Sec);
bool isStubsSection = isMachOStubsSection(Sec); bool isStubsSection = isMachOStubsSection(Sec);
auto *FirstAtom = *Sec.atoms().begin(); bool SectionContainsContent = false;
auto *LastAtom = FirstAtom; bool SectionContainsZeroFill = false;
for (auto *DA : Sec.atoms()) {
if (DA->getAddress() < FirstAtom->getAddress()) auto *FirstSym = *Sec.symbols().begin();
FirstAtom = DA; auto *LastSym = FirstSym;
if (DA->getAddress() > LastAtom->getAddress()) for (auto *Sym : Sec.symbols()) {
LastAtom = DA; if (Sym->getAddress() < FirstSym->getAddress())
FirstSym = Sym;
if (Sym->getAddress() > LastSym->getAddress())
LastSym = Sym;
if (isGOTSection) { if (isGOTSection) {
if (Sec.isZeroFill()) if (Sym->isSymbolZeroFill())
return make_error<StringError>("Content atom in zero-fill section", return make_error<StringError>("zero-fill atom in GOT section",
inconvertibleErrorCode()); inconvertibleErrorCode());
if (auto TA = getMachOGOTTarget(G, *DA)) { if (auto TS = getMachOGOTTarget(G, Sym->getBlock()))
FileInfo.GOTEntryInfos[TA->getName()] = {DA->getContent(), FileInfo.GOTEntryInfos[TS->getName()] = {Sym->getSymbolContent(),
DA->getAddress()}; Sym->getAddress()};
} else
return TA.takeError();
} else if (isStubsSection) {
if (Sec.isZeroFill())
return make_error<StringError>("Content atom in zero-fill section",
inconvertibleErrorCode());
if (auto TA = getMachOStubTarget(G, *DA))
FileInfo.StubInfos[TA->getName()] = {DA->getContent(),
DA->getAddress()};
else else
return TA.takeError(); return TS.takeError();
} else if (DA->hasName() && DA->isGlobal()) { SectionContainsContent = true;
if (DA->isZeroFill()) } else if (isStubsSection) {
S.SymbolInfos[DA->getName()] = {DA->getSize(), DA->getAddress()}; if (Sym->isSymbolZeroFill())
else { return make_error<StringError>("zero-fill atom in Stub section",
if (Sec.isZeroFill())
return make_error<StringError>("Content atom in zero-fill section",
inconvertibleErrorCode()); inconvertibleErrorCode());
S.SymbolInfos[DA->getName()] = {DA->getContent(), DA->getAddress()};
if (auto TS = getMachOStubTarget(G, Sym->getBlock()))
FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
Sym->getAddress()};
else
return TS.takeError();
SectionContainsContent = true;
} else if (Sym->hasName()) {
if (Sym->isSymbolZeroFill()) {
S.SymbolInfos[Sym->getName()] = {Sym->getSize(), Sym->getAddress()};
SectionContainsZeroFill = true;
} else {
S.SymbolInfos[Sym->getName()] = {Sym->getSymbolContent(),
Sym->getAddress()};
SectionContainsContent = true;
} }
} }
} }
JITTargetAddress SecAddr = FirstAtom->getAddress(); JITTargetAddress SecAddr = FirstSym->getAddress();
uint64_t SecSize = (LastAtom->getAddress() + LastAtom->getSize()) - uint64_t SecSize =
FirstAtom->getAddress(); (LastSym->getBlock().getAddress() + LastSym->getBlock().getSize()) -
SecAddr;
if (Sec.isZeroFill()) if (SectionContainsZeroFill && SectionContainsContent)
return make_error<StringError>("Mixed zero-fill and content sections not "
"supported yet",
inconvertibleErrorCode());
if (SectionContainsZeroFill)
FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr}; FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr};
else else
FileInfo.SectionInfos[Sec.getName()] = { FileInfo.SectionInfos[Sec.getName()] = {
StringRef(FirstAtom->getContent().data(), SecSize), SecAddr}; StringRef(FirstSym->getBlock().getContent().data(), SecSize),
SecAddr};
} }
return Error::success(); return Error::success();

View File

@ -86,9 +86,9 @@ static cl::opt<bool> ShowAddrs(
cl::desc("Print registered symbol, section, got and stub addresses"), cl::desc("Print registered symbol, section, got and stub addresses"),
cl::init(false)); cl::init(false));
static cl::opt<bool> ShowAtomGraph( static cl::opt<bool> ShowLinkGraph(
"show-graph", "show-graph",
cl::desc("Print the atom graph after fixups have been applied"), cl::desc("Print the link graph after fixups have been applied"),
cl::init(false)); cl::init(false));
static cl::opt<bool> ShowSizes( static cl::opt<bool> ShowSizes(
@ -151,17 +151,14 @@ operator<<(raw_ostream &OS, const Session::FileInfoMap &FIM) {
return OS; return OS;
} }
static uint64_t computeTotalAtomSizes(AtomGraph &G) { static uint64_t computeTotalBlockSizes(LinkGraph &G) {
uint64_t TotalSize = 0; uint64_t TotalSize = 0;
for (auto *DA : G.defined_atoms()) for (auto *B : G.blocks())
if (DA->isZeroFill()) TotalSize += B->getSize();
TotalSize += DA->getZeroFillSize();
else
TotalSize += DA->getContent().size();
return TotalSize; return TotalSize;
} }
static void dumpSectionContents(raw_ostream &OS, AtomGraph &G) { static void dumpSectionContents(raw_ostream &OS, LinkGraph &G) {
constexpr JITTargetAddress DumpWidth = 16; constexpr JITTargetAddress DumpWidth = 16;
static_assert(isPowerOf2_64(DumpWidth), "DumpWidth must be a power of two"); static_assert(isPowerOf2_64(DumpWidth), "DumpWidth must be a power of two");
@ -172,56 +169,55 @@ static void dumpSectionContents(raw_ostream &OS, AtomGraph &G) {
std::sort(Sections.begin(), Sections.end(), std::sort(Sections.begin(), Sections.end(),
[](const Section *LHS, const Section *RHS) { [](const Section *LHS, const Section *RHS) {
if (LHS->atoms_empty() && RHS->atoms_empty()) if (LHS->symbols_empty() && RHS->symbols_empty())
return false; return false;
if (LHS->atoms_empty()) if (LHS->symbols_empty())
return false; return false;
if (RHS->atoms_empty()) if (RHS->symbols_empty())
return true; return true;
return (*LHS->atoms().begin())->getAddress() < SectionRange LHSRange(*LHS);
(*RHS->atoms().begin())->getAddress(); SectionRange RHSRange(*RHS);
return LHSRange.getStart() < RHSRange.getStart();
}); });
for (auto *S : Sections) { for (auto *S : Sections) {
OS << S->getName() << " content:"; OS << S->getName() << " content:";
if (S->atoms_empty()) { if (S->symbols_empty()) {
OS << "\n section empty\n"; OS << "\n section empty\n";
continue; continue;
} }
// Sort atoms into order, then render. // Sort symbols into order, then render.
std::vector<DefinedAtom *> Atoms(S->atoms().begin(), S->atoms().end()); std::vector<Symbol *> Syms(S->symbols().begin(), S->symbols().end());
std::sort(Atoms.begin(), Atoms.end(), llvm::sort(Syms, [](const Symbol *LHS, const Symbol *RHS) {
[](const DefinedAtom *LHS, const DefinedAtom *RHS) {
return LHS->getAddress() < RHS->getAddress(); return LHS->getAddress() < RHS->getAddress();
}); });
JITTargetAddress NextAddr = Atoms.front()->getAddress() & ~(DumpWidth - 1); JITTargetAddress NextAddr = Syms.front()->getAddress() & ~(DumpWidth - 1);
for (auto *DA : Atoms) { for (auto *Sym : Syms) {
bool IsZeroFill = DA->isZeroFill(); bool IsZeroFill = Sym->getBlock().isZeroFill();
JITTargetAddress AtomStart = DA->getAddress(); JITTargetAddress SymStart = Sym->getAddress();
JITTargetAddress AtomSize = JITTargetAddress SymSize = Sym->getSize();
IsZeroFill ? DA->getZeroFillSize() : DA->getContent().size(); JITTargetAddress SymEnd = SymStart + SymSize;
JITTargetAddress AtomEnd = AtomStart + AtomSize; const uint8_t *SymData =
const uint8_t *AtomData = IsZeroFill ? nullptr : Sym->getSymbolContent().bytes_begin();
IsZeroFill ? nullptr : DA->getContent().bytes_begin();
// Pad any space before the atom starts. // Pad any space before the symbol starts.
while (NextAddr != AtomStart) { while (NextAddr != SymStart) {
if (NextAddr % DumpWidth == 0) if (NextAddr % DumpWidth == 0)
OS << formatv("\n{0:x16}:", NextAddr); OS << formatv("\n{0:x16}:", NextAddr);
OS << " "; OS << " ";
++NextAddr; ++NextAddr;
} }
// Render the atom content. // Render the symbol content.
while (NextAddr != AtomEnd) { while (NextAddr != SymEnd) {
if (NextAddr % DumpWidth == 0) if (NextAddr % DumpWidth == 0)
OS << formatv("\n{0:x16}:", NextAddr); OS << formatv("\n{0:x16}:", NextAddr);
if (IsZeroFill) if (IsZeroFill)
OS << " 00"; OS << " 00";
else else
OS << formatv(" {0:x-2}", AtomData[NextAddr - AtomStart]); OS << formatv(" {0:x-2}", SymData[NextAddr - SymStart]);
++NextAddr; ++NextAddr;
} }
} }
@ -291,18 +287,17 @@ public:
for (auto &KV : Request) { for (auto &KV : Request) {
auto &Seg = KV.second; auto &Seg = KV.second;
if (Seg.getContentAlignment() > PageSize) if (Seg.getAlignment() > PageSize)
return make_error<StringError>("Cannot request higher than page " return make_error<StringError>("Cannot request higher than page "
"alignment", "alignment",
inconvertibleErrorCode()); inconvertibleErrorCode());
if (PageSize % Seg.getContentAlignment() != 0) if (PageSize % Seg.getAlignment() != 0)
return make_error<StringError>("Page size is not a multiple of " return make_error<StringError>("Page size is not a multiple of "
"alignment", "alignment",
inconvertibleErrorCode()); inconvertibleErrorCode());
uint64_t ZeroFillStart = uint64_t ZeroFillStart = Seg.getContentSize();
alignTo(Seg.getContentSize(), Seg.getZeroFillAlignment());
uint64_t SegmentSize = ZeroFillStart + Seg.getZeroFillSize(); uint64_t SegmentSize = ZeroFillStart + Seg.getZeroFillSize();
// Round segment size up to page boundary. // Round segment size up to page boundary.
@ -427,7 +422,7 @@ void Session::dumpSessionInfo(raw_ostream &OS) {
void Session::modifyPassConfig(const Triple &FTT, void Session::modifyPassConfig(const Triple &FTT,
PassConfiguration &PassConfig) { PassConfiguration &PassConfig) {
if (!CheckFiles.empty()) if (!CheckFiles.empty())
PassConfig.PostFixupPasses.push_back([this](AtomGraph &G) { PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) {
if (TT.getObjectFormat() == Triple::MachO) if (TT.getObjectFormat() == Triple::MachO)
return registerMachOStubsAndGOT(*this, G); return registerMachOStubsAndGOT(*this, G);
return make_error<StringError>("Unsupported object format for GOT/stub " return make_error<StringError>("Unsupported object format for GOT/stub "
@ -435,27 +430,26 @@ void Session::modifyPassConfig(const Triple &FTT,
inconvertibleErrorCode()); inconvertibleErrorCode());
}); });
if (ShowAtomGraph) if (ShowLinkGraph)
PassConfig.PostFixupPasses.push_back([](AtomGraph &G) -> Error { PassConfig.PostFixupPasses.push_back([](LinkGraph &G) -> Error {
outs() << "Atom graph post-fixup:\n"; outs() << "Link graph post-fixup:\n";
G.dump(outs()); G.dump(outs());
return Error::success(); return Error::success();
}); });
if (ShowSizes) { if (ShowSizes) {
PassConfig.PrePrunePasses.push_back([this](AtomGraph &G) -> Error { PassConfig.PrePrunePasses.push_back([this](LinkGraph &G) -> Error {
SizeBeforePruning += computeTotalAtomSizes(G); SizeBeforePruning += computeTotalBlockSizes(G);
return Error::success(); return Error::success();
}); });
PassConfig.PostFixupPasses.push_back([this](AtomGraph &G) -> Error { PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) -> Error {
SizeAfterFixups += computeTotalAtomSizes(G); SizeAfterFixups += computeTotalBlockSizes(G);
return Error::success(); return Error::success();
}); });
} }
if (ShowRelocatedSectionContents) if (ShowRelocatedSectionContents)
PassConfig.PostFixupPasses.push_back([](AtomGraph &G) -> Error { PassConfig.PostFixupPasses.push_back([](LinkGraph &G) -> Error {
outs() << "Relocated section contents for " << G.getName() << ":\n"; outs() << "Relocated section contents for " << G.getName() << ":\n";
dumpSectionContents(outs(), G); dumpSectionContents(outs(), G);
return Error::success(); return Error::success();
@ -757,8 +751,8 @@ Error runChecks(Session &S) {
static void dumpSessionStats(Session &S) { static void dumpSessionStats(Session &S) {
if (ShowSizes) if (ShowSizes)
outs() << "Total size of all atoms before pruning: " << S.SizeBeforePruning outs() << "Total size of all blocks before pruning: " << S.SizeBeforePruning
<< "\nTotal size of all atoms after fixups: " << S.SizeAfterFixups << "\nTotal size of all blocks after fixups: " << S.SizeAfterFixups
<< "\n"; << "\n";
} }

View File

@ -65,7 +65,7 @@ struct Session {
uint64_t SizeAfterFixups = 0; uint64_t SizeAfterFixups = 0;
}; };
Error registerMachOStubsAndGOT(Session &S, jitlink::AtomGraph &G); Error registerMachOStubsAndGOT(Session &S, jitlink::LinkGraph &G);
} // end namespace llvm } // end namespace llvm

View File

@ -145,7 +145,7 @@ void JITLinkTestCommon::TestJITLinkContext::notifyFailed(Error Err) {
void JITLinkTestCommon::TestJITLinkContext::lookup( void JITLinkTestCommon::TestJITLinkContext::lookup(
const DenseSet<StringRef> &Symbols, const DenseSet<StringRef> &Symbols,
JITLinkAsyncLookupContinuation LookupContinuation) { std::unique_ptr<JITLinkAsyncLookupContinuation> LC) {
jitlink::AsyncLookupResult LookupResult; jitlink::AsyncLookupResult LookupResult;
DenseSet<StringRef> MissingSymbols; DenseSet<StringRef> MissingSymbols;
for (const auto &Symbol : Symbols) { for (const auto &Symbol : Symbols) {
@ -157,7 +157,7 @@ void JITLinkTestCommon::TestJITLinkContext::lookup(
} }
if (MissingSymbols.empty()) if (MissingSymbols.empty())
LookupContinuation(std::move(LookupResult)); LC->run(std::move(LookupResult));
else { else {
std::string ErrMsg; std::string ErrMsg;
{ {
@ -167,12 +167,12 @@ void JITLinkTestCommon::TestJITLinkContext::lookup(
ErrMsgStream << " " << Sym; ErrMsgStream << " " << Sym;
ErrMsgStream << " ]\n"; ErrMsgStream << " ]\n";
} }
LookupContinuation( LC->run(
make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode())); make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode()));
} }
} }
void JITLinkTestCommon::TestJITLinkContext::notifyResolved(AtomGraph &G) { void JITLinkTestCommon::TestJITLinkContext::notifyResolved(LinkGraph &G) {
if (NotifyResolved) if (NotifyResolved)
NotifyResolved(G); NotifyResolved(G);
} }
@ -186,7 +186,7 @@ void JITLinkTestCommon::TestJITLinkContext::notifyFinalized(
Error JITLinkTestCommon::TestJITLinkContext::modifyPassConfig( Error JITLinkTestCommon::TestJITLinkContext::modifyPassConfig(
const Triple &TT, PassConfiguration &Config) { const Triple &TT, PassConfiguration &Config) {
if (TestCase) if (TestCase)
Config.PostFixupPasses.push_back([&](AtomGraph &G) -> Error { Config.PostFixupPasses.push_back([&](LinkGraph &G) -> Error {
TestCase(G); TestCase(G);
return Error::success(); return Error::success();
}); });
@ -196,11 +196,11 @@ Error JITLinkTestCommon::TestJITLinkContext::modifyPassConfig(
JITLinkTestCommon::JITLinkTestCommon() { initializeLLVMTargets(); } JITLinkTestCommon::JITLinkTestCommon() { initializeLLVMTargets(); }
Expected<std::pair<MCInst, size_t>> Expected<std::pair<MCInst, size_t>>
JITLinkTestCommon::disassemble(const MCDisassembler &Dis, JITLinkTestCommon::disassemble(const MCDisassembler &Dis, jitlink::Block &B,
jitlink::DefinedAtom &Atom, size_t Offset) { size_t Offset) {
ArrayRef<uint8_t> InstBuffer( ArrayRef<uint8_t> InstBuffer(
reinterpret_cast<const uint8_t *>(Atom.getContent().data()) + Offset, reinterpret_cast<const uint8_t *>(B.getContent().data()) + Offset,
Atom.getContent().size() - Offset); B.getContent().size() - Offset);
MCInst Inst; MCInst Inst;
uint64_t InstSize; uint64_t InstSize;
@ -214,11 +214,9 @@ JITLinkTestCommon::disassemble(const MCDisassembler &Dis,
return std::make_pair(Inst, InstSize); return std::make_pair(Inst, InstSize);
} }
Expected<int64_t> Expected<int64_t> JITLinkTestCommon::decodeImmediateOperand(
JITLinkTestCommon::decodeImmediateOperand(const MCDisassembler &Dis, const MCDisassembler &Dis, jitlink::Block &B, size_t OpIdx, size_t Offset) {
jitlink::DefinedAtom &Atom, auto InstAndSize = disassemble(Dis, B, Offset);
size_t OpIdx, size_t Offset) {
auto InstAndSize = disassemble(Dis, Atom, Offset);
if (!InstAndSize) if (!InstAndSize)
return InstAndSize.takeError(); return InstAndSize.takeError();

View File

@ -77,9 +77,9 @@ public:
class TestJITLinkContext : public jitlink::JITLinkContext { class TestJITLinkContext : public jitlink::JITLinkContext {
public: public:
using TestCaseFunction = std::function<void(jitlink::AtomGraph &)>; using TestCaseFunction = std::function<void(jitlink::LinkGraph &)>;
using NotifyResolvedFunction = std::function<void(jitlink::AtomGraph &G)>; using NotifyResolvedFunction = std::function<void(jitlink::LinkGraph &G)>;
using NotifyFinalizedFunction = std::function<void( using NotifyFinalizedFunction = std::function<void(
std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation>)>; std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation>)>;
@ -103,11 +103,11 @@ public:
void notifyFailed(Error Err) override; void notifyFailed(Error Err) override;
void void lookup(
lookup(const DenseSet<StringRef> &Symbols, const DenseSet<StringRef> &Symbols,
jitlink::JITLinkAsyncLookupContinuation LookupContinuation) override; std::unique_ptr<jitlink::JITLinkAsyncLookupContinuation> LC) override;
void notifyResolved(jitlink::AtomGraph &G) override; void notifyResolved(jitlink::LinkGraph &G) override;
void notifyFinalized( void notifyFinalized(
std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation> A) override; std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation> A) override;
@ -140,56 +140,60 @@ public:
} }
template <typename T> template <typename T>
static Expected<T> readInt(jitlink::AtomGraph &G, jitlink::DefinedAtom &A, static Expected<T> readInt(jitlink::LinkGraph &G, jitlink::Block &B,
size_t Offset = 0) { size_t Offset = 0) {
if (Offset + sizeof(T) > A.getContent().size()) if (Offset + sizeof(T) > B.getSize())
return make_error<StringError>("Reading past end of atom content", return make_error<StringError>("Reading past end of block content",
inconvertibleErrorCode()); inconvertibleErrorCode());
return support::endian::read<T, 1>(A.getContent().data() + Offset, return support::endian::read<T, 1>(B.getContent().data() + Offset,
G.getEndianness()); G.getEndianness());
} }
template <typename T> template <typename T>
static Expected<T> readInt(jitlink::AtomGraph &G, StringRef AtomName, static Expected<T> readInt(jitlink::LinkGraph &G, StringRef SymbolName,
size_t Offset = 0) { size_t Offset = 0) {
auto DA = G.findDefinedAtomByName(AtomName); for (auto *Sym : G.defined_symbols()) {
if (!DA) if (Sym->getName() == SymbolName)
return DA.takeError(); return readInt<T>(G, Sym->getBlock(), Sym->getOffset() + Offset);
return readInt<T>(G, *DA); }
return make_error<StringError>("Symbol \"" + SymbolName + "\" not found",
inconvertibleErrorCode());
} }
static Expected<std::pair<MCInst, size_t>> static Expected<std::pair<MCInst, size_t>>
disassemble(const MCDisassembler &Dis, jitlink::DefinedAtom &Atom, disassemble(const MCDisassembler &Dis, jitlink::Block &B, size_t Offset = 0);
size_t Offset = 0);
static Expected<int64_t> decodeImmediateOperand(const MCDisassembler &Dis, static Expected<int64_t> decodeImmediateOperand(const MCDisassembler &Dis,
jitlink::DefinedAtom &Atom, jitlink::Block &B,
size_t OpIdx, size_t OpIdx,
size_t Offset = 0); size_t Offset = 0);
static jitlink::Atom &atom(jitlink::AtomGraph &G, StringRef Name) { static jitlink::Symbol &symbol(jitlink::LinkGraph &G, StringRef Name) {
return G.getAtomByName(Name); for (auto *Sym : G.defined_symbols())
if (Sym->getName() == Name)
return *Sym;
for (auto *Sym : G.external_symbols())
if (Sym->getName() == Name)
return *Sym;
for (auto *Sym : G.absolute_symbols())
if (Sym->getName() == Name)
return *Sym;
llvm_unreachable("Name must reference a symbol");
} }
static jitlink::DefinedAtom &definedAtom(jitlink::AtomGraph &G, static JITTargetAddress symbolAddr(jitlink::LinkGraph &G, StringRef Name) {
StringRef Name) { return symbol(G, Name).getAddress();
return G.getDefinedAtomByName(Name);
}
static JITTargetAddress atomAddr(jitlink::AtomGraph &G, StringRef Name) {
return atom(G, Name).getAddress();
} }
template <typename PredT> template <typename PredT>
static size_t countEdgesMatching(jitlink::DefinedAtom &DA, static size_t countEdgesMatching(jitlink::Block &B, const PredT &Pred) {
const PredT &Pred) { return std::count_if(B.edges().begin(), B.edges().end(), Pred);
return std::count_if(DA.edges().begin(), DA.edges().end(), Pred);
} }
template <typename PredT> template <typename PredT>
static size_t countEdgesMatching(jitlink::AtomGraph &G, StringRef Name, static size_t countEdgesMatching(jitlink::LinkGraph &G, StringRef Name,
const PredT &Pred) { const PredT &Pred) {
return countEdgesMatching(definedAtom(G, Name), Pred); return countEdgesMatching(symbol(G, Name), Pred);
} }
private: private:

View File

@ -24,7 +24,7 @@ class JITLinkTest_MachO_x86_64 : public JITLinkTestCommon,
public testing::Test { public testing::Test {
public: public:
using BasicVerifyGraphFunction = using BasicVerifyGraphFunction =
std::function<void(AtomGraph &, const MCDisassembler &)>; std::function<void(LinkGraph &, const MCDisassembler &)>;
void runBasicVerifyGraphTest(StringRef AsmSrc, StringRef Triple, void runBasicVerifyGraphTest(StringRef AsmSrc, StringRef Triple,
StringMap<JITEvaluatedSymbol> Externals, StringMap<JITEvaluatedSymbol> Externals,
@ -40,7 +40,7 @@ public:
} }
auto JTCtx = std::make_unique<TestJITLinkContext>( auto JTCtx = std::make_unique<TestJITLinkContext>(
**TR, [&](AtomGraph &G) { RunGraphTest(G, (*TR)->getDisassembler()); }); **TR, [&](LinkGraph &G) { RunGraphTest(G, (*TR)->getDisassembler()); });
JTCtx->externals() = std::move(Externals); JTCtx->externals() = std::move(Externals);
@ -48,78 +48,77 @@ public:
} }
protected: protected:
static void verifyIsPointerTo(AtomGraph &G, DefinedAtom &A, Atom &Target) { static void verifyIsPointerTo(LinkGraph &G, Block &B, Symbol &Target) {
EXPECT_EQ(A.edges_size(), 1U) << "Incorrect number of edges for pointer"; EXPECT_EQ(B.edges_size(), 1U) << "Incorrect number of edges for pointer";
if (A.edges_size() != 1U) if (B.edges_size() != 1U)
return; return;
auto &E = *A.edges().begin(); auto &E = *B.edges().begin();
EXPECT_EQ(E.getOffset(), 0U) << "Expected edge offset of zero";
EXPECT_EQ(E.getKind(), Pointer64) EXPECT_EQ(E.getKind(), Pointer64)
<< "Expected pointer to have a pointer64 relocation"; << "Expected pointer to have a pointer64 relocation";
EXPECT_EQ(&E.getTarget(), &Target) << "Expected edge to point at target"; EXPECT_EQ(&E.getTarget(), &Target) << "Expected edge to point at target";
EXPECT_THAT_EXPECTED(readInt<uint64_t>(G, A), HasValue(Target.getAddress())) EXPECT_THAT_EXPECTED(readInt<uint64_t>(G, B), HasValue(Target.getAddress()))
<< "Pointer does not point to target"; << "Pointer does not point to target";
} }
static void verifyGOTLoad(AtomGraph &G, DefinedAtom &A, Edge &E, static void verifyGOTLoad(LinkGraph &G, Edge &E, Symbol &Target) {
Atom &Target) {
EXPECT_EQ(E.getAddend(), 0U) << "Expected GOT load to have a zero addend"; EXPECT_EQ(E.getAddend(), 0U) << "Expected GOT load to have a zero addend";
EXPECT_TRUE(E.getTarget().isDefined()) EXPECT_TRUE(E.getTarget().isDefined())
<< "GOT entry should be a defined atom"; << "GOT entry should be a defined symbol";
if (!E.getTarget().isDefined()) if (!E.getTarget().isDefined())
return; return;
verifyIsPointerTo(G, static_cast<DefinedAtom &>(E.getTarget()), Target); verifyIsPointerTo(G, E.getTarget().getBlock(), Target);
} }
static void verifyCall(const MCDisassembler &Dis, AtomGraph &G, static void verifyCall(const MCDisassembler &Dis, LinkGraph &G,
DefinedAtom &Caller, Edge &E, Atom &Callee) { Block &CallerBlock, Edge &E, Symbol &Callee) {
EXPECT_EQ(E.getKind(), Branch32) << "Edge is not a Branch32"; EXPECT_EQ(E.getKind(), Branch32) << "Edge is not a Branch32";
EXPECT_EQ(E.getAddend(), 0U) << "Expected no addend on stub call"; EXPECT_EQ(E.getAddend(), 0U) << "Expected no addend on stub call";
EXPECT_EQ(&E.getTarget(), &Callee) EXPECT_EQ(&E.getTarget(), &Callee)
<< "Edge does not point at expected callee"; << "Edge does not point at expected callee";
JITTargetAddress FixupAddress = Caller.getAddress() + E.getOffset(); JITTargetAddress FixupAddress = CallerBlock.getAddress() + E.getOffset();
uint64_t PCRelDelta = Callee.getAddress() - (FixupAddress + 4); uint64_t PCRelDelta = Callee.getAddress() - (FixupAddress + 4);
EXPECT_THAT_EXPECTED( EXPECT_THAT_EXPECTED(
decodeImmediateOperand(Dis, Caller, 0, E.getOffset() - 1), decodeImmediateOperand(Dis, CallerBlock, 0, E.getOffset() - 1),
HasValue(PCRelDelta)); HasValue(PCRelDelta));
} }
static void verifyIndirectCall(const MCDisassembler &Dis, AtomGraph &G, static void verifyIndirectCall(const MCDisassembler &Dis, LinkGraph &G,
DefinedAtom &Caller, Edge &E, Atom &Callee) { Block &CallerBlock, Edge &E, Symbol &Callee) {
EXPECT_EQ(E.getKind(), PCRel32) << "Edge is not a PCRel32"; EXPECT_EQ(E.getKind(), PCRel32) << "Edge is not a PCRel32";
EXPECT_EQ(E.getAddend(), 0) << "Expected no addend on stub cal"; EXPECT_EQ(E.getAddend(), 0) << "Expected no addend on stub cal";
EXPECT_TRUE(E.getTarget().isDefined()) << "Target is not a defined atom"; EXPECT_TRUE(E.getTarget().isDefined()) << "Target is not a defined symbol";
if (!E.getTarget().isDefined()) if (!E.getTarget().isDefined())
return; return;
verifyIsPointerTo(G, static_cast<DefinedAtom &>(E.getTarget()), Callee); verifyIsPointerTo(G, E.getTarget().getBlock(), Callee);
JITTargetAddress FixupAddress = Caller.getAddress() + E.getOffset(); JITTargetAddress FixupAddress = CallerBlock.getAddress() + E.getOffset();
uint64_t PCRelDelta = E.getTarget().getAddress() - (FixupAddress + 4); uint64_t PCRelDelta = E.getTarget().getAddress() - (FixupAddress + 4);
EXPECT_THAT_EXPECTED( EXPECT_THAT_EXPECTED(
decodeImmediateOperand(Dis, Caller, 3, E.getOffset() - 2), decodeImmediateOperand(Dis, CallerBlock, 3, E.getOffset() - 2),
HasValue(PCRelDelta)); HasValue(PCRelDelta));
} }
static void verifyCallViaStub(const MCDisassembler &Dis, AtomGraph &G, static void verifyCallViaStub(const MCDisassembler &Dis, LinkGraph &G,
DefinedAtom &Caller, Edge &E, Atom &Callee) { Block &CallerBlock, Edge &E, Symbol &Callee) {
verifyCall(Dis, G, Caller, E, E.getTarget()); verifyCall(Dis, G, CallerBlock, E, E.getTarget());
if (!E.getTarget().isDefined()) { if (!E.getTarget().isDefined()) {
ADD_FAILURE() << "Edge target is not a stub"; ADD_FAILURE() << "Edge target is not a stub";
return; return;
} }
auto &StubAtom = static_cast<DefinedAtom &>(E.getTarget()); auto &StubBlock = E.getTarget().getBlock();
EXPECT_EQ(StubAtom.edges_size(), 1U) EXPECT_EQ(StubBlock.edges_size(), 1U)
<< "Expected one edge from stub to target"; << "Expected one edge from stub to target";
auto &StubEdge = *StubAtom.edges().begin(); auto &StubEdge = *StubBlock.edges().begin();
verifyIndirectCall(Dis, G, static_cast<DefinedAtom &>(StubAtom), StubEdge, verifyIndirectCall(Dis, G, StubBlock, StubEdge, Callee);
Callee);
} }
}; };
@ -161,24 +160,24 @@ TEST_F(JITLinkTest_MachO_x86_64, BasicRelocations) {
{{"_y", JITEvaluatedSymbol(0xdeadbeef, JITSymbolFlags::Exported)}, {{"_y", JITEvaluatedSymbol(0xdeadbeef, JITSymbolFlags::Exported)},
{"_baz", JITEvaluatedSymbol(0xcafef00d, JITSymbolFlags::Exported)}}, {"_baz", JITEvaluatedSymbol(0xcafef00d, JITSymbolFlags::Exported)}},
true, false, MCTargetOptions(), true, false, MCTargetOptions(),
[](AtomGraph &G, const MCDisassembler &Dis) { [](LinkGraph &G, const MCDisassembler &Dis) {
// Name the atoms in the asm above. // Name the symbols in the asm above.
auto &Baz = atom(G, "_baz"); auto &Baz = symbol(G, "_baz");
auto &Y = atom(G, "_y"); auto &Y = symbol(G, "_y");
auto &Bar = symbol(G, "_bar");
auto &Bar = definedAtom(G, "_bar"); auto &Foo = symbol(G, "_foo");
auto &Foo = definedAtom(G, "_foo"); auto &Foo_1 = symbol(G, "_foo.1");
auto &Foo_1 = definedAtom(G, "_foo.1"); auto &Foo_2 = symbol(G, "_foo.2");
auto &Foo_2 = definedAtom(G, "_foo.2"); auto &X = symbol(G, "_x");
auto &X = definedAtom(G, "_x"); auto &P = symbol(G, "_p");
auto &P = definedAtom(G, "_p");
// Check unsigned reloc for _p // Check unsigned reloc for _p
{ {
EXPECT_EQ(P.edges_size(), 1U) << "Unexpected number of relocations"; EXPECT_EQ(P.getBlock().edges_size(), 1U)
EXPECT_EQ(P.edges().begin()->getKind(), Pointer64) << "Unexpected number of relocations";
EXPECT_EQ(P.getBlock().edges().begin()->getKind(), Pointer64)
<< "Unexpected edge kind for _p"; << "Unexpected edge kind for _p";
EXPECT_THAT_EXPECTED(readInt<uint64_t>(G, P), EXPECT_THAT_EXPECTED(readInt<uint64_t>(G, P.getBlock()),
HasValue(X.getAddress())) HasValue(X.getAddress()))
<< "Unsigned relocation did not apply correctly"; << "Unsigned relocation did not apply correctly";
} }
@ -188,40 +187,44 @@ TEST_F(JITLinkTest_MachO_x86_64, BasicRelocations) {
// indirect call, and that the pointer for the indirect call points to // indirect call, and that the pointer for the indirect call points to
// baz. // baz.
{ {
EXPECT_EQ(Bar.edges_size(), 1U) EXPECT_EQ(Bar.getBlock().edges_size(), 1U)
<< "Incorrect number of edges for bar"; << "Incorrect number of edges for bar";
EXPECT_EQ(Bar.edges().begin()->getKind(), Branch32) EXPECT_EQ(Bar.getBlock().edges().begin()->getKind(), Branch32)
<< "Unexpected edge kind for _bar"; << "Unexpected edge kind for _bar";
verifyCallViaStub(Dis, G, Bar, *Bar.edges().begin(), Baz); verifyCallViaStub(Dis, G, Bar.getBlock(),
*Bar.getBlock().edges().begin(), Baz);
} }
// Check that _foo is a direct call to _bar. // Check that _foo is a direct call to _bar.
{ {
EXPECT_EQ(Foo.edges_size(), 1U) EXPECT_EQ(Foo.getBlock().edges_size(), 1U)
<< "Incorrect number of edges for foo"; << "Incorrect number of edges for foo";
EXPECT_EQ(Foo.edges().begin()->getKind(), Branch32); EXPECT_EQ(Foo.getBlock().edges().begin()->getKind(), Branch32);
verifyCall(Dis, G, Foo, *Foo.edges().begin(), Bar); verifyCall(Dis, G, Foo.getBlock(), *Foo.getBlock().edges().begin(),
Bar);
} }
// Check .got load in _foo.1 // Check .got load in _foo.1
{ {
EXPECT_EQ(Foo_1.edges_size(), 1U) EXPECT_EQ(Foo_1.getBlock().edges_size(), 1U)
<< "Incorrect number of edges for foo_1"; << "Incorrect number of edges for foo_1";
EXPECT_EQ(Foo_1.edges().begin()->getKind(), PCRel32); EXPECT_EQ(Foo_1.getBlock().edges().begin()->getKind(), PCRel32);
verifyGOTLoad(G, Foo_1, *Foo_1.edges().begin(), Y); verifyGOTLoad(G, *Foo_1.getBlock().edges().begin(), Y);
} }
// Check PCRel ref to _p in _foo.2 // Check PCRel ref to _p in _foo.2
{ {
EXPECT_EQ(Foo_2.edges_size(), 1U) EXPECT_EQ(Foo_2.getBlock().edges_size(), 1U)
<< "Incorrect number of edges for foo_2"; << "Incorrect number of edges for foo_2";
EXPECT_EQ(Foo_2.edges().begin()->getKind(), PCRel32); EXPECT_EQ(Foo_2.getBlock().edges().begin()->getKind(), PCRel32);
JITTargetAddress FixupAddress = JITTargetAddress FixupAddress =
Foo_2.getAddress() + Foo_2.edges().begin()->getOffset(); Foo_2.getBlock().getAddress() +
Foo_2.getBlock().edges().begin()->getOffset();
uint64_t PCRelDelta = P.getAddress() - (FixupAddress + 4); uint64_t PCRelDelta = P.getAddress() - (FixupAddress + 4);
EXPECT_THAT_EXPECTED(decodeImmediateOperand(Dis, Foo_2, 4, 0), EXPECT_THAT_EXPECTED(
decodeImmediateOperand(Dis, Foo_2.getBlock(), 4, 0),
HasValue(PCRelDelta)) HasValue(PCRelDelta))
<< "PCRel load does not reference expected target"; << "PCRel load does not reference expected target";
} }