1 //===--- ppc64.h - Generic JITLink ppc64 edge kinds, utilities --*- C++ -*-===//
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 64-bit PowerPC objects.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
14 #define LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
15 
16 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
17 #include "llvm/ExecutionEngine/JITLink/TableManager.h"
18 #include "llvm/Support/Endian.h"
19 
20 namespace llvm::jitlink::ppc64 {
21 
22 /// Represents ppc64 fixups and other ppc64-specific edge kinds.
23 enum EdgeKind_ppc64 : Edge::Kind {
24   Pointer64 = Edge::FirstRelocation,
25   Pointer32,
26   Delta64,
27   Delta32,
28   NegDelta32,
29   Delta16,
30   Delta16HA,
31   Delta16LO,
32   TOCDelta16HA,
33   TOCDelta16LO,
34   TOCDelta16DS,
35   TOCDelta16LODS,
36   CallBranchDelta,
37   // Need to restore r2 after the bl, suggesting the bl is followed by a nop.
38   CallBranchDeltaRestoreTOC,
39   // Need PLT call stub using TOC, TOC pointer is not saved before branching.
40   RequestPLTCallStub,
41   // Need PLT call stub using TOC, TOC pointer is saved before branching.
42   RequestPLTCallStubSaveTOC,
43   // Need PLT call stub without using TOC.
44   RequestPLTCallStubNoTOC,
45 };
46 
47 enum PLTCallStubKind {
48   LongBranch,
49   LongBranchSaveR2,
50   LongBranchNoTOC,
51 };
52 
53 extern const char NullPointerContent[8];
54 extern const char PointerJumpStubContent_big[20];
55 extern const char PointerJumpStubContent_little[20];
56 extern const char PointerJumpStubNoTOCContent_big[32];
57 extern const char PointerJumpStubNoTOCContent_little[32];
58 
59 struct PLTCallStubReloc {
60   Edge::Kind K;
61   size_t Offset;
62   Edge::AddendT A;
63 };
64 
65 struct PLTCallStubInfo {
66   ArrayRef<char> Content;
67   SmallVector<PLTCallStubReloc, 2> Relocs;
68 };
69 
70 template <support::endianness Endianness>
71 inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) {
72   constexpr bool isLE = Endianness == support::endianness::little;
73   switch (StubKind) {
74   case LongBranch: {
75     ArrayRef<char> Content =
76         isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
77     // Skip save r2.
78     Content = Content.slice(4);
79     return PLTCallStubInfo{
80         Content,
81         {{TOCDelta16HA, 0, 0}, {TOCDelta16LO, 4, 0}},
82     };
83   }
84   case LongBranchSaveR2: {
85     ArrayRef<char> Content =
86         isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
87     return PLTCallStubInfo{
88         Content,
89         {{TOCDelta16HA, 4, 0}, {TOCDelta16LO, 8, 0}},
90     };
91   }
92   case LongBranchNoTOC: {
93     ArrayRef<char> Content = isLE ? PointerJumpStubNoTOCContent_little
94                                   : PointerJumpStubNoTOCContent_big;
95     return PLTCallStubInfo{
96         Content,
97         {{Delta16HA, 16, 8}, {Delta16LO, 20, 12}},
98     };
99   }
100   }
101   llvm_unreachable("Unknown PLTCallStubKind enum");
102 }
103 
104 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
105                                       Symbol *InitialTarget = nullptr,
106                                       uint64_t InitialAddend = 0) {
107   assert(G.getPointerSize() == sizeof(NullPointerContent) &&
108          "LinkGraph's pointer size should be consistent with size of "
109          "NullPointerContent");
110   Block &B = G.createContentBlock(PointerSection, NullPointerContent,
111                                   orc::ExecutorAddr(), G.getPointerSize(), 0);
112   if (InitialTarget)
113     B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
114   return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
115 }
116 
117 template <support::endianness Endianness>
118 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
119                                               Section &StubSection,
120                                               Symbol &PointerSymbol,
121                                               PLTCallStubKind StubKind) {
122   PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind);
123   Block &B = G.createContentBlock(StubSection, StubInfo.Content,
124                                   orc::ExecutorAddr(), 4, 0);
125   for (auto const &Reloc : StubInfo.Relocs)
126     B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A);
127   return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false);
128 }
129 
130 template <support::endianness Endianness>
131 class TOCTableManager : public TableManager<TOCTableManager<Endianness>> {
132 public:
133   // FIXME: `llvm-jitlink -check` relies this name to be $__GOT.
134   static StringRef getSectionName() { return "$__GOT"; }
135 
136   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
137     Edge::Kind K = E.getKind();
138     switch (K) {
139     case TOCDelta16HA:
140     case TOCDelta16LO:
141     case TOCDelta16DS:
142     case TOCDelta16LODS:
143     case CallBranchDeltaRestoreTOC:
144     case RequestPLTCallStub:
145     case RequestPLTCallStubSaveTOC:
146       // Create TOC section if TOC relocation, PLT or GOT is used.
147       getOrCreateTOCSection(G);
148       return false;
149     default:
150       return false;
151     }
152   }
153 
154   Symbol &createEntry(LinkGraph &G, Symbol &Target) {
155     return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target);
156   }
157 
158 private:
159   Section &getOrCreateTOCSection(LinkGraph &G) {
160     TOCSection = G.findSectionByName(getSectionName());
161     if (!TOCSection)
162       TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read);
163     return *TOCSection;
164   }
165 
166   Section *TOCSection = nullptr;
167 };
168 
169 template <support::endianness Endianness>
170 class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
171 public:
172   PLTTableManager(TOCTableManager<Endianness> &TOC) : TOC(TOC) {}
173 
174   static StringRef getSectionName() { return "$__STUBS"; }
175 
176   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
177     Edge::Kind K = E.getKind();
178     if (K == ppc64::RequestPLTCallStubSaveTOC && E.getTarget().isExternal()) {
179       E.setKind(ppc64::CallBranchDeltaRestoreTOC);
180       this->StubKind = LongBranchSaveR2;
181       E.setTarget(this->getEntryForTarget(G, E.getTarget()));
182       return true;
183     }
184     if (K == ppc64::RequestPLTCallStubNoTOC && E.getTarget().isExternal()) {
185       E.setKind(ppc64::CallBranchDelta);
186       this->StubKind = LongBranchNoTOC;
187       E.setTarget(this->getEntryForTarget(G, E.getTarget()));
188       return true;
189     }
190     return false;
191   }
192 
193   Symbol &createEntry(LinkGraph &G, Symbol &Target) {
194     return createAnonymousPointerJumpStub<Endianness>(
195         G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
196         this->StubKind);
197   }
198 
199 private:
200   Section &getOrCreateStubsSection(LinkGraph &G) {
201     PLTSection = G.findSectionByName(getSectionName());
202     if (!PLTSection)
203       PLTSection = &G.createSection(getSectionName(),
204                                     orc::MemProt::Read | orc::MemProt::Exec);
205     return *PLTSection;
206   }
207 
208   TOCTableManager<Endianness> &TOC;
209   Section *PLTSection = nullptr;
210   PLTCallStubKind StubKind;
211 };
212 
213 /// Returns a string name for the given ppc64 edge. For debugging purposes
214 /// only.
215 const char *getEdgeKindName(Edge::Kind K);
216 
217 inline static uint16_t ha16(uint64_t x) { return (x + 0x8000) >> 16; }
218 
219 inline static uint16_t lo16(uint64_t x) { return x & 0xffff; }
220 
221 /// Apply fixup expression for edge to block content.
222 template <support::endianness Endianness>
223 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
224                         const Symbol *TOCSymbol) {
225   char *BlockWorkingMem = B.getAlreadyMutableContent().data();
226   char *FixupPtr = BlockWorkingMem + E.getOffset();
227   orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
228   int64_t S = E.getTarget().getAddress().getValue();
229   int64_t A = E.getAddend();
230   int64_t P = FixupAddress.getValue();
231   int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0;
232   Edge::Kind K = E.getKind();
233 
234   DEBUG_WITH_TYPE("jitlink", {
235     dbgs() << "    Applying fixup on " << G.getEdgeKindName(K)
236            << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", "
237            << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", "
238            << formatv("{0:x}", TOCBase) << ")\n";
239   });
240 
241   switch (K) {
242   case Pointer64: {
243     uint64_t Value = S + A;
244     support::endian::write64<Endianness>(FixupPtr, Value);
245     break;
246   }
247   case Delta16HA:
248   case Delta16LO: {
249     int64_t Value = S + A - P;
250     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
251       return makeTargetOutOfRangeError(G, B, E);
252     }
253     if (K == Delta16LO)
254       support::endian::write16<Endianness>(FixupPtr, lo16(Value));
255     else
256       support::endian::write16<Endianness>(FixupPtr, ha16(Value));
257     break;
258   }
259   case TOCDelta16HA:
260   case TOCDelta16LO: {
261     int64_t Value = S + A - TOCBase;
262     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
263       return makeTargetOutOfRangeError(G, B, E);
264     }
265     if (K == TOCDelta16LO)
266       support::endian::write16<Endianness>(FixupPtr, lo16(Value));
267     else
268       support::endian::write16<Endianness>(FixupPtr, ha16(Value));
269     break;
270   }
271   case TOCDelta16DS:
272   case TOCDelta16LODS: {
273     int64_t Value = S + A - TOCBase;
274     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
275       return makeTargetOutOfRangeError(G, B, E);
276     }
277     if (K == TOCDelta16LODS)
278       support::endian::write16<Endianness>(FixupPtr, lo16(Value) & ~3);
279     else
280       support::endian::write16<Endianness>(FixupPtr, Value & ~3);
281     break;
282   }
283   case CallBranchDeltaRestoreTOC:
284   case CallBranchDelta: {
285     int64_t Value = S + A - P;
286     if (LLVM_UNLIKELY(!isInt<26>(Value))) {
287       return makeTargetOutOfRangeError(G, B, E);
288     }
289     uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
290     support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) |
291                                                        (Value & 0x03fffffc));
292     if (K == CallBranchDeltaRestoreTOC) {
293       uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4);
294       assert(NopInst == 0x60000000 &&
295              "NOP should be placed here for restoring r2");
296       (void)NopInst;
297       // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`.
298       support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018);
299     }
300     break;
301   }
302   case Delta64: {
303     int64_t Value = S + A - P;
304     support::endian::write64<Endianness>(FixupPtr, Value);
305     break;
306   }
307   case Delta32: {
308     int64_t Value = S + A - P;
309     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
310       return makeTargetOutOfRangeError(G, B, E);
311     }
312     support::endian::write32<Endianness>(FixupPtr, Value);
313     break;
314   }
315   case NegDelta32: {
316     int64_t Value = P - S + A;
317     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
318       return makeTargetOutOfRangeError(G, B, E);
319     }
320     support::endian::write32<Endianness>(FixupPtr, Value);
321     break;
322   }
323   default:
324     return make_error<JITLinkError>(
325         "In graph " + G.getName() + ", section " + B.getSection().getName() +
326         " unsupported edge kind " + getEdgeKindName(E.getKind()));
327   }
328   return Error::success();
329 }
330 
331 } // end namespace llvm::jitlink::ppc64
332 
333 #endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
334