From cc3115cd1d35b7325d4f1d53f860048e32e82e43 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Mon, 18 Oct 2021 21:41:08 -0700 Subject: [PATCH] [JITLink][x86-64] Lift GOT, PLT table managers into x86_64.h; reuse for MachO. This lifts the global offset table and procedure linkage table builders out of ELF_x86_64.h and into x86_64.h, renaming them with generic names x86_64::GOTTableBuilder and x86_64::PLTTableBuilder. MachO_x86_64.cpp is updated to use these classes instead of the older PerGraphGOTAndStubsBuilder tool. --- .../llvm/ExecutionEngine/JITLink/TableManager.h | 4 +- llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h | 117 +++++++++++++++-- llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp | 139 ++------------------- llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp | 84 ++----------- llvm/lib/ExecutionEngine/JITLink/x86_64.cpp | 2 +- 5 files changed, 128 insertions(+), 218 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h b/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h index 2b50239..c20f62d 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h @@ -38,7 +38,7 @@ public: if (EntryI == Entries.end()) { auto &Entry = impl().createEntry(G, Target); DEBUG_WITH_TYPE("jitlink", { - dbgs() << " Created" << impl().getTableName() << "entry for " + dbgs() << " Created" << impl().getSectionName() << "entry for " << Target.getName() << ": " << Entry << "\n"; }); EntryI = Entries.insert(std::make_pair(Target.getName(), &Entry)).first; @@ -46,7 +46,7 @@ public: assert(EntryI != Entries.end() && "Could not get entry symbol"); DEBUG_WITH_TYPE("jitlink", { - dbgs() << " Using " << impl().getTableName() << " entry " + dbgs() << " Using " << impl().getSectionName() << " entry " << *EntryI->second << "\n"; }); return *EntryI->second; diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h b/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h index 1f6a861e..3130ea3 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h @@ -14,6 +14,7 @@ #define LLVM_EXECUTIONENGINE_JITLINK_X86_64_H #include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/JITLink/TableManager.h" #include @@ -349,14 +350,6 @@ enum EdgeKind_x86_64 : Edge::Kind { /// only. const char *getEdgeKindName(Edge::Kind K); -/// Optimize the GOT and Stub relocations if the edge target address is in range -/// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range, -/// then replace GOT load with lea -/// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is -/// in range, replace a indirect jump by plt stub with a direct jump to the -/// target -Error optimize_x86_64_GOTAndStubs(LinkGraph &G); - /// Returns true if the given uint64_t value is in range for a uint32_t. inline bool isInRangeForImmU32(uint64_t Value) { return Value <= std::numeric_limits::max(); @@ -523,6 +516,114 @@ inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, false); } +/// Global Offset Table Builder. +class GOTTableManager : public TableManager { +public: + static StringRef getSectionName() { return "$__GOT"; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + Edge::Kind KindToSet = Edge::Invalid; + switch (E.getKind()) { + case x86_64::Delta64FromGOT: { + // we need to make sure that the GOT section exists, but don't otherwise + // need to fix up this edge + getGOTSection(G); + return false; + } + case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: + KindToSet = x86_64::PCRel32GOTLoadREXRelaxable; + break; + case x86_64::RequestGOTAndTransformToPCRel32GOTLoadRelaxable: + KindToSet = x86_64::PCRel32GOTLoadRelaxable; + break; + case x86_64::RequestGOTAndTransformToDelta64: + KindToSet = x86_64::Delta64; + break; + case x86_64::RequestGOTAndTransformToDelta64FromGOT: + KindToSet = x86_64::Delta64FromGOT; + break; + case x86_64::RequestGOTAndTransformToDelta32: + KindToSet = x86_64::Delta32; + break; + default: + return false; + } + assert(KindToSet != Edge::Invalid && + "Fell through switch, but no new kind to set"); + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " + << formatv("{0:x}", B->getFixupAddress(E)) << " (" + << formatv("{0:x}", B->getAddress()) << " + " + << formatv("{0:x}", E.getOffset()) << ")\n"; + }); + E.setKind(KindToSet); + E.setTarget(getEntryForTarget(G, E.getTarget())); + return true; + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + return createAnonymousPointer(G, getGOTSection(G), &Target); + } + +private: + Section &getGOTSection(LinkGraph &G) { + if (!GOTSection) + GOTSection = &G.createSection(getSectionName(), MemProt::Read); + return *GOTSection; + } + + Section *GOTSection = nullptr; +}; + +/// Procedure Linkage Table Builder. +class PLTTableManager : public TableManager { +public: + PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {} + + static StringRef getSectionName() { return "$__STUBS"; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + if (E.getKind() == x86_64::BranchPCRel32 && !E.getTarget().isDefined()) { + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " + << formatv("{0:x}", B->getFixupAddress(E)) << " (" + << formatv("{0:x}", B->getAddress()) << " + " + << formatv("{0:x}", E.getOffset()) << ")\n"; + }); + // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to + // be optimized when the target is in-range. + E.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable); + E.setTarget(getEntryForTarget(G, E.getTarget())); + return true; + } + return false; + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + return createAnonymousPointerJumpStub(G, getStubsSection(G), + GOT.getEntryForTarget(G, Target)); + } + +public: + Section &getStubsSection(LinkGraph &G) { + if (!PLTSection) + PLTSection = + &G.createSection(getSectionName(), MemProt::Read | MemProt::Exec); + return *PLTSection; + } + + GOTTableManager &GOT; + Section *PLTSection = nullptr; +}; + +/// Optimize the GOT and Stub relocations if the edge target address is in range +/// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range, +/// then replace GOT load with lea +/// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is +/// in range, replace a indirect jump by plt stub with a direct jump to the +/// target +Error optimizeGOTAndStubAccesses(LinkGraph &G); + } // namespace x86_64 } // end namespace jitlink } // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp index 7153a3f..0cd1117 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp @@ -21,7 +21,6 @@ #include "EHFrameSupportImpl.h" #include "ELFLinkGraphBuilder.h" #include "JITLinkGeneric.h" -#include "PerGraphGOTAndPLTStubsBuilder.h" #define DEBUG_TYPE "jitlink" @@ -31,139 +30,15 @@ using namespace llvm::jitlink::ELF_x86_64_Edges; namespace { -constexpr StringRef ELFGOTSectionName = "$__GOT"; constexpr StringRef ELFGOTSymbolName = "_GLOBAL_OFFSET_TABLE_"; constexpr StringRef ELFTLSInfoSectionName = "$__TLSINFO"; -class GOTTableManager_ELF_x86_64 - : public TableManager { -public: - static const uint8_t NullGOTEntryContent[8]; - - // Nice name for table - StringRef getTableName() { return "GOT"; } - - bool visitEdge(LinkGraph &G, Block *B, Edge &E) { - Edge::Kind KindToSet = Edge::Invalid; - switch (E.getKind()) { - case x86_64::Delta64FromGOT: { - // we need to make sure that the GOT section exists, but don't otherwise - // need to fix up this edge - getGOTSection(G); - return false; - } - case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: - KindToSet = x86_64::PCRel32GOTLoadREXRelaxable; - break; - case x86_64::RequestGOTAndTransformToPCRel32GOTLoadRelaxable: - KindToSet = x86_64::PCRel32GOTLoadRelaxable; - break; - case x86_64::RequestGOTAndTransformToDelta64: - KindToSet = x86_64::Delta64; - break; - case x86_64::RequestGOTAndTransformToDelta64FromGOT: - KindToSet = x86_64::Delta64FromGOT; - break; - case x86_64::RequestGOTAndTransformToDelta32: - KindToSet = x86_64::Delta32; - break; - default: - return false; - } - assert(KindToSet != Edge::Invalid && - "Fell through switch, but no new kind to set"); - LLVM_DEBUG({ - dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " - << formatv("{0:x}", B->getFixupAddress(E)) << " (" - << formatv("{0:x}", B->getAddress()) << " + " - << formatv("{0:x}", E.getOffset()) << ")\n"; - }); - E.setKind(KindToSet); - E.setTarget(getEntryForTarget(G, E.getTarget())); - return true; - } - - Symbol &createEntry(LinkGraph &G, Symbol &Target) { - auto &GOTEntryBlock = G.createContentBlock( - getGOTSection(G), getGOTEntryBlockContent(), 0, 8, 0); - GOTEntryBlock.addEdge(x86_64::Pointer64, 0, Target, 0); - return G.addAnonymousSymbol(GOTEntryBlock, 0, 8, false, false); - } - -private: - Section &getGOTSection(LinkGraph &G) { - if (!GOTSection) - GOTSection = &G.createSection(ELFGOTSectionName, MemProt::Read); - return *GOTSection; - } - ArrayRef getGOTEntryBlockContent() const { - return {reinterpret_cast(NullGOTEntryContent), - sizeof(NullGOTEntryContent)}; - } - Section *GOTSection = nullptr; -}; -const uint8_t GOTTableManager_ELF_x86_64::NullGOTEntryContent[8] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -class PLTTableManager_ELF_x86_64 - : public TableManager { -public: - PLTTableManager_ELF_x86_64(GOTTableManager_ELF_x86_64 &GOTTable) - : GOTTable(GOTTable) {} - - StringRef getTableName() { return "PLT"; } - - static const uint8_t StubContent[6]; - bool visitEdge(LinkGraph &G, Block *B, Edge &E) { - if (E.getKind() == x86_64::BranchPCRel32 && !E.getTarget().isDefined()) { - LLVM_DEBUG({ - dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " - << formatv("{0:x}", B->getFixupAddress(E)) << " (" - << formatv("{0:x}", B->getAddress()) << " + " - << formatv("{0:x}", E.getOffset()) << ")\n"; - }); - // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to - // be optimized when the target is in-range. - E.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable); - E.setTarget(getEntryForTarget(G, E.getTarget())); - return true; - } - return false; - } - - Symbol &createEntry(LinkGraph &G, Symbol &Target) { - auto &StubContentBlock = G.createContentBlock( - getStubsSection(G), getStubBlockContent(), 0, 1, 0); - // Re-use GOT entries for stub targets. - auto &GOTEntrySymbol = GOTTable.getEntryForTarget(G, Target); - StubContentBlock.addEdge(x86_64::Delta32, 2, GOTEntrySymbol, -4); - return G.addAnonymousSymbol(StubContentBlock, 0, 6, true, false); - } - -private: - Section &getStubsSection(LinkGraph &G) { - if (!StubsSection) - StubsSection = - &G.createSection("$__STUBS", MemProt::Read | MemProt::Exec); - return *StubsSection; - } - - ArrayRef getStubBlockContent() { - return {reinterpret_cast(StubContent), sizeof(StubContent)}; - } - - Section *StubsSection = nullptr; - GOTTableManager_ELF_x86_64 &GOTTable; -}; -const uint8_t PLTTableManager_ELF_x86_64::StubContent[6] = {0xFF, 0x25, 0x00, - 0x00, 0x00, 0x00}; - class TLSInfoTableManager_ELF_x86_64 : public TableManager { public: static const uint8_t TLSInfoEntryContent[16]; - StringRef getTableName() { return "TLSInfo"; } + static StringRef getSectionName() { return ELFTLSInfoSectionName; } bool visitEdge(LinkGraph &G, Block *B, Edge &E) { if (E.getKind() == x86_64::RequestTLSDescInGOTAndTransformToDelta32) { @@ -213,8 +88,8 @@ const uint8_t TLSInfoTableManager_ELF_x86_64::TLSInfoEntryContent[16] = { Error buildTables_ELF_x86_64(LinkGraph &G) { LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); - GOTTableManager_ELF_x86_64 GOT; - PLTTableManager_ELF_x86_64 PLT(GOT); + x86_64::GOTTableManager GOT; + x86_64::PLTTableManager PLT(GOT); TLSInfoTableManager_ELF_x86_64 TLSInfo; visitExistingEdges(G, GOT, PLT, TLSInfo); return Error::success(); @@ -412,7 +287,8 @@ private: createDefineExternalSectionStartAndEndSymbolsPass( [&](LinkGraph &LG, Symbol &Sym) -> SectionRangeSymbolDesc { if (Sym.getName() == ELFGOTSymbolName) - if (auto *GOTSection = G.findSectionByName(ELFGOTSectionName)) { + if (auto *GOTSection = G.findSectionByName( + x86_64::GOTTableManager::getSectionName())) { GOTSymbol = &Sym; return {*GOTSection, true}; } @@ -431,7 +307,8 @@ private: // Otherwise look for a GOT section: If it already has a start symbol we'll // record it, otherwise we'll create our own. // If there's a GOT section but we didn't find an external GOT symbol... - if (auto *GOTSection = G.findSectionByName(ELFGOTSectionName)) { + if (auto *GOTSection = + G.findSectionByName(x86_64::GOTTableManager::getSectionName())) { // Check for an existing defined symbol. for (auto *Sym : GOTSection->symbols()) @@ -522,7 +399,7 @@ void link_ELF_x86_64(std::unique_ptr G, identifyELFSectionStartAndEndSymbols)); // Add GOT/Stubs optimizer pass. - Config.PreFixupPasses.push_back(x86_64::optimize_x86_64_GOTAndStubs); + Config.PreFixupPasses.push_back(x86_64::optimizeGOTAndStubAccesses); } if (auto Err = Ctx->modifyPassConfig(*G, Config)) diff --git a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp index 40ded07..18f92c8 100644 --- a/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/MachO_x86_64.cpp @@ -420,79 +420,12 @@ private: } }; -class PerGraphGOTAndPLTStubsBuilder_MachO_x86_64 - : public PerGraphGOTAndPLTStubsBuilder< - PerGraphGOTAndPLTStubsBuilder_MachO_x86_64> { -public: - - using PerGraphGOTAndPLTStubsBuilder< - PerGraphGOTAndPLTStubsBuilder_MachO_x86_64>:: - PerGraphGOTAndPLTStubsBuilder; - - bool isGOTEdgeToFix(Edge &E) const { - return E.getKind() == x86_64::RequestGOTAndTransformToDelta32 || - E.getKind() == - x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable; - } - - Symbol &createGOTEntry(Symbol &Target) { - return x86_64::createAnonymousPointer(G, getGOTSection(), &Target); - } - - void fixGOTEdge(Edge &E, Symbol &GOTEntry) { - // Fix the edge kind. - switch (E.getKind()) { - case x86_64::RequestGOTAndTransformToDelta32: - E.setKind(x86_64::Delta32); - break; - case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: - E.setKind(x86_64::PCRel32GOTLoadREXRelaxable); - break; - default: - llvm_unreachable("Not a GOT transform edge"); - } - // Fix the target, leave the addend as-is. - E.setTarget(GOTEntry); - } - - bool isExternalBranchEdge(Edge &E) { - return E.getKind() == x86_64::BranchPCRel32 && E.getTarget().isExternal(); - } - - Symbol &createPLTStub(Symbol &Target) { - return x86_64::createAnonymousPointerJumpStub(G, getStubsSection(), - getGOTEntry(Target)); - } - - void fixPLTEdge(Edge &E, Symbol &Stub) { - assert(E.getKind() == x86_64::BranchPCRel32 && "Not a Branch32 edge?"); - assert(E.getAddend() == 0 && - "BranchPCRel32 edge has unexpected addend value"); - - // Set the edge kind to BranchPCRel32ToPtrJumpStubBypassable. We will use - // this to check for stub optimization opportunities in the - // optimizeMachO_x86_64_GOTAndStubs pass below. - E.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable); - E.setTarget(Stub); - } - -private: - Section &getGOTSection() { - if (!GOTSection) - GOTSection = &G.createSection("$__GOT", MemProt::Read); - return *GOTSection; - } - - Section &getStubsSection() { - if (!StubsSection) - StubsSection = - &G.createSection("$__STUBS", MemProt::Read | MemProt::Exec); - return *StubsSection; - } - - Section *GOTSection = nullptr; - Section *StubsSection = nullptr; -}; +Error buildGOTAndStubs_MachO_x86_64(LinkGraph &G) { + x86_64::GOTTableManager GOT; + x86_64::PLTTableManager PLT(GOT); + visitExistingEdges(G, GOT, PLT); + return Error::success(); +} } // namespace @@ -543,11 +476,10 @@ void link_MachO_x86_64(std::unique_ptr G, Config.PrePrunePasses.push_back(markAllSymbolsLive); // Add an in-place GOT/Stubs pass. - Config.PostPrunePasses.push_back( - PerGraphGOTAndPLTStubsBuilder_MachO_x86_64::asPass); + Config.PostPrunePasses.push_back(buildGOTAndStubs_MachO_x86_64); // Add GOT/Stubs optimizer pass. - Config.PreFixupPasses.push_back(x86_64::optimize_x86_64_GOTAndStubs); + Config.PreFixupPasses.push_back(x86_64::optimizeGOTAndStubAccesses); } if (auto Err = Ctx->modifyPassConfig(*G, Config)) diff --git a/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp index 8d95b33..4852128 100644 --- a/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp @@ -71,7 +71,7 @@ const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00, const char PointerJumpStubContent[6] = { static_cast(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00}; -Error optimize_x86_64_GOTAndStubs(LinkGraph &G) { +Error optimizeGOTAndStubAccesses(LinkGraph &G) { LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n"); for (auto *B : G.blocks()) -- 2.7.4