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 18 #include <limits> 19 20 namespace llvm { 21 namespace jitlink { 22 namespace x86_64 { 23 24 /// Represents x86-64 fixups and other x86-64-specific edge kinds. 25 enum EdgeKind_x86_64 : Edge::Kind { 26 27 /// A plain 64-bit pointer value relocation. 28 /// 29 /// Fixup expression: 30 /// Fixup <- Target + Addend : uint64 31 /// 32 Pointer64 = Edge::FirstRelocation, 33 34 /// A plain 32-bit pointer value relocation. 35 /// 36 /// Fixup expression: 37 /// Fixup <- Target + Addend : uint32 38 /// 39 /// Errors: 40 /// - The target must reside in the low 32-bits of the address space, 41 /// otherwise an out-of-range error will be returned. 42 /// 43 Pointer32, 44 45 /// A 64-bit delta. 46 /// 47 /// Delta from the fixup to the target. 48 /// 49 /// Fixup expression: 50 /// Fixup <- Target - Fixup + Addend : int64 51 /// 52 Delta64, 53 54 /// A 32-bit delta. 55 /// 56 /// Delta from the fixup to the target. 57 /// 58 /// Fixup expression: 59 /// Fixup <- Target - Fixup + Addend : int64 60 /// 61 /// Errors: 62 /// - The result of the fixup expression must fit into an int32, otherwise 63 /// an out-of-range error will be returned. 64 /// 65 Delta32, 66 67 /// A 64-bit negative delta. 68 /// 69 /// Delta from target back to the fixup. 70 /// 71 /// Fixup expression: 72 /// Fixup <- Fixup - Target + Addend : int64 73 /// 74 NegDelta64, 75 76 /// A 32-bit negative delta. 77 /// 78 /// Delta from the target back to the fixup. 79 /// 80 /// Fixup expression: 81 /// Fixup <- Fixup - Target + Addend : int32 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 NegDelta32, 87 88 /// A 32-bit PC-relative branch. 89 /// 90 /// Represents a PC-relative call or branch to a target. This can be used to 91 /// identify, record, and/or patch call sites. 92 /// 93 /// The fixup expression for this kind includes an implicit offset to account 94 /// for the PC (unlike the Delta edges) so that a Branch32PCRel with a target 95 /// T and addend zero is a call/branch to the start (offset zero) of T. 96 /// 97 /// Fixup expression: 98 /// Fixup <- Target - (Fixup + 4) + Addend : int32 99 /// 100 /// Errors: 101 /// - The result of the fixup expression must fit into an int32, otherwise 102 /// an out-of-range error will be returned. 103 /// 104 BranchPCRel32, 105 106 /// A 32-bit PC-relative branch to a pointer jump stub. 107 /// 108 /// The target of this relocation should be a pointer jump stub of the form: 109 /// 110 /// \code{.s} 111 /// .text 112 /// jmpq *tgtptr(%rip) 113 /// ; ... 114 /// 115 /// .data 116 /// tgtptr: 117 /// .quad 0 118 /// \endcode 119 /// 120 /// This edge kind has the same fixup expression as BranchPCRel32, but further 121 /// identifies the call/branch as being to a pointer jump stub. For edges of 122 /// this kind the jump stub should not be bypassed (use 123 /// BranchPCRel32ToPtrJumpStubRelaxable for that), but the pointer location 124 /// target may be recorded to allow manipulation at runtime. 125 /// 126 /// Fixup expression: 127 /// Fixup <- Target - Fixup + Addend - 4 : int32 128 /// 129 /// Errors: 130 /// - The result of the fixup expression must fit into an int32, otherwise 131 /// an out-of-range error will be returned. 132 /// 133 BranchPCRel32ToPtrJumpStub, 134 135 /// A relaxable version of BranchPCRel32ToPtrJumpStub. 136 /// 137 /// The edge kind has the same fixup expression as BranchPCRel32ToPtrJumpStub, 138 /// but identifies the call/branch as being to a pointer jump stub that may be 139 /// bypassed if the ultimate target is within range of the fixup location. 140 /// 141 /// Fixup expression: 142 /// Fixup <- Target - Fixup + Addend - 4: int32 143 /// 144 /// Errors: 145 /// - The result of the fixup expression must fit into an int32, otherwise 146 /// an out-of-range error will be returned. 147 /// 148 BranchPCRel32ToPtrJumpStubRelaxable, 149 150 /// A GOT entry getter/constructor, transformed to Delta32 pointing at the GOT 151 /// entry for the original target. 152 /// 153 /// Indicates that this edge should be transformed into a Delta32 targeting 154 /// the GOT entry for the edge's current target, maintaining the same addend. 155 /// A GOT entry for the target should be created if one does not already 156 /// exist. 157 /// 158 /// Edges of this kind are usually handled by a GOT builder pass inserted by 159 /// default. 160 /// 161 /// Fixup expression: 162 /// NONE 163 /// 164 /// Errors: 165 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 166 /// phase will result in an assert/unreachable during the fixup phase. 167 /// 168 RequestGOTAndTransformToDelta32, 169 170 /// A PC-relative reference to a GOT entry, relaxable if GOT entry target 171 /// is in-range of the fixup. 172 /// 173 /// If the GOT entry target is in-range of the fixup then the load from the 174 /// GOT may be replaced with a direct memory address calculation. 175 /// 176 /// Fixup expression: 177 /// Fixup <- Target - (Fixup + 4) + Addend : int32 178 /// 179 /// Errors: 180 /// - The result of the fixup expression must fit into an int32, otherwise 181 /// an out-of-range error will be returned. 182 /// 183 PCRel32GOTLoadRelaxable, 184 185 /// A GOT entry getter/constructor, transformed to PCRel32ToGOTLoadRelaxable 186 /// pointing at the GOT entry for the original target. 187 /// 188 /// Indicates that this edge should be transformed into a 189 /// PC32ToGOTLoadRelaxable targeting the GOT entry for the edge's current 190 /// target, maintaining the same addend. A GOT entry for the target should be 191 /// created if one does not already exist. 192 /// 193 /// Edges of this kind are usually handled by a GOT builder pass inserted by 194 /// default. 195 /// 196 /// Fixup expression: 197 /// NONE 198 /// 199 /// Errors: 200 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 201 /// phase will result in an assert/unreachable during the fixup phase. 202 /// 203 RequestGOTAndTransformToPCRel32GOTLoadRelaxable, 204 205 /// A PC-relative reference to a Thread Local Variable Pointer (TLVP) entry, 206 /// relaxable if the TLVP entry target is in-range of the fixup. 207 /// 208 /// If the TLVP entry target is in-range of the fixup then the load frmo the 209 /// TLVP may be replaced with a direct memory address calculation. 210 /// 211 /// The target of this edge must be a thread local variable entry of the form 212 /// .quad <tlv getter thunk> 213 /// .quad <tlv key> 214 /// .quad <tlv initializer> 215 /// 216 /// Fixup expression: 217 /// Fixup <- Target - (Fixup + 4) + Addend : int32 218 /// 219 /// Errors: 220 /// - The result of the fixup expression must fit into an int32, otherwise 221 /// an out-of-range error will be returned. 222 /// - The target must be either external, or a TLV entry of the required 223 /// form, otherwise a malformed TLV entry error will be returned. 224 /// 225 PCRel32TLVPLoadRelaxable, 226 227 /// A TLVP entry getter/constructor, transformed to 228 /// Delta32ToTLVPLoadRelaxable. 229 /// 230 /// Indicates that this edge should be transformed into a 231 /// Delta32ToTLVPLoadRelaxable targeting the TLVP entry for the edge's current 232 /// target. A TLVP entry for the target should be created if one does not 233 /// already exist. 234 /// 235 /// Fixup expression: 236 /// NONE 237 /// 238 /// Errors: 239 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 240 /// phase will result in an assert/unreachable during the fixup phase. 241 /// 242 RequestTLVPAndTransformToPCRel32TLVPLoadRelaxable 243 }; 244 245 /// Returns a string name for the given x86-64 edge. For debugging purposes 246 /// only. 247 const char *getEdgeKindName(Edge::Kind K); 248 249 /// Returns true if the given uint64_t value is in range for a uint32_t. 250 inline bool isInRangeForImmU32(uint64_t Value) { 251 return Value <= std::numeric_limits<uint32_t>::max(); 252 } 253 254 /// Returns true if the given int64_t value is in range for an int32_t. 255 inline bool isInRangeForImmS32(int64_t Value) { 256 return (Value >= std::numeric_limits<int32_t>::min() && 257 Value <= std::numeric_limits<int32_t>::max()); 258 } 259 260 /// Apply fixup expression for edge to block content. 261 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) { 262 using namespace support; 263 264 char *BlockWorkingMem = B.getAlreadyMutableContent().data(); 265 char *FixupPtr = BlockWorkingMem + E.getOffset(); 266 JITTargetAddress FixupAddress = B.getAddress() + E.getOffset(); 267 268 switch (E.getKind()) { 269 270 case Pointer64: { 271 uint64_t Value = E.getTarget().getAddress() + E.getAddend(); 272 *(ulittle64_t *)FixupPtr = Value; 273 break; 274 } 275 276 case Pointer32: { 277 uint64_t Value = E.getTarget().getAddress() + E.getAddend(); 278 if (LLVM_LIKELY(isInRangeForImmU32(Value))) 279 *(ulittle32_t *)FixupPtr = Value; 280 else 281 return makeTargetOutOfRangeError(G, B, E); 282 break; 283 } 284 285 case BranchPCRel32: 286 case BranchPCRel32ToPtrJumpStub: 287 case BranchPCRel32ToPtrJumpStubRelaxable: 288 case PCRel32GOTLoadRelaxable: 289 case PCRel32TLVPLoadRelaxable: { 290 int64_t Value = 291 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 292 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 293 *(little32_t *)FixupPtr = Value; 294 else 295 return makeTargetOutOfRangeError(G, B, E); 296 break; 297 } 298 299 case Delta64: { 300 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 301 *(little64_t *)FixupPtr = Value; 302 break; 303 } 304 305 case Delta32: { 306 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 307 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 308 *(little32_t *)FixupPtr = Value; 309 else 310 return makeTargetOutOfRangeError(G, B, E); 311 break; 312 } 313 314 case NegDelta64: { 315 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend(); 316 *(little64_t *)FixupPtr = Value; 317 break; 318 } 319 320 case NegDelta32: { 321 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend(); 322 if (LLVM_LIKELY(isInRangeForImmS32(Value))) 323 *(little32_t *)FixupPtr = Value; 324 else 325 return makeTargetOutOfRangeError(G, B, E); 326 break; 327 } 328 329 default: { 330 // If you hit this you should check that *constructor and other non-fixup 331 // edges have been removed prior to applying fixups. 332 llvm_unreachable("Graph contains edge kind with no fixup expression"); 333 } 334 } 335 336 return Error::success(); 337 } 338 339 /// x86_64 pointer size. 340 constexpr uint64_t PointerSize = 8; 341 342 /// x86-64 null pointer content. 343 extern const char NullPointerContent[PointerSize]; 344 345 /// x86-64 pointer jump stub content. 346 /// 347 /// Contains the instruction sequence for an indirect jump via an in-memory 348 /// pointer: 349 /// jmpq *ptr(%rip) 350 extern const char PointerJumpStubContent[6]; 351 352 /// Creates a new pointer block in the given section and returns an anonymous 353 /// symbol pointing to it. 354 /// 355 /// If InitialTarget is given then an Pointer64 relocation will be added to the 356 /// block pointing at InitialTarget. 357 /// 358 /// The pointer block will have the following default values: 359 /// alignment: 64-bit 360 /// alignment-offset: 0 361 /// address: highest allowable (~7U) 362 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, 363 Symbol *InitialTarget = nullptr, 364 uint64_t InitialAddend = 0) { 365 auto &B = 366 G.createContentBlock(PointerSection, NullPointerContent, ~7ULL, 8, 0); 367 if (InitialTarget) 368 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend); 369 return G.addAnonymousSymbol(B, 0, 8, false, false); 370 } 371 372 /// Create a jump stub block that jumps via the pointer at the given symbol. 373 /// 374 /// The stub block will have the following default values: 375 /// alignment: 8-bit 376 /// alignment-offset: 0 377 /// address: highest allowable: (~5U) 378 inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection, 379 Symbol &PointerSymbol) { 380 auto &B = 381 G.createContentBlock(StubSection, PointerJumpStubContent, ~5ULL, 1, 0); 382 B.addEdge(Delta32, 2, PointerSymbol, -4); 383 return B; 384 } 385 386 /// Create a jump stub that jumps via the pointer at the given symbol and 387 /// an anonymous symbol pointing to it. Return the anonymous symbol. 388 /// 389 /// The stub block will be created by createPointerJumpStubBlock. 390 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, 391 Section &StubSection, 392 Symbol &PointerSymbol) { 393 return G.addAnonymousSymbol( 394 createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true, 395 false); 396 } 397 398 } // namespace x86_64 399 } // end namespace jitlink 400 } // end namespace llvm 401 402 #endif // LLVM_EXECUTIONENGINE_JITLINK_X86_64_H 403