1 //=== i386.h - Generic JITLink i386 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 i386 objects. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_JITLINK_I386_H 14 #define LLVM_EXECUTIONENGINE_JITLINK_I386_H 15 16 #include "llvm/ExecutionEngine/JITLink/JITLink.h" 17 #include "llvm/ExecutionEngine/JITLink/TableManager.h" 18 19 namespace llvm::jitlink::i386 { 20 /// Represets i386 fixups 21 enum EdgeKind_i386 : Edge::Kind { 22 23 /// None 24 None = Edge::FirstRelocation, 25 26 /// A plain 32-bit pointer value relocation. 27 /// 28 /// Fixup expression: 29 /// Fixup <- Target + Addend : uint32 30 /// 31 /// Errors: 32 /// - The target must reside in the low 32-bits of the address space, 33 /// otherwise an out-of-range error will be returned. 34 /// 35 Pointer32, 36 37 /// A 32-bit PC-relative relocation. 38 /// 39 /// Represents a data/control flow instruction using PC-relative addressing 40 /// to a target. 41 /// 42 /// The fixup expression for this kind includes an implicit offset to account 43 /// for the PC (unlike the Delta edges) so that a PCRel32 with a target 44 /// T and addend zero is a call/branch to the start (offset zero) of T. 45 /// 46 /// Fixup expression: 47 /// Fixup <- Target - (Fixup + 4) + Addend : int32 48 /// 49 /// Errors: 50 /// - The result of the fixup expression must fit into an int32, otherwise 51 /// an out-of-range error will be returned. 52 /// 53 PCRel32, 54 55 /// A plain 16-bit pointer value relocation. 56 /// 57 /// Fixup expression: 58 /// Fixup <- Target + Addend : uint16 59 /// 60 /// Errors: 61 /// - The target must reside in the low 16-bits of the address space, 62 /// otherwise an out-of-range error will be returned. 63 /// 64 Pointer16, 65 66 /// A 16-bit PC-relative relocation. 67 /// 68 /// Represents a data/control flow instruction using PC-relative addressing 69 /// to a target. 70 /// 71 /// The fixup expression for this kind includes an implicit offset to account 72 /// for the PC (unlike the Delta edges) so that a PCRel16 with a target 73 /// T and addend zero is a call/branch to the start (offset zero) of T. 74 /// 75 /// Fixup expression: 76 /// Fixup <- Target - (Fixup + 4) + Addend : int16 77 /// 78 /// Errors: 79 /// - The result of the fixup expression must fit into an int16, otherwise 80 /// an out-of-range error will be returned. 81 /// 82 PCRel16, 83 84 /// A 32-bit delta. 85 /// 86 /// Delta from the fixup to the target. 87 /// 88 /// Fixup expression: 89 /// Fixup <- Target - Fixup + Addend : int64 90 /// 91 /// Errors: 92 /// - The result of the fixup expression must fit into an int32, otherwise 93 /// an out-of-range error will be returned. 94 Delta32, 95 96 /// A 32-bit GOT delta. 97 /// 98 /// Delta from the global offset table to the target. 99 /// 100 /// Fixup expression: 101 /// Fixup <- Target - GOTSymbol + Addend : int32 102 /// 103 /// Errors: 104 /// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section 105 /// symbol was not been defined. 106 Delta32FromGOT, 107 108 /// A GOT entry offset within GOT getter/constructor, transformed to 109 /// Delta32FromGOT pointing at the GOT entry for the original target. 110 /// 111 /// Indicates that this edge should be transformed into a Delta32FromGOT 112 /// targeting the GOT entry for the edge's current target, maintaining the 113 /// same addend. 114 /// A GOT entry for the target should be created if one does not already 115 /// exist. 116 /// 117 /// Edges of this kind are usually handled by a GOT builder pass inserted by 118 /// default 119 /// 120 /// Fixup expression: 121 /// NONE 122 /// 123 /// Errors: 124 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup 125 /// phase will result in an assert/unreachable during the fixup phase 126 RequestGOTAndTransformToDelta32FromGOT, 127 128 /// A 32-bit PC-relative branch. 129 /// 130 /// Represents a PC-relative call or branch to a target. This can be used to 131 /// identify, record, and/or patch call sites. 132 /// 133 /// The fixup expression for this kind includes an implicit offset to account 134 /// for the PC (unlike the Delta edges) so that a Branch32PCRel with a target 135 /// T and addend zero is a call/branch to the start (offset zero) of T. 136 /// 137 /// Fixup expression: 138 /// Fixup <- Target - (Fixup + 4) + Addend : int32 139 /// 140 /// Errors: 141 /// - The result of the fixup expression must fit into an int32, otherwise 142 /// an out-of-range error will be returned. 143 /// 144 BranchPCRel32, 145 146 /// A 32-bit PC-relative branch to a pointer jump stub. 147 /// 148 /// The target of this relocation should be a pointer jump stub of the form: 149 /// 150 /// \code{.s} 151 /// .text 152 /// jmp *tgtptr 153 /// ; ... 154 /// 155 /// .data 156 /// tgtptr: 157 /// .quad 0 158 /// \endcode 159 /// 160 /// This edge kind has the same fixup expression as BranchPCRel32, but further 161 /// identifies the call/branch as being to a pointer jump stub. For edges of 162 /// this kind the jump stub should not be bypassed (use 163 /// BranchPCRel32ToPtrJumpStubBypassable for that), but the pointer location 164 /// target may be recorded to allow manipulation at runtime. 165 /// 166 /// Fixup expression: 167 /// Fixup <- Target - Fixup + Addend - 4 : int32 168 /// 169 /// Errors: 170 /// - The result of the fixup expression must fit into an int32, otherwise 171 /// an out-of-range error will be returned. 172 /// 173 BranchPCRel32ToPtrJumpStub, 174 175 /// A relaxable version of BranchPCRel32ToPtrJumpStub. 176 /// 177 /// The edge kind has the same fixup expression as BranchPCRel32ToPtrJumpStub, 178 /// but identifies the call/branch as being to a pointer jump stub that may be 179 /// bypassed with a direct jump to the ultimate target if the ultimate target 180 /// is within range of the fixup location. 181 /// 182 /// Fixup expression: 183 /// Fixup <- Target - Fixup + Addend - 4: int32 184 /// 185 /// Errors: 186 /// - The result of the fixup expression must fit into an int32, otherwise 187 /// an out-of-range error will be returned. 188 /// 189 BranchPCRel32ToPtrJumpStubBypassable, 190 }; 191 192 /// Returns a string name for the given i386 edge. For debugging purposes 193 /// only 194 const char *getEdgeKindName(Edge::Kind K); 195 196 /// Apply fixup expression for edge to block content. 197 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, 198 const Symbol *GOTSymbol) { 199 using namespace i386; 200 using namespace llvm::support; 201 202 char *BlockWorkingMem = B.getAlreadyMutableContent().data(); 203 char *FixupPtr = BlockWorkingMem + E.getOffset(); 204 auto FixupAddress = B.getAddress() + E.getOffset(); 205 206 switch (E.getKind()) { 207 case i386::None: { 208 break; 209 } 210 211 case i386::Pointer32: { 212 uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 213 *(ulittle32_t *)FixupPtr = Value; 214 break; 215 } 216 217 case i386::PCRel32: { 218 int32_t Value = 219 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 220 *(little32_t *)FixupPtr = Value; 221 break; 222 } 223 224 case i386::Pointer16: { 225 uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 226 if (LLVM_LIKELY(isUInt<16>(Value))) 227 *(ulittle16_t *)FixupPtr = Value; 228 else 229 return makeTargetOutOfRangeError(G, B, E); 230 break; 231 } 232 233 case i386::PCRel16: { 234 int32_t Value = 235 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 236 if (LLVM_LIKELY(isInt<16>(Value))) 237 *(little16_t *)FixupPtr = Value; 238 else 239 return makeTargetOutOfRangeError(G, B, E); 240 break; 241 } 242 243 case i386::Delta32: { 244 int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 245 *(little32_t *)FixupPtr = Value; 246 break; 247 } 248 249 case i386::Delta32FromGOT: { 250 assert(GOTSymbol && "No GOT section symbol"); 251 int32_t Value = 252 E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend(); 253 *(little32_t *)FixupPtr = Value; 254 break; 255 } 256 257 case i386::BranchPCRel32: 258 case i386::BranchPCRel32ToPtrJumpStub: 259 case i386::BranchPCRel32ToPtrJumpStubBypassable: { 260 int32_t Value = 261 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 262 *(little32_t *)FixupPtr = Value; 263 break; 264 } 265 266 default: 267 return make_error<JITLinkError>( 268 "In graph " + G.getName() + ", section " + B.getSection().getName() + 269 " unsupported edge kind " + getEdgeKindName(E.getKind())); 270 } 271 272 return Error::success(); 273 } 274 275 /// i386 pointer size. 276 constexpr uint32_t PointerSize = 4; 277 278 /// i386 null pointer content. 279 extern const char NullPointerContent[PointerSize]; 280 281 /// i386 pointer jump stub content. 282 /// 283 /// Contains the instruction sequence for an indirect jump via an in-memory 284 /// pointer: 285 /// jmpq *ptr 286 extern const char PointerJumpStubContent[6]; 287 288 /// Creates a new pointer block in the given section and returns an anonymous 289 /// symbol pointing to it. 290 /// 291 /// If InitialTarget is given then an Pointer32 relocation will be added to the 292 /// block pointing at InitialTarget. 293 /// 294 /// The pointer block will have the following default values: 295 /// alignment: 32-bit 296 /// alignment-offset: 0 297 /// address: highest allowable (~7U) 298 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, 299 Symbol *InitialTarget = nullptr, 300 uint64_t InitialAddend = 0) { 301 auto &B = G.createContentBlock(PointerSection, NullPointerContent, 302 orc::ExecutorAddr(), 8, 0); 303 if (InitialTarget) 304 B.addEdge(Pointer32, 0, *InitialTarget, InitialAddend); 305 return G.addAnonymousSymbol(B, 0, PointerSize, false, false); 306 } 307 308 /// Create a jump stub block that jumps via the pointer at the given symbol. 309 /// 310 /// The stub block will have the following default values: 311 /// alignment: 8-bit 312 /// alignment-offset: 0 313 /// address: highest allowable: (~5U) 314 inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection, 315 Symbol &PointerSymbol) { 316 auto &B = G.createContentBlock(StubSection, PointerJumpStubContent, 317 orc::ExecutorAddr(), 8, 0); 318 B.addEdge(Pointer32, 319 // Offset is 2 because the the first 2 bytes of the 320 // jump stub block are {0xff, 0x25} -- an indirect absolute 321 // jump. 322 2, PointerSymbol, 0); 323 return B; 324 } 325 326 /// Create a jump stub that jumps via the pointer at the given symbol and 327 /// an anonymous symbol pointing to it. Return the anonymous symbol. 328 /// 329 /// The stub block will be created by createPointerJumpStubBlock. 330 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, 331 Section &StubSection, 332 Symbol &PointerSymbol) { 333 return G.addAnonymousSymbol( 334 createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true, 335 false); 336 } 337 338 /// Global Offset Table Builder. 339 class GOTTableManager : public TableManager<GOTTableManager> { 340 public: 341 static StringRef getSectionName() { return "$__GOT"; } 342 343 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 344 Edge::Kind KindToSet = Edge::Invalid; 345 switch (E.getKind()) { 346 case i386::Delta32FromGOT: { 347 // we need to make sure that the GOT section exists, but don't otherwise 348 // need to fix up this edge 349 getGOTSection(G); 350 return false; 351 } 352 case i386::RequestGOTAndTransformToDelta32FromGOT: 353 KindToSet = i386::Delta32FromGOT; 354 break; 355 default: 356 return false; 357 } 358 assert(KindToSet != Edge::Invalid && 359 "Fell through switch, but no new kind to set"); 360 DEBUG_WITH_TYPE("jitlink", { 361 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " 362 << B->getFixupAddress(E) << " (" << B->getAddress() << " + " 363 << formatv("{0:x}", E.getOffset()) << ")\n"; 364 }); 365 E.setKind(KindToSet); 366 E.setTarget(getEntryForTarget(G, E.getTarget())); 367 return true; 368 } 369 370 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 371 return createAnonymousPointer(G, getGOTSection(G), &Target); 372 } 373 374 private: 375 Section &getGOTSection(LinkGraph &G) { 376 if (!GOTSection) 377 GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read); 378 return *GOTSection; 379 } 380 381 Section *GOTSection = nullptr; 382 }; 383 384 /// Procedure Linkage Table Builder. 385 class PLTTableManager : public TableManager<PLTTableManager> { 386 public: 387 PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {} 388 389 static StringRef getSectionName() { return "$__STUBS"; } 390 391 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 392 if (E.getKind() == i386::BranchPCRel32 && !E.getTarget().isDefined()) { 393 DEBUG_WITH_TYPE("jitlink", { 394 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " 395 << B->getFixupAddress(E) << " (" << B->getAddress() << " + " 396 << formatv("{0:x}", E.getOffset()) << ")\n"; 397 }); 398 // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to 399 // be optimized when the target is in-range. 400 E.setKind(i386::BranchPCRel32ToPtrJumpStubBypassable); 401 E.setTarget(getEntryForTarget(G, E.getTarget())); 402 return true; 403 } 404 return false; 405 } 406 407 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 408 return createAnonymousPointerJumpStub(G, getStubsSection(G), 409 GOT.getEntryForTarget(G, Target)); 410 } 411 412 public: 413 Section &getStubsSection(LinkGraph &G) { 414 if (!PLTSection) 415 PLTSection = &G.createSection(getSectionName(), 416 orc::MemProt::Read | orc::MemProt::Exec); 417 return *PLTSection; 418 } 419 420 GOTTableManager &GOT; 421 Section *PLTSection = nullptr; 422 }; 423 424 /// Optimize the GOT and Stub relocations if the edge target address is in range 425 /// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range, 426 /// then replace GOT load with lea. (THIS IS UNIMPLEMENTED RIGHT NOW!) 427 /// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is 428 /// in range, replace a indirect jump by plt stub with a direct jump to the 429 /// target 430 Error optimizeGOTAndStubAccesses(LinkGraph &G); 431 432 } // namespace llvm::jitlink::i386 433 434 #endif // LLVM_EXECUTIONENGINE_JITLINK_I386_H 435