diff options
Diffstat (limited to 'llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp')
-rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp | 131 |
1 files changed, 119 insertions, 12 deletions
diff --git a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp index 69ec72aae292..a91bc3b6033c 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp @@ -95,15 +95,6 @@ private: ", length=" + formatv("{0:d}", RI.r_length)); } - MachO::relocation_info - getRelocationInfo(const object::relocation_iterator RelItr) { - MachO::any_relocation_info ARI = - getObject().getRelocation(RelItr->getRawDataRefImpl()); - MachO::relocation_info RI; - memcpy(&RI, &ARI, sizeof(MachO::relocation_info)); - return RI; - } - using PairRelocInfo = std::tuple<MachOX86RelocationKind, Symbol *, uint64_t>; // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success, @@ -196,6 +187,7 @@ private: JITTargetAddress SectionAddress = S.getAddress(); + // Skip relocations virtual sections. if (S.isVirtual()) { if (S.relocation_begin() != S.relocation_end()) return make_error<JITLinkError>("Virtual section contains " @@ -203,6 +195,21 @@ private: continue; } + // Skip relocations for debug symbols. + { + auto &NSec = + getSectionByIndex(Obj.getSectionIndex(S.getRawDataRefImpl())); + if (!NSec.GraphSection) { + LLVM_DEBUG({ + dbgs() << "Skipping relocations for MachO section " << NSec.SegName + << "/" << NSec.SectName + << " which has no associated graph section\n"; + }); + continue; + } + } + + // Add relocations for section. for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end(); RelItr != RelEnd; ++RelItr) { @@ -350,6 +357,9 @@ private: class MachO_x86_64_GOTAndStubsBuilder : public BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder> { public: + static const uint8_t NullGOTEntryContent[8]; + static const uint8_t StubContent[6]; + MachO_x86_64_GOTAndStubsBuilder(LinkGraph &G) : BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder>(G) {} @@ -367,7 +377,13 @@ public: void fixGOTEdge(Edge &E, Symbol &GOTEntry) { assert((E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad) && "Not a GOT edge?"); - E.setKind(PCRel32); + // If this is a PCRel32GOT then change it to an ordinary PCRel32. If it is + // a PCRel32GOTLoad then leave it as-is for now. We will use the kind to + // check for GOT optimization opportunities in the + // optimizeMachO_x86_64_GOTAndStubs pass below. + if (E.getKind() == PCRel32GOT) + E.setKind(PCRel32); + E.setTarget(GOTEntry); // Leave the edge addend as-is. } @@ -388,6 +404,11 @@ public: void fixExternalBranchEdge(Edge &E, Symbol &Stub) { assert(E.getKind() == Branch32 && "Not a Branch32 edge?"); assert(E.getAddend() == 0 && "Branch32 edge has non-zero addend?"); + + // Set the edge kind to Branch32ToStub. We will use this to check for stub + // optimization opportunities in the optimizeMachO_x86_64_GOTAndStubs pass + // below. + E.setKind(Branch32ToStub); E.setTarget(Stub); } @@ -417,8 +438,6 @@ private: sizeof(StubContent)); } - static const uint8_t NullGOTEntryContent[8]; - static const uint8_t StubContent[6]; Section *GOTSection = nullptr; Section *StubsSection = nullptr; }; @@ -429,6 +448,89 @@ const uint8_t MachO_x86_64_GOTAndStubsBuilder::StubContent[6] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00}; } // namespace +static Error optimizeMachO_x86_64_GOTAndStubs(LinkGraph &G) { + LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n"); + + for (auto *B : G.blocks()) + for (auto &E : B->edges()) + if (E.getKind() == PCRel32GOTLoad) { + assert(E.getOffset() >= 3 && "GOT edge occurs too early in block"); + + // Switch the edge kind to PCRel32: Whether we change the edge target + // or not this will be the desired kind. + E.setKind(PCRel32); + + // Optimize GOT references. + auto &GOTBlock = E.getTarget().getBlock(); + assert(GOTBlock.getSize() == G.getPointerSize() && + "GOT entry block should be pointer sized"); + assert(GOTBlock.edges_size() == 1 && + "GOT entry should only have one outgoing edge"); + + auto &GOTTarget = GOTBlock.edges().begin()->getTarget(); + JITTargetAddress EdgeAddr = B->getAddress() + E.getOffset(); + JITTargetAddress TargetAddr = GOTTarget.getAddress(); + + // Check that this is a recognized MOV instruction. + // FIXME: Can we assume this? + constexpr uint8_t MOVQRIPRel[] = {0x48, 0x8b}; + if (strncmp(B->getContent().data() + E.getOffset() - 3, + reinterpret_cast<const char *>(MOVQRIPRel), 2) != 0) + continue; + + int64_t Displacement = TargetAddr - EdgeAddr + 4; + if (Displacement >= std::numeric_limits<int32_t>::min() && + Displacement <= std::numeric_limits<int32_t>::max()) { + E.setTarget(GOTTarget); + auto *BlockData = reinterpret_cast<uint8_t *>( + const_cast<char *>(B->getContent().data())); + BlockData[E.getOffset() - 2] = 0x8d; + LLVM_DEBUG({ + dbgs() << " Replaced GOT load wih LEA:\n "; + printEdge(dbgs(), *B, E, + getMachOX86RelocationKindName(E.getKind())); + dbgs() << "\n"; + }); + } + } else if (E.getKind() == Branch32ToStub) { + + // Switch the edge kind to PCRel32: Whether we change the edge target + // or not this will be the desired kind. + E.setKind(Branch32); + + auto &StubBlock = E.getTarget().getBlock(); + assert(StubBlock.getSize() == + sizeof(MachO_x86_64_GOTAndStubsBuilder::StubContent) && + "Stub block should be stub sized"); + assert(StubBlock.edges_size() == 1 && + "Stub block should only have one outgoing edge"); + + auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock(); + assert(GOTBlock.getSize() == G.getPointerSize() && + "GOT block should be pointer sized"); + assert(GOTBlock.edges_size() == 1 && + "GOT block should only have one outgoing edge"); + + auto &GOTTarget = GOTBlock.edges().begin()->getTarget(); + JITTargetAddress EdgeAddr = B->getAddress() + E.getOffset(); + JITTargetAddress TargetAddr = GOTTarget.getAddress(); + + int64_t Displacement = TargetAddr - EdgeAddr + 4; + if (Displacement >= std::numeric_limits<int32_t>::min() && + Displacement <= std::numeric_limits<int32_t>::max()) { + E.setTarget(GOTTarget); + LLVM_DEBUG({ + dbgs() << " Replaced stub branch with direct branch:\n "; + printEdge(dbgs(), *B, E, + getMachOX86RelocationKindName(E.getKind())); + dbgs() << "\n"; + }); + } + } + + return Error::success(); +} + namespace llvm { namespace jitlink { @@ -570,6 +672,9 @@ void jitLink_MachO_x86_64(std::unique_ptr<JITLinkContext> Ctx) { MachO_x86_64_GOTAndStubsBuilder(G).run(); return Error::success(); }); + + // Add GOT/Stubs optimizer pass. + Config.PostAllocationPasses.push_back(optimizeMachO_x86_64_GOTAndStubs); } if (auto Err = Ctx->modifyPassConfig(TT, Config)) @@ -583,6 +688,8 @@ StringRef getMachOX86RelocationKindName(Edge::Kind R) { switch (R) { case Branch32: return "Branch32"; + case Branch32ToStub: + return "Branch32ToStub"; case Pointer32: return "Pointer32"; case Pointer64: |