1 //===-- x86_64.h - Generic JITLink x86-64 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 x86-64 objects. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_JITLINK_X86_64_H 14 #define LLVM_EXECUTIONENGINE_JITLINK_X86_64_H 15 16 #include "llvm/ExecutionEngine/JITLink/JITLink.h" 17 #include "llvm/ExecutionEngine/JITLink/TableManager.h" 18 19 #include <limits> 20 21 namespace llvm { 22 namespace jitlink { 23 namespace x86_64 { 24 25 /// Represents x86-64 fixups and other x86-64-specific edge kinds. 26 enum EdgeKind_x86_64 : Edge::Kind { 27 28 /// A plain 64-bit pointer value relocation. 29 /// 30 /// Fixup expression: 31 /// Fixup <- Target + Addend : uint64 32 /// 33 Pointer64 = Edge::FirstRelocation, 34 35 /// A plain 32-bit pointer value relocation. 36 /// 37 /// Fixup expression: 38 /// Fixup <- Target + Addend : uint32 39 /// 40 /// Errors: 41 /// - The target must reside in the low 32-bits of the address space, 42 /// otherwise an out-of-range error will be returned. 43 /// 44 Pointer32, 45 46 /// A signed 32-bit pointer value relocation 47 /// 48 /// Fixup expression: 49 /// Fixup <- Target + Addend : int32 50 /// 51 /// Errors: 52 /// - The target must reside in the signed 32-bits([-2**31, 2**32 - 1]) of 53 /// the address space, otherwise an out-of-range error will be returned. 54 Pointer32Signed, 55 56 /// A 64-bit delta. 57 /// 58 /// Delta from the fixup to the target. 59 /// 60 /// Fixup expression: 61 /// Fixup <- Target - Fixup + Addend : int64 62 /// 63 Delta64, 64 65 /// A 32-bit delta. 66 /// 67 /// Delta from the fixup to the target. 68 /// 69 /// Fixup expression: 70 /// Fixup <- Target - Fixup + Addend : int64 71 /// 72 /// Errors: 73 /// - The result of the fixup expression must fit into an int32, otherwise 74 /// an out-of-range error will be returned. 75 /// 76 Delta32, 77 78 /// A 64-bit negative delta. 79 /// 80 /// Delta from target back to the fixup. 81 /// 82 /// Fixup expression: 83 /// Fixup <- Fixup - Target + Addend : int64 84 /// 85 NegDelta64, 86 87 /// A 32-bit negative delta. 88 /// 89 /// Delta from the target back to the fixup. 90 /// 91 /// Fixup expression: 92 /// Fixup <- Fixup - Target + Addend : int32 93 /// 94 /// Errors: 95 /// - The result of the fixup expression must fit into an int32, otherwise 96 /// an out-of-range error will be returned. 97 NegDelta32, 98 99 /// A 64-bit GOT delta. 100 /// 101 /// Delta from the global offset table to the target 102 /// 103 /// Fixup expression: 104 /// Fixup <- Target - GOTSymbol + Addend : int64 105 /// 106 /// Errors: 107 /// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section 108 /// symbol was not been defined. 109 Delta64FromGOT, 110 111 /// A 32-bit PC-relative branch. 112 /// 113 /// Represents a PC-relative call or branch to a target. This can be used to 114 /// identify, record, and/or patch call sites. 115 /// 116 /// The fixup expression for this kind includes an implicit offset to account 117 /// for the PC (unlike the Delta edges) so that a Branch32PCRel with a target 118 /// T and addend zero is a call/branch to the start (offset zero) of T. 119 /// 120 /// Fixup expression: 121 /// Fixup <- Target - (Fixup + 4) + Addend : int32 122 /// 123 /// Errors: 124 /// - The result of the fixup expression must fit into an int32, otherwise 125 /// an out-of-range error will be returned. 126 /// 127 BranchPCRel32, 128 129 /// A 32-bit PC-relative branch to a pointer jump stub. 130 /// 131 /// The target of this relocation should be a pointer jump stub of the form: 132 /// 133 /// \code{.s} 134 /// .text 135 /// jmpq *tgtptr(%rip) 136 /// ; ... 137 /// 138 /// .data 139 /// tgtptr: 140 /// .quad 0 141 /// \endcode 142 /// 143 /// This edge kind has the same fixup expression as BranchPCRel32, but further 144 /// identifies the call/branch as being to a pointer jump stub. For edges of 145 /// this kind the jump stub should not be bypassed (use 146 /// BranchPCRel32ToPtrJumpStubBypassable for that), but the pointer location 147 /// target may be recorded to allow manipulation at runtime. 148 /// 149 /// Fixup expression: 150 /// Fixup <- Target - Fixup + Addend - 4 : int32 151 /// 152 /// Errors: 153 /// - The result of the fixup expression must fit into an int32, otherwise 154 /// an out-of-range error will be returned. 155 /// 156 BranchPCRel32ToPtrJumpStub, 157 158 /// A relaxable version of BranchPCRel32ToPtrJumpStub. 159 /// 160 /// The edge kind has the same fixup expression as BranchPCRel32ToPtrJumpStub, 161 /// but identifies the call/branch as being to a pointer jump stub that may be 162 /// bypassed with a direct jump to the ultimate target if the ultimate target 163 /// is within range of the fixup location. 164 /// 165 /// Fixup expression: 166 /// Fixup <- Target - Fixup + Addend - 4: int32 167 /// 168 /// Errors: 169 /// - The result of the fixup expression must fit into an int32, otherwise 170 /// an out-of-range error will be returned. 171 /// 172 BranchPCRel32ToPtrJumpStubBypassable, 173 174 /// A GOT entry getter/constructor, transformed to Delta32 pointing at the GOT 175 /// entry for the original target. 176 /// 177 /// Indicates that this edge should be transformed into a Delta32 targeting 178 /// the GOT entry for the edge's current target, maintaining the same addend. 179 /// A GOT entry for the target should be created if one does not already 180 /// exist. 181 /// 182 /// Edges of this kind are usually handled by a GOT builder pass inserted by 183 /// default. 184 /// 185 /// Fixup expression: 186 /// NONE 187 /// 188 /// Errors: 189 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 190 /// phase will result in an assert/unreachable during the fixup phase. 191 /// 192 RequestGOTAndTransformToDelta32, 193 194 /// A GOT entry getter/constructor, transformed to Delta64 pointing at the GOT 195 /// entry for the original target. 196 /// 197 /// Indicates that this edge should be transformed into a Delta64 targeting 198 /// the GOT entry for the edge's current target, maintaining the same addend. 199 /// A GOT entry for the target should be created if one does not already 200 /// exist. 201 /// 202 /// Edges of this kind are usually handled by a GOT builder pass inserted by 203 /// default. 204 /// 205 /// Fixup expression: 206 /// NONE 207 /// 208 /// Errors: 209 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 210 /// phase will result in an assert/unreachable during the fixup phase. 211 /// 212 RequestGOTAndTransformToDelta64, 213 214 /// A GOT entry offset within GOT getter/constructor, transformed to 215 /// Delta64FromGOT 216 /// pointing at the GOT entry for the original target 217 /// 218 /// Indicates that this edge should be transformed into a Delta64FromGOT 219 /// targeting 220 /// the GOT entry for the edge's current target, maintaining the same addend. 221 /// A GOT entry for the target should be created if one does not already 222 /// exist. 223 /// 224 /// Edges of this kind are usually handled by a GOT builder pass inserted by 225 /// default 226 /// 227 /// Fixup expression: 228 /// NONE 229 /// 230 /// Errors: 231 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 232 /// phase will result in an assert/unreachable during the fixup phase 233 RequestGOTAndTransformToDelta64FromGOT, 234 235 /// A PC-relative load of a GOT entry, relaxable if GOT entry target is 236 /// in-range of the fixup 237 /// 238 /// TODO: Explain the optimization 239 /// 240 /// Fixup expression 241 /// Fixup <- Target - (Fixup + 4) + Addend : int32 242 /// 243 /// Errors: 244 /// - The result of the fixup expression must fit into an int32, otherwise 245 /// an out-of-range error will be returned. 246 // 247 PCRel32GOTLoadRelaxable, 248 249 /// A PC-relative REX load of a GOT entry, relaxable if GOT entry target 250 /// is in-range of the fixup. 251 /// 252 /// If the GOT entry target is in-range of the fixup then the load from the 253 /// GOT may be replaced with a direct memory address calculation. 254 /// 255 /// Fixup expression: 256 /// Fixup <- Target - (Fixup + 4) + Addend : int32 257 /// 258 /// Errors: 259 /// - The result of the fixup expression must fit into an int32, otherwise 260 /// an out-of-range error will be returned. 261 /// 262 PCRel32GOTLoadREXRelaxable, 263 264 /// A GOT entry getter/constructor, transformed to 265 /// PCRel32ToGOTLoadREXRelaxable pointing at the GOT entry for the original 266 /// target. 267 /// 268 /// Indicates that this edge should be lowered to a PC32ToGOTLoadREXRelaxable 269 /// targeting the GOT entry for the edge's current target, maintaining the 270 /// same addend. A GOT entry for the target should be created if one does not 271 /// already exist. 272 /// 273 /// Edges of this kind are usually lowered by a GOT builder pass inserted by 274 /// default. 275 /// 276 /// Fixup expression: 277 /// NONE 278 /// 279 /// Errors: 280 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 281 /// phase will result in an assert/unreachable during the fixup phase. 282 /// 283 RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable, 284 285 /// A GOT entry getter/constructor, transformed to 286 /// PCRel32ToGOTLoadRelaxable pointing at the GOT entry for the original 287 /// target. 288 /// 289 /// Indicates that this edge should be lowered to a PC32ToGOTLoadRelaxable 290 /// targeting the GOT entry for the edge's current target, maintaining the 291 /// same addend. A GOT entry for the target should be created if one does not 292 /// already exist. 293 /// 294 /// Edges of this kind are usually lowered by a GOT builder pass inserted by 295 /// default. 296 /// 297 /// Fixup expression: 298 /// NONE 299 /// 300 /// Errors: 301 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 302 /// phase will result in an assert/unreachable during the fixup phase. 303 /// 304 RequestGOTAndTransformToPCRel32GOTLoadRelaxable, 305 306 /// A PC-relative REX load of a Thread Local Variable Pointer (TLVP) entry, 307 /// relaxable if the TLVP entry target is in-range of the fixup. 308 /// 309 /// If the TLVP entry target is in-range of the fixup then the load from the 310 /// TLVP may be replaced with a direct memory address calculation. 311 /// 312 /// The target of this edge must be a thread local variable entry of the form 313 /// .quad <tlv getter thunk> 314 /// .quad <tlv key> 315 /// .quad <tlv initializer> 316 /// 317 /// Fixup expression: 318 /// Fixup <- Target - (Fixup + 4) + Addend : int32 319 /// 320 /// Errors: 321 /// - The result of the fixup expression must fit into an int32, otherwise 322 /// an out-of-range error will be returned. 323 /// - The target must be either external, or a TLV entry of the required 324 /// form, otherwise a malformed TLV entry error will be returned. 325 /// 326 PCRel32TLVPLoadREXRelaxable, 327 328 /// TODO: Explain the generic edge kind 329 RequestTLSDescInGOTAndTransformToDelta32, 330 331 /// A TLVP entry getter/constructor, transformed to 332 /// Delta32ToTLVPLoadREXRelaxable. 333 /// 334 /// Indicates that this edge should be transformed into a 335 /// Delta32ToTLVPLoadREXRelaxable targeting the TLVP entry for the edge's 336 /// current target. A TLVP entry for the target should be created if one does 337 /// not already exist. 338 /// 339 /// Fixup expression: 340 /// NONE 341 /// 342 /// Errors: 343 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 344 /// phase will result in an assert/unreachable during the fixup phase. 345 /// 346 RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable 347 }; 348 349 /// Returns a string name for the given x86-64 edge. For debugging purposes 350 /// only. 351 const char *getEdgeKindName(Edge::Kind K); 352 353 /// Returns true if the given uint64_t value is in range for a uint32_t. 354 inline bool isInRangeForImmU32(uint64_t Value) { 355 return Value <= std::numeric_limits<uint32_t>::max(); 356 } 357 358 /// Returns true if the given int64_t value is in range for an int32_t. 359 inline bool isInRangeForImmS32(int64_t Value) { 360 return (Value >= std::numeric_limits<int32_t>::min() && 361 Value <= std::numeric_limits<int32_t>::max()); 362 } 363 364 /// Apply fixup expression for edge to block content. 365 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, 366 const Symbol *GOTSymbol) { 367 using namespace support; 368 369 char *BlockWorkingMem = B.getAlreadyMutableContent().data(); 370 char *FixupPtr = BlockWorkingMem + E.getOffset(); 371 auto FixupAddress = B.getAddress() + E.getOffset(); 372 373 switch (E.getKind()) { 374 375 case Pointer64: { 376 uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 377 *(ulittle64_t *)FixupPtr = Value; 378 break; 379 } 380 381 case Pointer32: { 382 uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 383 if (LLVM_LIKELY(isInRangeForImmU32(Value))) 384 *(ulittle32_t *)FixupPtr = Value; 385 else 386 return makeTargetOutOfRangeError(G, B, E); 387 break; 388 } 389 case Pointer32Signed: { 390 int64_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 391 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 392 *(little32_t *)FixupPtr = Value; 393 else 394 return makeTargetOutOfRangeError(G, B, E); 395 break; 396 } 397 398 case BranchPCRel32: 399 case BranchPCRel32ToPtrJumpStub: 400 case BranchPCRel32ToPtrJumpStubBypassable: 401 case PCRel32GOTLoadRelaxable: 402 case PCRel32GOTLoadREXRelaxable: 403 case PCRel32TLVPLoadREXRelaxable: { 404 int64_t Value = 405 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 406 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 407 *(little32_t *)FixupPtr = Value; 408 else 409 return makeTargetOutOfRangeError(G, B, E); 410 break; 411 } 412 413 case Delta64: { 414 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 415 *(little64_t *)FixupPtr = Value; 416 break; 417 } 418 419 case Delta32: { 420 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 421 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 422 *(little32_t *)FixupPtr = Value; 423 else 424 return makeTargetOutOfRangeError(G, B, E); 425 break; 426 } 427 428 case NegDelta64: { 429 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend(); 430 *(little64_t *)FixupPtr = Value; 431 break; 432 } 433 434 case NegDelta32: { 435 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend(); 436 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 437 *(little32_t *)FixupPtr = Value; 438 else 439 return makeTargetOutOfRangeError(G, B, E); 440 break; 441 } 442 case Delta64FromGOT: { 443 assert(GOTSymbol && "No GOT section symbol"); 444 int64_t Value = 445 E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend(); 446 *(little64_t *)FixupPtr = Value; 447 break; 448 } 449 450 default: { 451 // If you hit this you should check that *constructor and other non-fixup 452 // edges have been removed prior to applying fixups. 453 llvm_unreachable("Graph contains edge kind with no fixup expression"); 454 } 455 } 456 457 return Error::success(); 458 } 459 460 /// x86_64 pointer size. 461 constexpr uint64_t PointerSize = 8; 462 463 /// x86-64 null pointer content. 464 extern const char NullPointerContent[PointerSize]; 465 466 /// x86-64 pointer jump stub content. 467 /// 468 /// Contains the instruction sequence for an indirect jump via an in-memory 469 /// pointer: 470 /// jmpq *ptr(%rip) 471 extern const char PointerJumpStubContent[6]; 472 473 /// Creates a new pointer block in the given section and returns an anonymous 474 /// symbol pointing to it. 475 /// 476 /// If InitialTarget is given then an Pointer64 relocation will be added to the 477 /// block pointing at InitialTarget. 478 /// 479 /// The pointer block will have the following default values: 480 /// alignment: 64-bit 481 /// alignment-offset: 0 482 /// address: highest allowable (~7U) 483 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, 484 Symbol *InitialTarget = nullptr, 485 uint64_t InitialAddend = 0) { 486 auto &B = G.createContentBlock(PointerSection, NullPointerContent, 487 orc::ExecutorAddr(~uint64_t(7)), 8, 0); 488 if (InitialTarget) 489 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend); 490 return G.addAnonymousSymbol(B, 0, 8, false, false); 491 } 492 493 /// Create a jump stub block that jumps via the pointer at the given symbol. 494 /// 495 /// The stub block will have the following default values: 496 /// alignment: 8-bit 497 /// alignment-offset: 0 498 /// address: highest allowable: (~5U) 499 inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection, 500 Symbol &PointerSymbol) { 501 auto &B = G.createContentBlock(StubSection, PointerJumpStubContent, 502 orc::ExecutorAddr(~uint64_t(5)), 1, 0); 503 B.addEdge(Delta32, 2, PointerSymbol, -4); 504 return B; 505 } 506 507 /// Create a jump stub that jumps via the pointer at the given symbol and 508 /// an anonymous symbol pointing to it. Return the anonymous symbol. 509 /// 510 /// The stub block will be created by createPointerJumpStubBlock. 511 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, 512 Section &StubSection, 513 Symbol &PointerSymbol) { 514 return G.addAnonymousSymbol( 515 createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true, 516 false); 517 } 518 519 /// Global Offset Table Builder. 520 class GOTTableManager : public TableManager<GOTTableManager> { 521 public: 522 static StringRef getSectionName() { return "$__GOT"; } 523 524 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 525 Edge::Kind KindToSet = Edge::Invalid; 526 switch (E.getKind()) { 527 case x86_64::Delta64FromGOT: { 528 // we need to make sure that the GOT section exists, but don't otherwise 529 // need to fix up this edge 530 getGOTSection(G); 531 return false; 532 } 533 case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: 534 KindToSet = x86_64::PCRel32GOTLoadREXRelaxable; 535 break; 536 case x86_64::RequestGOTAndTransformToPCRel32GOTLoadRelaxable: 537 KindToSet = x86_64::PCRel32GOTLoadRelaxable; 538 break; 539 case x86_64::RequestGOTAndTransformToDelta64: 540 KindToSet = x86_64::Delta64; 541 break; 542 case x86_64::RequestGOTAndTransformToDelta64FromGOT: 543 KindToSet = x86_64::Delta64FromGOT; 544 break; 545 case x86_64::RequestGOTAndTransformToDelta32: 546 KindToSet = x86_64::Delta32; 547 break; 548 default: 549 return false; 550 } 551 assert(KindToSet != Edge::Invalid && 552 "Fell through switch, but no new kind to set"); 553 DEBUG_WITH_TYPE("jitlink", { 554 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " 555 << B->getFixupAddress(E) << " (" << B->getAddress() << " + " 556 << formatv("{0:x}", E.getOffset()) << ")\n"; 557 }); 558 E.setKind(KindToSet); 559 E.setTarget(getEntryForTarget(G, E.getTarget())); 560 return true; 561 } 562 563 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 564 return createAnonymousPointer(G, getGOTSection(G), &Target); 565 } 566 567 private: 568 Section &getGOTSection(LinkGraph &G) { 569 if (!GOTSection) 570 GOTSection = &G.createSection(getSectionName(), MemProt::Read); 571 return *GOTSection; 572 } 573 574 Section *GOTSection = nullptr; 575 }; 576 577 /// Procedure Linkage Table Builder. 578 class PLTTableManager : public TableManager<PLTTableManager> { 579 public: 580 PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {} 581 582 static StringRef getSectionName() { return "$__STUBS"; } 583 584 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 585 if (E.getKind() == x86_64::BranchPCRel32 && !E.getTarget().isDefined()) { 586 DEBUG_WITH_TYPE("jitlink", { 587 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " 588 << B->getFixupAddress(E) << " (" << B->getAddress() << " + " 589 << formatv("{0:x}", E.getOffset()) << ")\n"; 590 }); 591 // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to 592 // be optimized when the target is in-range. 593 E.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable); 594 E.setTarget(getEntryForTarget(G, E.getTarget())); 595 return true; 596 } 597 return false; 598 } 599 600 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 601 return createAnonymousPointerJumpStub(G, getStubsSection(G), 602 GOT.getEntryForTarget(G, Target)); 603 } 604 605 public: 606 Section &getStubsSection(LinkGraph &G) { 607 if (!PLTSection) 608 PLTSection = 609 &G.createSection(getSectionName(), MemProt::Read | MemProt::Exec); 610 return *PLTSection; 611 } 612 613 GOTTableManager &GOT; 614 Section *PLTSection = nullptr; 615 }; 616 617 /// Optimize the GOT and Stub relocations if the edge target address is in range 618 /// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range, 619 /// then replace GOT load with lea 620 /// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is 621 /// in range, replace a indirect jump by plt stub with a direct jump to the 622 /// target 623 Error optimizeGOTAndStubAccesses(LinkGraph &G); 624 625 } // namespace x86_64 626 } // end namespace jitlink 627 } // end namespace llvm 628 629 #endif // LLVM_EXECUTIONENGINE_JITLINK_X86_64_H 630