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 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 67 enum 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 76 extern const char NullPointerContent[8]; 77 extern const char PointerJumpStubContent_big[20]; 78 extern const char PointerJumpStubContent_little[20]; 79 extern const char PointerJumpStubNoTOCContent_big[32]; 80 extern const char PointerJumpStubNoTOCContent_little[32]; 81 82 struct PLTCallStubReloc { 83 Edge::Kind K; 84 size_t Offset; 85 Edge::AddendT A; 86 }; 87 88 struct PLTCallStubInfo { 89 ArrayRef<char> Content; 90 SmallVector<PLTCallStubReloc, 2> Relocs; 91 }; 92 93 template <llvm::endianness Endianness> 94 inline 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(4); 102 size_t Offset = isLE ? 0 : 2; 103 return PLTCallStubInfo{ 104 Content, 105 {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 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, 114 {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 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, 124 {{Delta16HA, Offset, Addend}, {Delta16LO, Offset + 4, Addend + 4}}, 125 }; 126 } 127 } 128 llvm_unreachable("Unknown PLTCallStubKind enum"); 129 } 130 131 inline 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(PointerSection, NullPointerContent, 138 orc::ExecutorAddr(), G.getPointerSize(), 0); 139 if (InitialTarget) 140 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend); 141 return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false); 142 } 143 144 template <llvm::endianness Endianness> 145 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, 146 Section &StubSection, 147 Symbol &PointerSymbol, 148 PLTCallStubKind StubKind) { 149 PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind); 150 Block &B = G.createContentBlock(StubSection, StubInfo.Content, 151 orc::ExecutorAddr(), 4, 0); 152 for (auto const &Reloc : StubInfo.Relocs) 153 B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A); 154 return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false); 155 } 156 157 template <llvm::endianness Endianness> 158 class TOCTableManager : public TableManager<TOCTableManager<Endianness>> { 159 public: 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, 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 188 private: 189 Section &getOrCreateTOCSection(LinkGraph &G) { 190 TOCSection = G.findSectionByName(getSectionName()); 191 if (!TOCSection) 192 TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read); 193 return *TOCSection; 194 } 195 196 Section *TOCSection = nullptr; 197 }; 198 199 template <llvm::endianness Endianness> 200 class PLTTableManager : public TableManager<PLTTableManager<Endianness>> { 201 public: 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 246 private: 247 Section &getOrCreateStubsSection(LinkGraph &G) { 248 PLTSection = G.findSectionByName(getSectionName()); 249 if (!PLTSection) 250 PLTSection = &G.createSection(getSectionName(), 251 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. 262 const char *getEdgeKindName(Edge::Kind K); 263 264 inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; } 265 inline static uint64_t lo(uint64_t x) { return x & 0xffff; } 266 inline static uint16_t hi(uint64_t x) { return x >> 16; } 267 inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; } 268 inline static uint64_t higha(uint64_t x) { 269 return ((x + 0x8000) >> 16) & 0xffff; 270 } 271 inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; } 272 inline static uint64_t highera(uint64_t x) { 273 return ((x + 0x8000) >> 32) & 0xffff; 274 } 275 inline static uint16_t highest(uint64_t x) { return x >> 48; } 276 inline 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. 284 template <llvm::endianness Endianness> 285 inline 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 291 template <llvm::endianness Endianness> 292 inline 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 298 template <llvm::endianness Endianness> 299 inline 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(Value)); 314 break; 315 case Delta16HI: 316 case Pointer16HI: 317 case TOCDelta16HI: 318 support::endian::write16<Endianness>(FixupPtr, hi(Value)); 319 break; 320 case Pointer16HIGH: 321 support::endian::write16<Endianness>(FixupPtr, high(Value)); 322 break; 323 case Pointer16HIGHA: 324 support::endian::write16<Endianness>(FixupPtr, higha(Value)); 325 break; 326 case Pointer16HIGHER: 327 support::endian::write16<Endianness>(FixupPtr, higher(Value)); 328 break; 329 case Pointer16HIGHERA: 330 support::endian::write16<Endianness>(FixupPtr, highera(Value)); 331 break; 332 case Pointer16HIGHEST: 333 support::endian::write16<Endianness>(FixupPtr, highest(Value)); 334 break; 335 case Pointer16HIGHESTA: 336 support::endian::write16<Endianness>(FixupPtr, highesta(Value)); 337 break; 338 case Delta16LO: 339 case Pointer16LO: 340 case TOCDelta16LO: 341 support::endian::write16<Endianness>(FixupPtr, lo(Value)); 342 break; 343 case Pointer16LODS: 344 case TOCDelta16LODS: 345 support::endian::write16<Endianness>(FixupPtr, lo(Value) & ~3); 346 break; 347 default: 348 return make_error<JITLinkError>( 349 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. 356 template <llvm::endianness Endianness> 357 inline 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 "In graph " + G.getName() + ", section " + B.getSection().getName() + 491 " unsupported edge kind " + getEdgeKindName(E.getKind())); 492 } 493 return Error::success(); 494 } 495 496 } // end namespace llvm::jitlink::ppc64 497 498 #endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H 499