File indexing completed on 2026-05-10 08:43:50
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #ifndef LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
0014 #define LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
0015
0016 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
0017 #include "llvm/ExecutionEngine/JITLink/TableManager.h"
0018 #include "llvm/Support/Endian.h"
0019
0020 namespace llvm::jitlink::ppc64 {
0021
0022
0023 enum EdgeKind_ppc64 : Edge::Kind {
0024 Pointer64 = Edge::FirstRelocation,
0025 Pointer32,
0026 Pointer16,
0027 Pointer16DS,
0028 Pointer16HA,
0029 Pointer16HI,
0030 Pointer16HIGH,
0031 Pointer16HIGHA,
0032 Pointer16HIGHER,
0033 Pointer16HIGHERA,
0034 Pointer16HIGHEST,
0035 Pointer16HIGHESTA,
0036 Pointer16LO,
0037 Pointer16LODS,
0038 Pointer14,
0039 Delta64,
0040 Delta34,
0041 Delta32,
0042 NegDelta32,
0043 Delta16,
0044 Delta16HA,
0045 Delta16HI,
0046 Delta16LO,
0047 TOC,
0048 TOCDelta16,
0049 TOCDelta16DS,
0050 TOCDelta16HA,
0051 TOCDelta16HI,
0052 TOCDelta16LO,
0053 TOCDelta16LODS,
0054 RequestGOTAndTransformToDelta34,
0055 CallBranchDelta,
0056
0057 CallBranchDeltaRestoreTOC,
0058
0059 RequestCall,
0060
0061 RequestCallNoTOC,
0062 RequestTLSDescInGOTAndTransformToTOCDelta16HA,
0063 RequestTLSDescInGOTAndTransformToTOCDelta16LO,
0064 RequestTLSDescInGOTAndTransformToDelta34,
0065 };
0066
0067 enum PLTCallStubKind {
0068
0069 LongBranch,
0070
0071 LongBranchSaveR2,
0072
0073 LongBranchNoTOC,
0074 };
0075
0076 extern const char NullPointerContent[8];
0077 extern const char PointerJumpStubContent_big[20];
0078 extern const char PointerJumpStubContent_little[20];
0079 extern const char PointerJumpStubNoTOCContent_big[32];
0080 extern const char PointerJumpStubNoTOCContent_little[32];
0081
0082 struct PLTCallStubReloc {
0083 Edge::Kind K;
0084 size_t Offset;
0085 Edge::AddendT A;
0086 };
0087
0088 struct PLTCallStubInfo {
0089 ArrayRef<char> Content;
0090 SmallVector<PLTCallStubReloc, 2> Relocs;
0091 };
0092
0093 template <llvm::endianness Endianness>
0094 inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) {
0095 constexpr bool isLE = Endianness == llvm::endianness::little;
0096 switch (StubKind) {
0097 case LongBranch: {
0098 ArrayRef<char> Content =
0099 isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
0100
0101 Content = Content.slice(4);
0102 size_t Offset = isLE ? 0 : 2;
0103 return PLTCallStubInfo{
0104 Content,
0105 {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
0106 };
0107 }
0108 case LongBranchSaveR2: {
0109 ArrayRef<char> Content =
0110 isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
0111 size_t Offset = isLE ? 4 : 6;
0112 return PLTCallStubInfo{
0113 Content,
0114 {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
0115 };
0116 }
0117 case LongBranchNoTOC: {
0118 ArrayRef<char> Content = isLE ? PointerJumpStubNoTOCContent_little
0119 : PointerJumpStubNoTOCContent_big;
0120 size_t Offset = isLE ? 16 : 18;
0121 Edge::AddendT Addend = isLE ? 8 : 10;
0122 return PLTCallStubInfo{
0123 Content,
0124 {{Delta16HA, Offset, Addend}, {Delta16LO, Offset + 4, Addend + 4}},
0125 };
0126 }
0127 }
0128 llvm_unreachable("Unknown PLTCallStubKind enum");
0129 }
0130
0131 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
0132 Symbol *InitialTarget = nullptr,
0133 uint64_t InitialAddend = 0) {
0134 assert(G.getPointerSize() == sizeof(NullPointerContent) &&
0135 "LinkGraph's pointer size should be consistent with size of "
0136 "NullPointerContent");
0137 Block &B = G.createContentBlock(PointerSection, NullPointerContent,
0138 orc::ExecutorAddr(), G.getPointerSize(), 0);
0139 if (InitialTarget)
0140 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
0141 return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
0142 }
0143
0144 template <llvm::endianness Endianness>
0145 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
0146 Section &StubSection,
0147 Symbol &PointerSymbol,
0148 PLTCallStubKind StubKind) {
0149 PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind);
0150 Block &B = G.createContentBlock(StubSection, StubInfo.Content,
0151 orc::ExecutorAddr(), 4, 0);
0152 for (auto const &Reloc : StubInfo.Relocs)
0153 B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A);
0154 return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false);
0155 }
0156
0157 template <llvm::endianness Endianness>
0158 class TOCTableManager : public TableManager<TOCTableManager<Endianness>> {
0159 public:
0160
0161 static StringRef getSectionName() { return "$__GOT"; }
0162
0163 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
0164 Edge::Kind K = E.getKind();
0165 switch (K) {
0166 case TOCDelta16HA:
0167 case TOCDelta16LO:
0168 case TOCDelta16DS:
0169 case TOCDelta16LODS:
0170 case CallBranchDeltaRestoreTOC:
0171 case RequestCall:
0172
0173 getOrCreateTOCSection(G);
0174 return false;
0175 case RequestGOTAndTransformToDelta34:
0176 E.setKind(ppc64::Delta34);
0177 E.setTarget(createEntry(G, E.getTarget()));
0178 return true;
0179 default:
0180 return false;
0181 }
0182 }
0183
0184 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
0185 return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target);
0186 }
0187
0188 private:
0189 Section &getOrCreateTOCSection(LinkGraph &G) {
0190 TOCSection = G.findSectionByName(getSectionName());
0191 if (!TOCSection)
0192 TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read);
0193 return *TOCSection;
0194 }
0195
0196 Section *TOCSection = nullptr;
0197 };
0198
0199 template <llvm::endianness Endianness>
0200 class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
0201 public:
0202 PLTTableManager(TOCTableManager<Endianness> &TOC) : TOC(TOC) {}
0203
0204 static StringRef getSectionName() { return "$__STUBS"; }
0205
0206
0207
0208
0209
0210 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
0211 bool isExternal = E.getTarget().isExternal();
0212 Edge::Kind K = E.getKind();
0213 if (K == ppc64::RequestCall) {
0214 if (isExternal) {
0215 E.setKind(ppc64::CallBranchDeltaRestoreTOC);
0216 this->StubKind = LongBranchSaveR2;
0217
0218
0219
0220 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
0221
0222 E.setAddend(0);
0223 } else
0224
0225
0226
0227
0228 E.setKind(ppc64::CallBranchDelta);
0229 return true;
0230 }
0231 if (K == ppc64::RequestCallNoTOC) {
0232 E.setKind(ppc64::CallBranchDelta);
0233 this->StubKind = LongBranchNoTOC;
0234 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
0235 return true;
0236 }
0237 return false;
0238 }
0239
0240 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
0241 return createAnonymousPointerJumpStub<Endianness>(
0242 G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
0243 this->StubKind);
0244 }
0245
0246 private:
0247 Section &getOrCreateStubsSection(LinkGraph &G) {
0248 PLTSection = G.findSectionByName(getSectionName());
0249 if (!PLTSection)
0250 PLTSection = &G.createSection(getSectionName(),
0251 orc::MemProt::Read | orc::MemProt::Exec);
0252 return *PLTSection;
0253 }
0254
0255 TOCTableManager<Endianness> &TOC;
0256 Section *PLTSection = nullptr;
0257 PLTCallStubKind StubKind;
0258 };
0259
0260
0261
0262 const char *getEdgeKindName(Edge::Kind K);
0263
0264 inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; }
0265 inline static uint64_t lo(uint64_t x) { return x & 0xffff; }
0266 inline static uint16_t hi(uint64_t x) { return x >> 16; }
0267 inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; }
0268 inline static uint64_t higha(uint64_t x) {
0269 return ((x + 0x8000) >> 16) & 0xffff;
0270 }
0271 inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; }
0272 inline static uint64_t highera(uint64_t x) {
0273 return ((x + 0x8000) >> 32) & 0xffff;
0274 }
0275 inline static uint16_t highest(uint64_t x) { return x >> 48; }
0276 inline static uint16_t highesta(uint64_t x) { return (x + 0x8000) >> 48; }
0277
0278
0279
0280
0281
0282
0283
0284 template <llvm::endianness Endianness>
0285 inline static uint64_t readPrefixedInstruction(const char *Loc) {
0286 constexpr bool isLE = Endianness == llvm::endianness::little;
0287 uint64_t Inst = support::endian::read64<Endianness>(Loc);
0288 return isLE ? (Inst << 32) | (Inst >> 32) : Inst;
0289 }
0290
0291 template <llvm::endianness Endianness>
0292 inline static void writePrefixedInstruction(char *Loc, uint64_t Inst) {
0293 constexpr bool isLE = Endianness == llvm::endianness::little;
0294 Inst = isLE ? (Inst << 32) | (Inst >> 32) : Inst;
0295 support::endian::write64<Endianness>(Loc, Inst);
0296 }
0297
0298 template <llvm::endianness Endianness>
0299 inline Error relocateHalf16(char *FixupPtr, int64_t Value, Edge::Kind K) {
0300 switch (K) {
0301 case Delta16:
0302 case Pointer16:
0303 case TOCDelta16:
0304 support::endian::write16<Endianness>(FixupPtr, Value);
0305 break;
0306 case Pointer16DS:
0307 case TOCDelta16DS:
0308 support::endian::write16<Endianness>(FixupPtr, Value & ~3);
0309 break;
0310 case Delta16HA:
0311 case Pointer16HA:
0312 case TOCDelta16HA:
0313 support::endian::write16<Endianness>(FixupPtr, ha(Value));
0314 break;
0315 case Delta16HI:
0316 case Pointer16HI:
0317 case TOCDelta16HI:
0318 support::endian::write16<Endianness>(FixupPtr, hi(Value));
0319 break;
0320 case Pointer16HIGH:
0321 support::endian::write16<Endianness>(FixupPtr, high(Value));
0322 break;
0323 case Pointer16HIGHA:
0324 support::endian::write16<Endianness>(FixupPtr, higha(Value));
0325 break;
0326 case Pointer16HIGHER:
0327 support::endian::write16<Endianness>(FixupPtr, higher(Value));
0328 break;
0329 case Pointer16HIGHERA:
0330 support::endian::write16<Endianness>(FixupPtr, highera(Value));
0331 break;
0332 case Pointer16HIGHEST:
0333 support::endian::write16<Endianness>(FixupPtr, highest(Value));
0334 break;
0335 case Pointer16HIGHESTA:
0336 support::endian::write16<Endianness>(FixupPtr, highesta(Value));
0337 break;
0338 case Delta16LO:
0339 case Pointer16LO:
0340 case TOCDelta16LO:
0341 support::endian::write16<Endianness>(FixupPtr, lo(Value));
0342 break;
0343 case Pointer16LODS:
0344 case TOCDelta16LODS:
0345 support::endian::write16<Endianness>(FixupPtr, lo(Value) & ~3);
0346 break;
0347 default:
0348 return make_error<JITLinkError>(
0349 StringRef(getEdgeKindName(K)) +
0350 " relocation does not write at half16 field");
0351 }
0352 return Error::success();
0353 }
0354
0355
0356 template <llvm::endianness Endianness>
0357 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
0358 const Symbol *TOCSymbol) {
0359 char *BlockWorkingMem = B.getAlreadyMutableContent().data();
0360 char *FixupPtr = BlockWorkingMem + E.getOffset();
0361 orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
0362 int64_t S = E.getTarget().getAddress().getValue();
0363 int64_t A = E.getAddend();
0364 int64_t P = FixupAddress.getValue();
0365 int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0;
0366 Edge::Kind K = E.getKind();
0367
0368 DEBUG_WITH_TYPE("jitlink", {
0369 dbgs() << " Applying fixup on " << G.getEdgeKindName(K)
0370 << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", "
0371 << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", "
0372 << formatv("{0:x}", TOCBase) << ")\n";
0373 });
0374
0375 switch (K) {
0376 case Pointer64: {
0377 uint64_t Value = S + A;
0378 support::endian::write64<Endianness>(FixupPtr, Value);
0379 break;
0380 }
0381 case Delta16:
0382 case Delta16HA:
0383 case Delta16HI:
0384 case Delta16LO: {
0385 int64_t Value = S + A - P;
0386 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0387 return makeTargetOutOfRangeError(G, B, E);
0388 }
0389 return relocateHalf16<Endianness>(FixupPtr, Value, K);
0390 }
0391 case TOC:
0392 support::endian::write64<Endianness>(FixupPtr, TOCBase);
0393 break;
0394 case Pointer16:
0395 case Pointer16DS:
0396 case Pointer16HA:
0397 case Pointer16HI:
0398 case Pointer16HIGH:
0399 case Pointer16HIGHA:
0400 case Pointer16HIGHER:
0401 case Pointer16HIGHERA:
0402 case Pointer16HIGHEST:
0403 case Pointer16HIGHESTA:
0404 case Pointer16LO:
0405 case Pointer16LODS: {
0406 uint64_t Value = S + A;
0407 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0408 return makeTargetOutOfRangeError(G, B, E);
0409 }
0410 return relocateHalf16<Endianness>(FixupPtr, Value, K);
0411 }
0412 case Pointer14: {
0413 static const uint32_t Low14Mask = 0xfffc;
0414 uint64_t Value = S + A;
0415 assert((Value & 3) == 0 && "Pointer14 requires 4-byte alignment");
0416 if (LLVM_UNLIKELY(!isInt<16>(Value))) {
0417 return makeTargetOutOfRangeError(G, B, E);
0418 }
0419 uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
0420 support::endian::write32<Endianness>(FixupPtr, (Inst & ~Low14Mask) |
0421 (Value & Low14Mask));
0422 break;
0423 }
0424 case TOCDelta16:
0425 case TOCDelta16DS:
0426 case TOCDelta16HA:
0427 case TOCDelta16HI:
0428 case TOCDelta16LO:
0429 case TOCDelta16LODS: {
0430 int64_t Value = S + A - TOCBase;
0431 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0432 return makeTargetOutOfRangeError(G, B, E);
0433 }
0434 return relocateHalf16<Endianness>(FixupPtr, Value, K);
0435 }
0436 case CallBranchDeltaRestoreTOC:
0437 case CallBranchDelta: {
0438 int64_t Value = S + A - P;
0439 if (LLVM_UNLIKELY(!isInt<26>(Value))) {
0440 return makeTargetOutOfRangeError(G, B, E);
0441 }
0442 uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
0443 support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) |
0444 (Value & 0x03fffffc));
0445 if (K == CallBranchDeltaRestoreTOC) {
0446 uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4);
0447 assert(NopInst == 0x60000000 &&
0448 "NOP should be placed here for restoring r2");
0449 (void)NopInst;
0450
0451 support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018);
0452 }
0453 break;
0454 }
0455 case Delta64: {
0456 int64_t Value = S + A - P;
0457 support::endian::write64<Endianness>(FixupPtr, Value);
0458 break;
0459 }
0460 case Delta34: {
0461 int64_t Value = S + A - P;
0462 if (!LLVM_UNLIKELY(isInt<34>(Value)))
0463 return makeTargetOutOfRangeError(G, B, E);
0464 static const uint64_t SI0Mask = 0x00000003ffff0000;
0465 static const uint64_t SI1Mask = 0x000000000000ffff;
0466 static const uint64_t FullMask = 0x0003ffff0000ffff;
0467 uint64_t Inst = readPrefixedInstruction<Endianness>(FixupPtr) & ~FullMask;
0468 writePrefixedInstruction<Endianness>(
0469 FixupPtr, Inst | ((Value & SI0Mask) << 16) | (Value & SI1Mask));
0470 break;
0471 }
0472 case Delta32: {
0473 int64_t Value = S + A - P;
0474 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0475 return makeTargetOutOfRangeError(G, B, E);
0476 }
0477 support::endian::write32<Endianness>(FixupPtr, Value);
0478 break;
0479 }
0480 case NegDelta32: {
0481 int64_t Value = P - S + A;
0482 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0483 return makeTargetOutOfRangeError(G, B, E);
0484 }
0485 support::endian::write32<Endianness>(FixupPtr, Value);
0486 break;
0487 }
0488 default:
0489 return make_error<JITLinkError>(
0490 "In graph " + G.getName() + ", section " + B.getSection().getName() +
0491 " unsupported edge kind " + getEdgeKindName(E.getKind()));
0492 }
0493 return Error::success();
0494 }
0495
0496 }
0497
0498 #endif