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
20namespace llvm::jitlink::ppc64 {
21
22/// Represents ppc64 fixups and other ppc64-specific edge kinds.
23enum EdgeKind_ppc64 : Edge::Kind {
24 Pointer64 = Edge::FirstRelocation,
25 Pointer32,
26 Pointer16,
27 Pointer16DS,
28 Pointer16HA,
29 Pointer16HI,
30 Pointer16HIGH,
31 Pointer16HIGHA,
32 Pointer16HIGHER,
33 Pointer16HIGHERA,
34 Pointer16HIGHEST,
35 Pointer16HIGHESTA,
36 Pointer16LO,
37 Pointer16LODS,
38 Pointer14,
39 Delta64,
40 Delta34,
41 Delta32,
42 NegDelta32,
43 Delta16,
44 Delta16HA,
45 Delta16HI,
46 Delta16LO,
47 TOC,
48 TOCDelta16,
49 TOCDelta16DS,
50 TOCDelta16HA,
51 TOCDelta16HI,
52 TOCDelta16LO,
53 TOCDelta16LODS,
54 RequestGOTAndTransformToDelta34,
55 CallBranchDelta,
56 // Need to restore r2 after the bl, suggesting the bl is followed by a nop.
57 CallBranchDeltaRestoreTOC,
58 // Request calling function with TOC.
59 RequestCall,
60 // Request calling function without TOC.
61 RequestCallNoTOC,
62 RequestTLSDescInGOTAndTransformToTOCDelta16HA,
63 RequestTLSDescInGOTAndTransformToTOCDelta16LO,
64 RequestTLSDescInGOTAndTransformToDelta34,
65};
66
67enum PLTCallStubKind {
68 // Setup function entry(r12) and long branch to target using TOC.
69 LongBranch,
70 // Save TOC pointer, setup function entry and long branch to target using TOC.
71 LongBranchSaveR2,
72 // Setup function entry(r12) and long branch to target without using TOC.
73 LongBranchNoTOC,
74};
75
76extern const char NullPointerContent[8];
77extern const char PointerJumpStubContent_big[20];
78extern const char PointerJumpStubContent_little[20];
79extern const char PointerJumpStubNoTOCContent_big[32];
80extern const char PointerJumpStubNoTOCContent_little[32];
81
82struct PLTCallStubReloc {
83 Edge::Kind K;
84 size_t Offset;
85 Edge::AddendT A;
86};
87
88struct PLTCallStubInfo {
89 ArrayRef<char> Content;
90 SmallVector<PLTCallStubReloc, 2> Relocs;
91};
92
93template <llvm::endianness Endianness>
94inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) {
95 constexpr bool isLE = Endianness == llvm::endianness::little;
96 switch (StubKind) {
97 case LongBranch: {
98 ArrayRef<char> Content =
99 isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
100 // Skip save r2.
101 Content = Content.slice(N: 4);
102 size_t Offset = isLE ? 0 : 2;
103 return PLTCallStubInfo{
104 .Content: Content,
105 .Relocs: {{.K: TOCDelta16HA, .Offset: Offset, .A: 0}, {.K: TOCDelta16LO, .Offset: Offset + 4, .A: 0}},
106 };
107 }
108 case LongBranchSaveR2: {
109 ArrayRef<char> Content =
110 isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
111 size_t Offset = isLE ? 4 : 6;
112 return PLTCallStubInfo{
113 .Content: Content,
114 .Relocs: {{.K: TOCDelta16HA, .Offset: Offset, .A: 0}, {.K: TOCDelta16LO, .Offset: Offset + 4, .A: 0}},
115 };
116 }
117 case LongBranchNoTOC: {
118 ArrayRef<char> Content = isLE ? PointerJumpStubNoTOCContent_little
119 : PointerJumpStubNoTOCContent_big;
120 size_t Offset = isLE ? 16 : 18;
121 Edge::AddendT Addend = isLE ? 8 : 10;
122 return PLTCallStubInfo{
123 .Content: Content,
124 .Relocs: {{.K: Delta16HA, .Offset: Offset, .A: Addend}, {.K: Delta16LO, .Offset: Offset + 4, .A: Addend + 4}},
125 };
126 }
127 }
128 llvm_unreachable("Unknown PLTCallStubKind enum");
129}
130
131inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
132 Symbol *InitialTarget = nullptr,
133 uint64_t InitialAddend = 0) {
134 assert(G.getPointerSize() == sizeof(NullPointerContent) &&
135 "LinkGraph's pointer size should be consistent with size of "
136 "NullPointerContent");
137 Block &B = G.createContentBlock(Parent&: PointerSection, Content: NullPointerContent,
138 Address: orc::ExecutorAddr(), Alignment: G.getPointerSize(), AlignmentOffset: 0);
139 if (InitialTarget)
140 B.addEdge(K: Pointer64, Offset: 0, Target&: *InitialTarget, Addend: InitialAddend);
141 return G.addAnonymousSymbol(Content&: B, Offset: 0, Size: G.getPointerSize(), IsCallable: false, IsLive: false);
142}
143
144template <llvm::endianness Endianness>
145inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
146 Section &StubSection,
147 Symbol &PointerSymbol,
148 PLTCallStubKind StubKind) {
149 PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind);
150 Block &B = G.createContentBlock(Parent&: StubSection, Content: StubInfo.Content,
151 Address: orc::ExecutorAddr(), Alignment: 4, AlignmentOffset: 0);
152 for (auto const &Reloc : StubInfo.Relocs)
153 B.addEdge(K: Reloc.K, Offset: Reloc.Offset, Target&: PointerSymbol, Addend: Reloc.A);
154 return G.addAnonymousSymbol(Content&: B, Offset: 0, Size: StubInfo.Content.size(), IsCallable: true, IsLive: false);
155}
156
157template <llvm::endianness Endianness>
158class TOCTableManager : public TableManager<TOCTableManager<Endianness>> {
159public:
160 // FIXME: `llvm-jitlink -check` relies this name to be $__GOT.
161 static StringRef getSectionName() { return "$__GOT"; }
162
163 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
164 Edge::Kind K = E.getKind();
165 switch (K) {
166 case TOCDelta16HA:
167 case TOCDelta16LO:
168 case TOCDelta16DS:
169 case TOCDelta16LODS:
170 case CallBranchDeltaRestoreTOC:
171 case RequestCall:
172 // Create TOC section if TOC relocation, PLT or GOT is used.
173 getOrCreateTOCSection(G);
174 return false;
175 case RequestGOTAndTransformToDelta34:
176 E.setKind(ppc64::Delta34);
177 E.setTarget(createEntry(G, Target&: E.getTarget()));
178 return true;
179 default:
180 return false;
181 }
182 }
183
184 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
185 return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target);
186 }
187
188private:
189 Section &getOrCreateTOCSection(LinkGraph &G) {
190 TOCSection = G.findSectionByName(Name: getSectionName());
191 if (!TOCSection)
192 TOCSection = &G.createSection(Name: getSectionName(), Prot: orc::MemProt::Read);
193 return *TOCSection;
194 }
195
196 Section *TOCSection = nullptr;
197};
198
199template <llvm::endianness Endianness>
200class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
201public:
202 PLTTableManager(TOCTableManager<Endianness> &TOC) : TOC(TOC) {}
203
204 static StringRef getSectionName() { return "$__STUBS"; }
205
206 // FIXME: One external symbol can only have one PLT stub in a object file.
207 // This is a limitation when we need different PLT stubs for the same symbol.
208 // For example, we need two different PLT stubs for `bl __tls_get_addr` and
209 // `bl __tls_get_addr@notoc`.
210 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
211 bool isExternal = E.getTarget().isExternal();
212 Edge::Kind K = E.getKind();
213 if (K == ppc64::RequestCall) {
214 if (isExternal) {
215 E.setKind(ppc64::CallBranchDeltaRestoreTOC);
216 this->StubKind = LongBranchSaveR2;
217 // FIXME: We assume the addend to the external target is zero. It's
218 // quite unusual that the addend of an external target to be non-zero as
219 // if we have known the layout of the external object.
220 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
221 // Addend to the stub is zero.
222 E.setAddend(0);
223 } else
224 // TODO: There are cases a local function call need a call stub.
225 // 1. Caller uses TOC, the callee doesn't, need a r2 save stub.
226 // 2. Caller doesn't use TOC, the callee does, need a r12 setup stub.
227 // 3. Branching target is out of range.
228 E.setKind(ppc64::CallBranchDelta);
229 return true;
230 }
231 if (K == ppc64::RequestCallNoTOC) {
232 E.setKind(ppc64::CallBranchDelta);
233 this->StubKind = LongBranchNoTOC;
234 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
235 return true;
236 }
237 return false;
238 }
239
240 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
241 return createAnonymousPointerJumpStub<Endianness>(
242 G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
243 this->StubKind);
244 }
245
246private:
247 Section &getOrCreateStubsSection(LinkGraph &G) {
248 PLTSection = G.findSectionByName(Name: getSectionName());
249 if (!PLTSection)
250 PLTSection = &G.createSection(Name: getSectionName(),
251 Prot: orc::MemProt::Read | orc::MemProt::Exec);
252 return *PLTSection;
253 }
254
255 TOCTableManager<Endianness> &TOC;
256 Section *PLTSection = nullptr;
257 PLTCallStubKind StubKind;
258};
259
260/// Returns a string name for the given ppc64 edge. For debugging purposes
261/// only.
262const char *getEdgeKindName(Edge::Kind K);
263
264inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; }
265inline static uint64_t lo(uint64_t x) { return x & 0xffff; }
266inline static uint16_t hi(uint64_t x) { return x >> 16; }
267inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; }
268inline static uint64_t higha(uint64_t x) {
269 return ((x + 0x8000) >> 16) & 0xffff;
270}
271inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; }
272inline static uint64_t highera(uint64_t x) {
273 return ((x + 0x8000) >> 32) & 0xffff;
274}
275inline static uint16_t highest(uint64_t x) { return x >> 48; }
276inline static uint16_t highesta(uint64_t x) { return (x + 0x8000) >> 48; }
277
278// Prefixed instruction introduced in ISAv3.1 consists of two 32-bit words,
279// prefix word and suffix word, i.e., prefixed_instruction = concat(prefix_word,
280// suffix_word). That's to say, for a prefixed instruction encoded in uint64_t,
281// the most significant 32 bits belong to the prefix word. The prefix word is at
282// low address for both big/little endian. Byte order in each word still follows
283// its endian.
284template <llvm::endianness Endianness>
285inline static uint64_t readPrefixedInstruction(const char *Loc) {
286 constexpr bool isLE = Endianness == llvm::endianness::little;
287 uint64_t Inst = support::endian::read64<Endianness>(Loc);
288 return isLE ? (Inst << 32) | (Inst >> 32) : Inst;
289}
290
291template <llvm::endianness Endianness>
292inline static void writePrefixedInstruction(char *Loc, uint64_t Inst) {
293 constexpr bool isLE = Endianness == llvm::endianness::little;
294 Inst = isLE ? (Inst << 32) | (Inst >> 32) : Inst;
295 support::endian::write64<Endianness>(Loc, Inst);
296}
297
298template <llvm::endianness Endianness>
299inline Error relocateHalf16(char *FixupPtr, int64_t Value, Edge::Kind K) {
300 switch (K) {
301 case Delta16:
302 case Pointer16:
303 case TOCDelta16:
304 support::endian::write16<Endianness>(FixupPtr, Value);
305 break;
306 case Pointer16DS:
307 case TOCDelta16DS:
308 support::endian::write16<Endianness>(FixupPtr, Value & ~3);
309 break;
310 case Delta16HA:
311 case Pointer16HA:
312 case TOCDelta16HA:
313 support::endian::write16<Endianness>(FixupPtr, ha(x: Value));
314 break;
315 case Delta16HI:
316 case Pointer16HI:
317 case TOCDelta16HI:
318 support::endian::write16<Endianness>(FixupPtr, hi(x: Value));
319 break;
320 case Pointer16HIGH:
321 support::endian::write16<Endianness>(FixupPtr, high(x: Value));
322 break;
323 case Pointer16HIGHA:
324 support::endian::write16<Endianness>(FixupPtr, higha(x: Value));
325 break;
326 case Pointer16HIGHER:
327 support::endian::write16<Endianness>(FixupPtr, higher(x: Value));
328 break;
329 case Pointer16HIGHERA:
330 support::endian::write16<Endianness>(FixupPtr, highera(x: Value));
331 break;
332 case Pointer16HIGHEST:
333 support::endian::write16<Endianness>(FixupPtr, highest(x: Value));
334 break;
335 case Pointer16HIGHESTA:
336 support::endian::write16<Endianness>(FixupPtr, highesta(x: Value));
337 break;
338 case Delta16LO:
339 case Pointer16LO:
340 case TOCDelta16LO:
341 support::endian::write16<Endianness>(FixupPtr, lo(x: Value));
342 break;
343 case Pointer16LODS:
344 case TOCDelta16LODS:
345 support::endian::write16<Endianness>(FixupPtr, lo(x: Value) & ~3);
346 break;
347 default:
348 return make_error<JITLinkError>(
349 Args: StringRef(getEdgeKindName(K)) +
350 " relocation does not write at half16 field");
351 }
352 return Error::success();
353}
354
355/// Apply fixup expression for edge to block content.
356template <llvm::endianness Endianness>
357inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
358 const Symbol *TOCSymbol) {
359 char *BlockWorkingMem = B.getAlreadyMutableContent().data();
360 char *FixupPtr = BlockWorkingMem + E.getOffset();
361 orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
362 int64_t S = E.getTarget().getAddress().getValue();
363 int64_t A = E.getAddend();
364 int64_t P = FixupAddress.getValue();
365 int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0;
366 Edge::Kind K = E.getKind();
367
368 DEBUG_WITH_TYPE("jitlink", {
369 dbgs() << " Applying fixup on " << G.getEdgeKindName(K)
370 << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", "
371 << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", "
372 << formatv("{0:x}", TOCBase) << ")\n";
373 });
374
375 switch (K) {
376 case Pointer64: {
377 uint64_t Value = S + A;
378 support::endian::write64<Endianness>(FixupPtr, Value);
379 break;
380 }
381 case Delta16:
382 case Delta16HA:
383 case Delta16HI:
384 case Delta16LO: {
385 int64_t Value = S + A - P;
386 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
387 return makeTargetOutOfRangeError(G, B, E);
388 }
389 return relocateHalf16<Endianness>(FixupPtr, Value, K);
390 }
391 case TOC:
392 support::endian::write64<Endianness>(FixupPtr, TOCBase);
393 break;
394 case Pointer16:
395 case Pointer16DS:
396 case Pointer16HA:
397 case Pointer16HI:
398 case Pointer16HIGH:
399 case Pointer16HIGHA:
400 case Pointer16HIGHER:
401 case Pointer16HIGHERA:
402 case Pointer16HIGHEST:
403 case Pointer16HIGHESTA:
404 case Pointer16LO:
405 case Pointer16LODS: {
406 uint64_t Value = S + A;
407 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
408 return makeTargetOutOfRangeError(G, B, E);
409 }
410 return relocateHalf16<Endianness>(FixupPtr, Value, K);
411 }
412 case Pointer14: {
413 static const uint32_t Low14Mask = 0xfffc;
414 uint64_t Value = S + A;
415 assert((Value & 3) == 0 && "Pointer14 requires 4-byte alignment");
416 if (LLVM_UNLIKELY(!isInt<16>(Value))) {
417 return makeTargetOutOfRangeError(G, B, E);
418 }
419 uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
420 support::endian::write32<Endianness>(FixupPtr, (Inst & ~Low14Mask) |
421 (Value & Low14Mask));
422 break;
423 }
424 case TOCDelta16:
425 case TOCDelta16DS:
426 case TOCDelta16HA:
427 case TOCDelta16HI:
428 case TOCDelta16LO:
429 case TOCDelta16LODS: {
430 int64_t Value = S + A - TOCBase;
431 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
432 return makeTargetOutOfRangeError(G, B, E);
433 }
434 return relocateHalf16<Endianness>(FixupPtr, Value, K);
435 }
436 case CallBranchDeltaRestoreTOC:
437 case CallBranchDelta: {
438 int64_t Value = S + A - P;
439 if (LLVM_UNLIKELY(!isInt<26>(Value))) {
440 return makeTargetOutOfRangeError(G, B, E);
441 }
442 uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
443 support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) |
444 (Value & 0x03fffffc));
445 if (K == CallBranchDeltaRestoreTOC) {
446 uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4);
447 assert(NopInst == 0x60000000 &&
448 "NOP should be placed here for restoring r2");
449 (void)NopInst;
450 // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`.
451 support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018);
452 }
453 break;
454 }
455 case Delta64: {
456 int64_t Value = S + A - P;
457 support::endian::write64<Endianness>(FixupPtr, Value);
458 break;
459 }
460 case Delta34: {
461 int64_t Value = S + A - P;
462 if (!LLVM_UNLIKELY(isInt<34>(Value)))
463 return makeTargetOutOfRangeError(G, B, E);
464 static const uint64_t SI0Mask = 0x00000003ffff0000;
465 static const uint64_t SI1Mask = 0x000000000000ffff;
466 static const uint64_t FullMask = 0x0003ffff0000ffff;
467 uint64_t Inst = readPrefixedInstruction<Endianness>(FixupPtr) & ~FullMask;
468 writePrefixedInstruction<Endianness>(
469 FixupPtr, Inst | ((Value & SI0Mask) << 16) | (Value & SI1Mask));
470 break;
471 }
472 case Delta32: {
473 int64_t Value = S + A - P;
474 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
475 return makeTargetOutOfRangeError(G, B, E);
476 }
477 support::endian::write32<Endianness>(FixupPtr, Value);
478 break;
479 }
480 case NegDelta32: {
481 int64_t Value = P - S + A;
482 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
483 return makeTargetOutOfRangeError(G, B, E);
484 }
485 support::endian::write32<Endianness>(FixupPtr, Value);
486 break;
487 }
488 default:
489 return make_error<JITLinkError>(
490 Args: "In graph " + G.getName() + ", section " + B.getSection().getName() +
491 " unsupported edge kind " + getEdgeKindName(K: E.getKind()));
492 }
493 return Error::success();
494}
495
496} // end namespace llvm::jitlink::ppc64
497
498#endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
499

source code of llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h