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 129 /// Returns a string name for the given i386 edge. For debugging purposes 130 /// only 131 const char *getEdgeKindName(Edge::Kind K); 132 133 /// Returns true if the given uint32_t value is in range for a uint16_t. 134 inline bool isInRangeForImmU16(uint32_t Value) { 135 return Value <= std::numeric_limits<uint16_t>::max(); 136 } 137 138 /// Returns true if the given int32_t value is in range for an int16_t. 139 inline bool isInRangeForImmS16(int32_t Value) { 140 return (Value >= std::numeric_limits<int16_t>::min() && 141 Value <= std::numeric_limits<int16_t>::max()); 142 } 143 144 /// Apply fixup expression for edge to block content. 145 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, 146 const Symbol *GOTSymbol) { 147 using namespace i386; 148 using namespace llvm::support; 149 150 char *BlockWorkingMem = B.getAlreadyMutableContent().data(); 151 char *FixupPtr = BlockWorkingMem + E.getOffset(); 152 auto FixupAddress = B.getAddress() + E.getOffset(); 153 154 switch (E.getKind()) { 155 case i386::None: { 156 break; 157 } 158 159 case i386::Pointer32: { 160 uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 161 *(ulittle32_t *)FixupPtr = Value; 162 break; 163 } 164 165 case i386::PCRel32: { 166 int32_t Value = 167 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 168 *(little32_t *)FixupPtr = Value; 169 break; 170 } 171 172 case i386::Pointer16: { 173 uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); 174 if (LLVM_LIKELY(isInRangeForImmU16(Value))) 175 *(ulittle16_t *)FixupPtr = Value; 176 else 177 return makeTargetOutOfRangeError(G, B, E); 178 break; 179 } 180 181 case i386::PCRel16: { 182 int32_t Value = 183 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); 184 if (LLVM_LIKELY(isInRangeForImmS16(Value))) 185 *(little16_t *)FixupPtr = Value; 186 else 187 return makeTargetOutOfRangeError(G, B, E); 188 break; 189 } 190 191 case i386::Delta32: { 192 int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); 193 *(little32_t *)FixupPtr = Value; 194 break; 195 } 196 197 case i386::Delta32FromGOT: { 198 assert(GOTSymbol && "No GOT section symbol"); 199 int32_t Value = 200 E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend(); 201 *(little32_t *)FixupPtr = Value; 202 break; 203 } 204 205 default: 206 return make_error<JITLinkError>( 207 "In graph " + G.getName() + ", section " + B.getSection().getName() + 208 "unsupported edge kind" + getEdgeKindName(E.getKind())); 209 } 210 211 return Error::success(); 212 } 213 214 /// i386 pointer size. 215 constexpr uint32_t PointerSize = 4; 216 217 /// i386 null pointer content. 218 extern const char NullPointerContent[PointerSize]; 219 220 /// Creates a new pointer block in the given section and returns an anonymous 221 /// symbol pointing to it. 222 /// 223 /// If InitialTarget is given then an Pointer32 relocation will be added to the 224 /// block pointing at InitialTarget. 225 /// 226 /// The pointer block will have the following default values: 227 /// alignment: 32-bit 228 /// alignment-offset: 0 229 /// address: highest allowable (~7U) 230 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, 231 Symbol *InitialTarget = nullptr, 232 uint64_t InitialAddend = 0) { 233 auto &B = G.createContentBlock(PointerSection, NullPointerContent, 234 orc::ExecutorAddr(), 8, 0); 235 if (InitialTarget) 236 B.addEdge(Pointer32, 0, *InitialTarget, InitialAddend); 237 return G.addAnonymousSymbol(B, 0, PointerSize, false, false); 238 } 239 240 /// Global Offset Table Builder. 241 class GOTTableManager : public TableManager<GOTTableManager> { 242 public: 243 static StringRef getSectionName() { return "$__GOT"; } 244 245 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 246 Edge::Kind KindToSet = Edge::Invalid; 247 switch (E.getKind()) { 248 case i386::Delta32FromGOT: { 249 // we need to make sure that the GOT section exists, but don't otherwise 250 // need to fix up this edge 251 getGOTSection(G); 252 return false; 253 } 254 case i386::RequestGOTAndTransformToDelta32FromGOT: 255 KindToSet = i386::Delta32FromGOT; 256 break; 257 default: 258 return false; 259 } 260 assert(KindToSet != Edge::Invalid && 261 "Fell through switch, but no new kind to set"); 262 DEBUG_WITH_TYPE("jitlink", { 263 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " 264 << B->getFixupAddress(E) << " (" << B->getAddress() << " + " 265 << formatv("{0:x}", E.getOffset()) << ")\n"; 266 }); 267 E.setKind(KindToSet); 268 E.setTarget(getEntryForTarget(G, E.getTarget())); 269 return true; 270 } 271 272 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 273 return createAnonymousPointer(G, getGOTSection(G), &Target); 274 } 275 276 private: 277 Section &getGOTSection(LinkGraph &G) { 278 if (!GOTSection) 279 GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read); 280 return *GOTSection; 281 } 282 283 Section *GOTSection = nullptr; 284 }; 285 286 } // namespace llvm::jitlink::i386 287 288 #endif // LLVM_EXECUTIONENGINE_JITLINK_I386_H 289