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