1 //===----- x86_64.cpp - Generic JITLink x86-64 edge kinds, utilities ------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Generic utilities for graphs representing x86-64 objects. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/ExecutionEngine/JITLink/x86_64.h" 14 15 #define DEBUG_TYPE "jitlink" 16 17 namespace llvm { 18 namespace jitlink { 19 namespace x86_64 { 20 21 const char *getEdgeKindName(Edge::Kind K) { 22 switch (K) { 23 case Pointer64: 24 return "Pointer64"; 25 case Pointer32: 26 return "Pointer32"; 27 case Pointer32Signed: 28 return "Pointer32Signed"; 29 case Pointer16: 30 return "Pointer16"; 31 case Pointer8: 32 return "Pointer8"; 33 case Delta64: 34 return "Delta64"; 35 case Delta32: 36 return "Delta32"; 37 case Delta16: 38 return "Delta16"; 39 case Delta8: 40 return "Delta8"; 41 case NegDelta64: 42 return "NegDelta64"; 43 case NegDelta32: 44 return "NegDelta32"; 45 case Size64: 46 return "Size64"; 47 case Size32: 48 return "Size32"; 49 case Delta64FromGOT: 50 return "Delta64FromGOT"; 51 case PCRel32: 52 return "PCRel32"; 53 case BranchPCRel32: 54 return "BranchPCRel32"; 55 case BranchPCRel32ToPtrJumpStub: 56 return "BranchPCRel32ToPtrJumpStub"; 57 case BranchPCRel32ToPtrJumpStubBypassable: 58 return "BranchPCRel32ToPtrJumpStubBypassable"; 59 case RequestGOTAndTransformToDelta32: 60 return "RequestGOTAndTransformToDelta32"; 61 case RequestGOTAndTransformToDelta64: 62 return "RequestGOTAndTransformToDelta64"; 63 case RequestGOTAndTransformToDelta64FromGOT: 64 return "RequestGOTAndTransformToDelta64FromGOT"; 65 case PCRel32GOTLoadREXRelaxable: 66 return "PCRel32GOTLoadREXRelaxable"; 67 case RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: 68 return "RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable"; 69 case PCRel32GOTLoadRelaxable: 70 return "PCRel32GOTLoadRelaxable"; 71 case RequestGOTAndTransformToPCRel32GOTLoadRelaxable: 72 return "RequestGOTAndTransformToPCRel32GOTLoadRelaxable"; 73 case PCRel32TLVPLoadREXRelaxable: 74 return "PCRel32TLVPLoadREXRelaxable"; 75 case RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable: 76 return "RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable"; 77 default: 78 return getGenericEdgeKindName(static_cast<Edge::Kind>(K)); 79 } 80 } 81 82 const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00, 83 0x00, 0x00, 0x00, 0x00}; 84 85 const char PointerJumpStubContent[6] = { 86 static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00}; 87 88 const char ReentryTrampolineContent[5] = { 89 static_cast<char>(0xe8), 0x00, 0x00, 0x00, 0x00 90 }; 91 92 void GOTTableManager::registerExistingEntries() { 93 for (auto *EntrySym : GOTSection->symbols()) { 94 assert(EntrySym->getBlock().edges_size() == 1 && 95 "GOT block edge count != 1"); 96 registerPreExistingEntry(EntrySym->getBlock().edges().begin()->getTarget(), 97 *EntrySym); 98 } 99 } 100 101 void PLTTableManager::registerExistingEntries() { 102 for (auto *EntrySym : StubsSection->symbols()) { 103 assert(EntrySym->getBlock().edges_size() == 1 && 104 "PLT block edge count != 1"); 105 auto &GOTSym = EntrySym->getBlock().edges().begin()->getTarget(); 106 assert(GOTSym.getBlock().edges_size() == 1 && "GOT block edge count != 1"); 107 registerPreExistingEntry(GOTSym.getBlock().edges().begin()->getTarget(), 108 *EntrySym); 109 } 110 } 111 112 Error optimizeGOTAndStubAccesses(LinkGraph &G) { 113 LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n"); 114 115 for (auto *B : G.blocks()) 116 for (auto &E : B->edges()) { 117 if (E.getKind() == x86_64::PCRel32GOTLoadRelaxable || 118 E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable) { 119 #ifndef NDEBUG 120 bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable; 121 assert(E.getOffset() >= (REXPrefix ? 3u : 2u) && 122 "GOT edge occurs too early in block"); 123 #endif 124 auto *FixupData = reinterpret_cast<uint8_t *>( 125 const_cast<char *>(B->getContent().data())) + 126 E.getOffset(); 127 const uint8_t Op = FixupData[-2]; 128 const uint8_t ModRM = FixupData[-1]; 129 130 auto &GOTEntryBlock = E.getTarget().getBlock(); 131 assert(GOTEntryBlock.getSize() == G.getPointerSize() && 132 "GOT entry block should be pointer sized"); 133 assert(GOTEntryBlock.edges_size() == 1 && 134 "GOT entry should only have one outgoing edge"); 135 auto &GOTTarget = GOTEntryBlock.edges().begin()->getTarget(); 136 orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 137 orc::ExecutorAddr EdgeAddr = B->getFixupAddress(E); 138 int64_t Displacement = TargetAddr - EdgeAddr + 4; 139 bool TargetInRangeForImmU32 = isUInt<32>(TargetAddr.getValue()); 140 bool DisplacementInRangeForImmS32 = isInt<32>(Displacement); 141 142 // If both of the Target and displacement is out of range, then 143 // there isn't optimization chance. 144 if (!(TargetInRangeForImmU32 || DisplacementInRangeForImmS32)) 145 continue; 146 147 // Transform "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg". 148 if (Op == 0x8b && DisplacementInRangeForImmS32) { 149 FixupData[-2] = 0x8d; 150 E.setKind(x86_64::Delta32); 151 E.setTarget(GOTTarget); 152 E.setAddend(E.getAddend() - 4); 153 LLVM_DEBUG({ 154 dbgs() << " Replaced GOT load wih LEA:\n "; 155 printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 156 dbgs() << "\n"; 157 }); 158 continue; 159 } 160 161 // Transform call/jmp instructions 162 if (Op == 0xff && TargetInRangeForImmU32) { 163 if (ModRM == 0x15) { 164 // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call 165 // foo" But lld convert it to "addr32 call foo, because that makes 166 // result expression to be a single instruction. 167 FixupData[-2] = 0x67; 168 FixupData[-1] = 0xe8; 169 LLVM_DEBUG({ 170 dbgs() << " replaced call instruction's memory operand wih imm " 171 "operand:\n "; 172 printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 173 dbgs() << "\n"; 174 }); 175 } else { 176 // Transform "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop" 177 assert(ModRM == 0x25 && "Invalid ModRm for call/jmp instructions"); 178 FixupData[-2] = 0xe9; 179 FixupData[3] = 0x90; 180 E.setOffset(E.getOffset() - 1); 181 LLVM_DEBUG({ 182 dbgs() << " replaced jmp instruction's memory operand wih imm " 183 "operand:\n "; 184 printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 185 dbgs() << "\n"; 186 }); 187 } 188 E.setKind(x86_64::Pointer32); 189 E.setTarget(GOTTarget); 190 continue; 191 } 192 } else if (E.getKind() == x86_64::BranchPCRel32ToPtrJumpStubBypassable) { 193 auto &StubBlock = E.getTarget().getBlock(); 194 assert(StubBlock.getSize() == sizeof(PointerJumpStubContent) && 195 "Stub block should be stub sized"); 196 assert(StubBlock.edges_size() == 1 && 197 "Stub block should only have one outgoing edge"); 198 199 auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock(); 200 assert(GOTBlock.getSize() == G.getPointerSize() && 201 "GOT block should be pointer sized"); 202 assert(GOTBlock.edges_size() == 1 && 203 "GOT block should only have one outgoing edge"); 204 205 auto &GOTTarget = GOTBlock.edges().begin()->getTarget(); 206 orc::ExecutorAddr EdgeAddr = B->getAddress() + E.getOffset(); 207 orc::ExecutorAddr TargetAddr = GOTTarget.getAddress(); 208 209 int64_t Displacement = TargetAddr - EdgeAddr + 4; 210 if (isInt<32>(Displacement)) { 211 E.setKind(x86_64::BranchPCRel32); 212 E.setTarget(GOTTarget); 213 LLVM_DEBUG({ 214 dbgs() << " Replaced stub branch with direct branch:\n "; 215 printEdge(dbgs(), *B, E, getEdgeKindName(E.getKind())); 216 dbgs() << "\n"; 217 }); 218 } 219 } 220 } 221 222 return Error::success(); 223 } 224 225 } // end namespace x86_64 226 } // end namespace jitlink 227 } // end namespace llvm 228