Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 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 |