1ecf6466fSLang Hames //===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===// 2ecf6466fSLang Hames // 3ecf6466fSLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4ecf6466fSLang Hames // See https://llvm.org/LICENSE.txt for license information. 5ecf6466fSLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6ecf6466fSLang Hames // 7ecf6466fSLang Hames //===----------------------------------------------------------------------===// 8ecf6466fSLang Hames // 9ecf6466fSLang Hames // Generic utilities for graphs representing x86-64 objects. 10ecf6466fSLang Hames // 11ecf6466fSLang Hames //===----------------------------------------------------------------------===// 12ecf6466fSLang Hames 13ecf6466fSLang Hames #include "llvm/ExecutionEngine/JITLink/x86_64.h" 14ecf6466fSLang Hames 15ecf6466fSLang Hames #define DEBUG_TYPE "jitlink" 16ecf6466fSLang Hames 17ecf6466fSLang Hames namespace llvm { 18ecf6466fSLang Hames namespace jitlink { 19ecf6466fSLang Hames namespace x86_64 { 20ecf6466fSLang Hames 21ecf6466fSLang Hames const char *getEdgeKindName(Edge::Kind K) { 22ecf6466fSLang Hames switch (K) { 23ecf6466fSLang Hames case Pointer64: 24ecf6466fSLang Hames return "Pointer64"; 25ecf6466fSLang Hames case Pointer32: 26ecf6466fSLang Hames return "Pointer32"; 27dda116bcSluxufan case Pointer32Signed: 28dda116bcSluxufan return "Pointer32Signed"; 295cf0082aSSunho Kim case Pointer16: 305cf0082aSSunho Kim return "Pointer16"; 319ee16eb5SLang Hames case Pointer8: 329ee16eb5SLang Hames return "Pointer8"; 33ecf6466fSLang Hames case Delta64: 34ecf6466fSLang Hames return "Delta64"; 35ecf6466fSLang Hames case Delta32: 36ecf6466fSLang Hames return "Delta32"; 3757bee1e4SXing Guo case Delta16: 3857bee1e4SXing Guo return "Delta16"; 39ae6f730bSMaksim Panchenko case Delta8: 40ae6f730bSMaksim Panchenko return "Delta8"; 41ecf6466fSLang Hames case NegDelta64: 42ecf6466fSLang Hames return "NegDelta64"; 43ecf6466fSLang Hames case NegDelta32: 44ecf6466fSLang Hames return "NegDelta32"; 45210140abSXing Guo case Size64: 46210140abSXing Guo return "Size64"; 47210140abSXing Guo case Size32: 48210140abSXing Guo return "Size32"; 49ee659383Sluxufan case Delta64FromGOT: 50ee659383Sluxufan return "Delta64FromGOT"; 510f00e588SSunho Kim case PCRel32: 520f00e588SSunho Kim return "PCRel32"; 53ecf6466fSLang Hames case BranchPCRel32: 54ecf6466fSLang Hames return "BranchPCRel32"; 55ecf6466fSLang Hames case BranchPCRel32ToPtrJumpStub: 56ecf6466fSLang Hames return "BranchPCRel32ToPtrJumpStub"; 57632135acSLang Hames case BranchPCRel32ToPtrJumpStubBypassable: 58632135acSLang Hames return "BranchPCRel32ToPtrJumpStubBypassable"; 59ecf6466fSLang Hames case RequestGOTAndTransformToDelta32: 60ecf6466fSLang Hames return "RequestGOTAndTransformToDelta32"; 61ee659383Sluxufan case RequestGOTAndTransformToDelta64: 62ee659383Sluxufan return "RequestGOTAndTransformToDelta64"; 63ee659383Sluxufan case RequestGOTAndTransformToDelta64FromGOT: 64ee659383Sluxufan return "RequestGOTAndTransformToDelta64FromGOT"; 6527ea3f16SLang Hames case PCRel32GOTLoadREXRelaxable: 6627ea3f16SLang Hames return "PCRel32GOTLoadREXRelaxable"; 6727ea3f16SLang Hames case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: 6827ea3f16SLang Hames return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable"; 694ec32375Sluxufan case PCRel32GOTLoadRelaxable: 704ec32375Sluxufan return "PCRel32GOTLoadRelaxable"; 714ec32375Sluxufan case RequestGOTAndTransformToPCRel32GOTLoadRelaxable: 724ec32375Sluxufan return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable"; 7327ea3f16SLang Hames case PCRel32TLVPLoadREXRelaxable: 7427ea3f16SLang Hames return "PCRel32TLVPLoadREXRelaxable"; 7527ea3f16SLang Hames case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable: 7627ea3f16SLang Hames return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable"; 77ecf6466fSLang Hames default: 78ecf6466fSLang Hames return getGenericEdgeKindName(static_cast<Edge::Kind>(K)); 79ecf6466fSLang Hames } 80ecf6466fSLang Hames } 81ecf6466fSLang Hames 8274a96b4cSLang Hames const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00, 830269a407SLang Hames 0x00, 0x00, 0x00, 0x00}; 840269a407SLang Hames 850269a407SLang Hames const char PointerJumpStubContent[6] = { 86417b1164SDavid Stuttard static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00}; 870269a407SLang Hames 888daf4f16SLang Hames const char ReentryTrampolineContent[5] = { 898daf4f16SLang Hames static_cast<char>(0xe8), 0x00, 0x00, 0x00, 0x00 908daf4f16SLang Hames }; 918daf4f16SLang Hames 92*8fb29ba2SLang Hames void GOTTableManager::registerExistingEntries() { 93*8fb29ba2SLang Hames for (auto *EntrySym : GOTSection->symbols()) { 94*8fb29ba2SLang Hames assert(EntrySym->getBlock().edges_size() == 1 && 95*8fb29ba2SLang Hames "GOT block edge count != 1"); 96*8fb29ba2SLang Hames registerPreExistingEntry(EntrySym->getBlock().edges().begin()->getTarget(), 97*8fb29ba2SLang Hames *EntrySym); 98*8fb29ba2SLang Hames } 99*8fb29ba2SLang Hames } 100*8fb29ba2SLang Hames 101*8fb29ba2SLang Hames void PLTTableManager::registerExistingEntries() { 102*8fb29ba2SLang Hames for (auto *EntrySym : StubsSection->symbols()) { 103*8fb29ba2SLang Hames assert(EntrySym->getBlock().edges_size() == 1 && 104*8fb29ba2SLang Hames "PLT block edge count != 1"); 105*8fb29ba2SLang Hames auto &GOTSym = EntrySym->getBlock().edges().begin()->getTarget(); 106*8fb29ba2SLang Hames assert(GOTSym.getBlock().edges_size() == 1 && "GOT block edge count != 1"); 107*8fb29ba2SLang Hames registerPreExistingEntry(GOTSym.getBlock().edges().begin()->getTarget(), 108*8fb29ba2SLang Hames *EntrySym); 109*8fb29ba2SLang Hames } 110*8fb29ba2SLang Hames } 111*8fb29ba2SLang Hames 112cc3115cdSLang Hames Error optimizeGOTAndStubAccesses(LinkGraph &G) { 1134ec32375Sluxufan LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n"); 1144ec32375Sluxufan 1154ec32375Sluxufan for (auto *B : G.blocks()) 116a9095f00Sluxufan for (auto &E : B->edges()) { 117a9095f00Sluxufan if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable || 118a9095f00Sluxufan E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) { 119c20cb554SFrederik Gossen #ifndef NDEBUG 120a9095f00Sluxufan bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable; 12121d11c87SAaron Ballman assert(E.getOffset() >= (REXPrefix ? 3u : 2u) && 122a9095f00Sluxufan "GOT edge occurs too early in block"); 123c20cb554SFrederik Gossen #endif 124a9095f00Sluxufan auto *FixupData = reinterpret_cast<uint8_t *>( 125a9095f00Sluxufan const_cast<char *>(B->getContent().data())) + 126a9095f00Sluxufan E.getOffset(); 127a9095f00Sluxufan const uint8_t Op = FixupData[-2]; 128a9095f00Sluxufan const uint8_t ModRM = FixupData[-1]; 1294ec32375Sluxufan 130a9095f00Sluxufan auto &GOTEntryBlock = E.getTarget().getBlock(); 131a9095f00Sluxufan assert(GOTEntryBlock.getSize() == G.getPointerSize() && 132a9095f00Sluxufan "GOT entry block should be pointer sized"); 133a9095f00Sluxufan assert(GOTEntryBlock.edges_size() == 1 && 134a9095f00Sluxufan "GOT entry should only have one outgoing edge"); 135a9095f00Sluxufan auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget(); 136118e953bSLang Hames orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 137118e953bSLang Hames orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E); 138a9095f00Sluxufan int64_t Displacement = TargetAddr - EdgeAddr + 4; 139026fb048SLang Hames bool TargetInRangeForImmU32 = isUInt<32>(TargetAddr.getValue()); 140026fb048SLang Hames bool DisplacementInRangeForImmS32 = isInt<32>(Displacement); 141a9095f00Sluxufan 142a9095f00Sluxufan // If both of the Target and displacement is out of range, then 143a9095f00Sluxufan // there isn't optimization chance. 144a9095f00Sluxufan if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32)) 1454ec32375Sluxufan continue; 1464ec32375Sluxufan 147a9095f00Sluxufan // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". 148a9095f00Sluxufan if (Op == 0x8b && DisplacementInRangeForImmS32) { 149a9095f00Sluxufan FixupData[-2] = 0x8d; 1504ec32375Sluxufan E.setKind(x86_64::Delta32); 1514ec32375Sluxufan E.setTarget(GOTTarget); 1524ec32375Sluxufan E.setAddend(E.getAddend() - 4); 1534ec32375Sluxufan LLVM_DEBUG({ 1544ec32375Sluxufan dbgs() << " Replaced GOT load wih LEA:\n "; 1554ec32375Sluxufan printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 1564ec32375Sluxufan dbgs() << "\n"; 1574ec32375Sluxufan }); 158a9095f00Sluxufan continue; 159a9095f00Sluxufan } 160a9095f00Sluxufan 161a9095f00Sluxufan // Transform call/jmp instructions 162a9095f00Sluxufan if (Op == 0xff && TargetInRangeForImmU32) { 163a9095f00Sluxufan if (ModRM == 0x15) { 164a9095f00Sluxufan // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call 165a9095f00Sluxufan // foo" But lld convert it to "addr32 call foo, because that makes 166a9095f00Sluxufan // result expression to be a single instruction. 167a9095f00Sluxufan FixupData[-2] = 0x67; 168a9095f00Sluxufan FixupData[-1] = 0xe8; 169a9095f00Sluxufan LLVM_DEBUG({ 170a9095f00Sluxufan dbgs() << " replaced call instruction's memory operand wih imm " 171a9095f00Sluxufan "operand:\n "; 172a9095f00Sluxufan printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 173a9095f00Sluxufan dbgs() << "\n"; 174a9095f00Sluxufan }); 175a9095f00Sluxufan } else { 176a9095f00Sluxufan // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop" 177a9095f00Sluxufan assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions"); 178a9095f00Sluxufan FixupData[-2] = 0xe9; 179a9095f00Sluxufan FixupData[3] = 0x90; 180a9095f00Sluxufan E.setOffset(E.getOffset() - 1); 181a9095f00Sluxufan LLVM_DEBUG({ 182a9095f00Sluxufan dbgs() << " replaced jmp instruction's memory operand wih imm " 183a9095f00Sluxufan "operand:\n "; 184a9095f00Sluxufan printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 185a9095f00Sluxufan dbgs() << "\n"; 186a9095f00Sluxufan }); 187a9095f00Sluxufan } 188a9095f00Sluxufan E.setKind(x86_64::Pointer32); 189a9095f00Sluxufan E.setTarget(GOTTarget); 190a9095f00Sluxufan continue; 1914ec32375Sluxufan } 1924ec32375Sluxufan } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) { 1934ec32375Sluxufan auto &StubBlock = E.getTarget().getBlock(); 1944ec32375Sluxufan assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) && 1954ec32375Sluxufan "Stub block should be stub sized"); 1964ec32375Sluxufan assert(StubBlock.edges_size() == 1 && 1974ec32375Sluxufan "Stub block should only have one outgoing edge"); 1984ec32375Sluxufan 1994ec32375Sluxufan auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock(); 2004ec32375Sluxufan assert(GOTBlock.getSize() == G.getPointerSize() && 2014ec32375Sluxufan "GOT block should be pointer sized"); 2024ec32375Sluxufan assert(GOTBlock.edges_size() == 1 && 2034ec32375Sluxufan "GOT block should only have one outgoing edge"); 2044ec32375Sluxufan 2054ec32375Sluxufan auto &GOTTarget = GOTBlock.edges().begin()->getTarget(); 206118e953bSLang Hames orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset(); 207118e953bSLang Hames orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 2084ec32375Sluxufan 2094ec32375Sluxufan int64_t Displacement = TargetAddr - EdgeAddr + 4; 210026fb048SLang Hames if (isInt<32>(Displacement)) { 2114ec32375Sluxufan E.setKind(x86_64::BranchPCRel32); 2124ec32375Sluxufan E.setTarget(GOTTarget); 2134ec32375Sluxufan LLVM_DEBUG({ 2144ec32375Sluxufan dbgs() << " Replaced stub branch with direct branch:\n "; 2154ec32375Sluxufan printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 2164ec32375Sluxufan dbgs() << "\n"; 2174ec32375Sluxufan }); 2184ec32375Sluxufan } 2194ec32375Sluxufan } 220a9095f00Sluxufan } 2214ec32375Sluxufan 2224ec32375Sluxufan return Error::success(); 2234ec32375Sluxufan } 2244ec32375Sluxufan 225ecf6466fSLang Hames } // end namespace x86_64 226ecf6466fSLang Hames } // end namespace jitlink 227ecf6466fSLang Hames } // end namespace llvm 228