xref: /llvm-project/llvm/lib/ExecutionEngine/JITLink/x86_64.cpp (revision 8fb29ba287d72392bd7900c33d2a8d2149126dbe)
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