228 lines · cpp
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-exception6//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 17namespace llvm {18namespace jitlink {19namespace x86_64 {20 21const 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(K);79 }80}81 82const char NullPointerContent[PointerSize] = {0x00, 0x00, 0x00, 0x00,83 0x00, 0x00, 0x00, 0x00};84 85const char PointerJumpStubContent[6] = {86 static_cast<char>(0xFFu), 0x25, 0x00, 0x00, 0x00, 0x00};87 88const char ReentryTrampolineContent[5] = {89 static_cast<char>(0xe8), 0x00, 0x00, 0x00, 0x0090};91 92void 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 101void 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 112Error 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 NDEBUG120 bool REXPrefix = E.getKind() == x86_64::PCRel32GOTLoadREXRelaxable;121 assert(E.getOffset() >= (REXPrefix ? 3u : 2u) &&122 "GOT edge occurs too early in block");123#endif124 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, then143 // 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 instructions162 if (Op == 0xff && TargetInRangeForImmU32) {163 if (ModRM == 0x15) {164 // ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call165 // foo" But lld convert it to "addr32 call foo, because that makes166 // 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_64226} // end namespace jitlink227} // end namespace llvm228