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