1 //===--- ppc64.h - Generic JITLink ppc64 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 64-bit PowerPC objects. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_JITLINK_PPC64_H 14 #define LLVM_EXECUTIONENGINE_JITLINK_PPC64_H 15 16 #include "llvm/ExecutionEngine/JITLink/JITLink.h" 17 #include "llvm/ExecutionEngine/JITLink/TableManager.h" 18 #include "llvm/Support/Endian.h" 19 20 namespace llvm::jitlink::ppc64 { 21 22 /// Represents ppc64 fixups and other ppc64-specific edge kinds. 23 enum EdgeKind_ppc64 : Edge::Kind { 24 Pointer64 = Edge::FirstRelocation, 25 Pointer32, 26 Delta64, 27 Delta32, 28 NegDelta32, 29 Delta16, 30 Delta16HA, 31 Delta16LO, 32 TOCDelta16HA, 33 TOCDelta16LO, 34 TOCDelta16DS, 35 TOCDelta16LODS, 36 CallBranchDelta, 37 // Need to restore r2 after the bl, suggesting the bl is followed by a nop. 38 CallBranchDeltaRestoreTOC, 39 // Need PLT call stub using TOC, TOC pointer is not saved before branching. 40 RequestPLTCallStub, 41 // Need PLT call stub using TOC, TOC pointer is saved before branching. 42 RequestPLTCallStubSaveTOC, 43 // Need PLT call stub without using TOC. 44 RequestPLTCallStubNoTOC, 45 }; 46 47 enum PLTCallStubKind { 48 LongBranch, 49 LongBranchSaveR2, 50 LongBranchNoTOC, 51 }; 52 53 extern const char NullPointerContent[8]; 54 extern const char PointerJumpStubContent_big[20]; 55 extern const char PointerJumpStubContent_little[20]; 56 extern const char PointerJumpStubNoTOCContent_big[32]; 57 extern const char PointerJumpStubNoTOCContent_little[32]; 58 59 struct PLTCallStubReloc { 60 Edge::Kind K; 61 size_t Offset; 62 Edge::AddendT A; 63 }; 64 65 struct PLTCallStubInfo { 66 ArrayRef<char> Content; 67 SmallVector<PLTCallStubReloc, 2> Relocs; 68 }; 69 70 template <support::endianness Endianness> 71 inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) { 72 constexpr bool isLE = Endianness == support::endianness::little; 73 switch (StubKind) { 74 case LongBranch: { 75 ArrayRef<char> Content = 76 isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big; 77 // Skip save r2. 78 Content = Content.slice(4); 79 return PLTCallStubInfo{ 80 Content, 81 {{TOCDelta16HA, 0, 0}, {TOCDelta16LO, 4, 0}}, 82 }; 83 } 84 case LongBranchSaveR2: { 85 ArrayRef<char> Content = 86 isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big; 87 return PLTCallStubInfo{ 88 Content, 89 {{TOCDelta16HA, 4, 0}, {TOCDelta16LO, 8, 0}}, 90 }; 91 } 92 case LongBranchNoTOC: { 93 ArrayRef<char> Content = isLE ? PointerJumpStubNoTOCContent_little 94 : PointerJumpStubNoTOCContent_big; 95 return PLTCallStubInfo{ 96 Content, 97 {{Delta16HA, 16, 8}, {Delta16LO, 20, 12}}, 98 }; 99 } 100 } 101 llvm_unreachable("Unknown PLTCallStubKind enum"); 102 } 103 104 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, 105 Symbol *InitialTarget = nullptr, 106 uint64_t InitialAddend = 0) { 107 assert(G.getPointerSize() == sizeof(NullPointerContent) && 108 "LinkGraph's pointer size should be consistent with size of " 109 "NullPointerContent"); 110 Block &B = G.createContentBlock(PointerSection, NullPointerContent, 111 orc::ExecutorAddr(), G.getPointerSize(), 0); 112 if (InitialTarget) 113 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend); 114 return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false); 115 } 116 117 template <support::endianness Endianness> 118 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, 119 Section &StubSection, 120 Symbol &PointerSymbol, 121 PLTCallStubKind StubKind) { 122 PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind); 123 Block &B = G.createContentBlock(StubSection, StubInfo.Content, 124 orc::ExecutorAddr(), 4, 0); 125 for (auto const &Reloc : StubInfo.Relocs) 126 B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A); 127 return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false); 128 } 129 130 template <support::endianness Endianness> 131 class TOCTableManager : public TableManager<TOCTableManager<Endianness>> { 132 public: 133 // FIXME: `llvm-jitlink -check` relies this name to be $__GOT. 134 static StringRef getSectionName() { return "$__GOT"; } 135 136 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 137 Edge::Kind K = E.getKind(); 138 switch (K) { 139 case TOCDelta16HA: 140 case TOCDelta16LO: 141 case TOCDelta16DS: 142 case TOCDelta16LODS: 143 case CallBranchDeltaRestoreTOC: 144 case RequestPLTCallStub: 145 case RequestPLTCallStubSaveTOC: 146 // Create TOC section if TOC relocation, PLT or GOT is used. 147 getOrCreateTOCSection(G); 148 return false; 149 default: 150 return false; 151 } 152 } 153 154 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 155 return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target); 156 } 157 158 private: 159 Section &getOrCreateTOCSection(LinkGraph &G) { 160 TOCSection = G.findSectionByName(getSectionName()); 161 if (!TOCSection) 162 TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read); 163 return *TOCSection; 164 } 165 166 Section *TOCSection = nullptr; 167 }; 168 169 template <support::endianness Endianness> 170 class PLTTableManager : public TableManager<PLTTableManager<Endianness>> { 171 public: 172 PLTTableManager(TOCTableManager<Endianness> &TOC) : TOC(TOC) {} 173 174 static StringRef getSectionName() { return "$__STUBS"; } 175 176 bool visitEdge(LinkGraph &G, Block *B, Edge &E) { 177 Edge::Kind K = E.getKind(); 178 if (K == ppc64::RequestPLTCallStubSaveTOC && E.getTarget().isExternal()) { 179 E.setKind(ppc64::CallBranchDeltaRestoreTOC); 180 this->StubKind = LongBranchSaveR2; 181 E.setTarget(this->getEntryForTarget(G, E.getTarget())); 182 return true; 183 } 184 if (K == ppc64::RequestPLTCallStubNoTOC && E.getTarget().isExternal()) { 185 E.setKind(ppc64::CallBranchDelta); 186 this->StubKind = LongBranchNoTOC; 187 E.setTarget(this->getEntryForTarget(G, E.getTarget())); 188 return true; 189 } 190 return false; 191 } 192 193 Symbol &createEntry(LinkGraph &G, Symbol &Target) { 194 return createAnonymousPointerJumpStub<Endianness>( 195 G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target), 196 this->StubKind); 197 } 198 199 private: 200 Section &getOrCreateStubsSection(LinkGraph &G) { 201 PLTSection = G.findSectionByName(getSectionName()); 202 if (!PLTSection) 203 PLTSection = &G.createSection(getSectionName(), 204 orc::MemProt::Read | orc::MemProt::Exec); 205 return *PLTSection; 206 } 207 208 TOCTableManager<Endianness> &TOC; 209 Section *PLTSection = nullptr; 210 PLTCallStubKind StubKind; 211 }; 212 213 /// Returns a string name for the given ppc64 edge. For debugging purposes 214 /// only. 215 const char *getEdgeKindName(Edge::Kind K); 216 217 inline static uint16_t ha16(uint64_t x) { return (x + 0x8000) >> 16; } 218 219 inline static uint16_t lo16(uint64_t x) { return x & 0xffff; } 220 221 /// Apply fixup expression for edge to block content. 222 template <support::endianness Endianness> 223 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, 224 const Symbol *TOCSymbol) { 225 char *BlockWorkingMem = B.getAlreadyMutableContent().data(); 226 char *FixupPtr = BlockWorkingMem + E.getOffset(); 227 orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset(); 228 int64_t S = E.getTarget().getAddress().getValue(); 229 int64_t A = E.getAddend(); 230 int64_t P = FixupAddress.getValue(); 231 int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0; 232 Edge::Kind K = E.getKind(); 233 234 DEBUG_WITH_TYPE("jitlink", { 235 dbgs() << " Applying fixup on " << G.getEdgeKindName(K) 236 << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", " 237 << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", " 238 << formatv("{0:x}", TOCBase) << ")\n"; 239 }); 240 241 switch (K) { 242 case Pointer64: { 243 uint64_t Value = S + A; 244 support::endian::write64<Endianness>(FixupPtr, Value); 245 break; 246 } 247 case Delta16HA: 248 case Delta16LO: { 249 int64_t Value = S + A - P; 250 if (LLVM_UNLIKELY(!isInt<32>(Value))) { 251 return makeTargetOutOfRangeError(G, B, E); 252 } 253 if (K == Delta16LO) 254 support::endian::write16<Endianness>(FixupPtr, lo16(Value)); 255 else 256 support::endian::write16<Endianness>(FixupPtr, ha16(Value)); 257 break; 258 } 259 case TOCDelta16HA: 260 case TOCDelta16LO: { 261 int64_t Value = S + A - TOCBase; 262 if (LLVM_UNLIKELY(!isInt<32>(Value))) { 263 return makeTargetOutOfRangeError(G, B, E); 264 } 265 if (K == TOCDelta16LO) 266 support::endian::write16<Endianness>(FixupPtr, lo16(Value)); 267 else 268 support::endian::write16<Endianness>(FixupPtr, ha16(Value)); 269 break; 270 } 271 case TOCDelta16DS: 272 case TOCDelta16LODS: { 273 int64_t Value = S + A - TOCBase; 274 if (LLVM_UNLIKELY(!isInt<32>(Value))) { 275 return makeTargetOutOfRangeError(G, B, E); 276 } 277 if (K == TOCDelta16LODS) 278 support::endian::write16<Endianness>(FixupPtr, lo16(Value) & ~3); 279 else 280 support::endian::write16<Endianness>(FixupPtr, Value & ~3); 281 break; 282 } 283 case CallBranchDeltaRestoreTOC: 284 case CallBranchDelta: { 285 int64_t Value = S + A - P; 286 if (LLVM_UNLIKELY(!isInt<26>(Value))) { 287 return makeTargetOutOfRangeError(G, B, E); 288 } 289 uint32_t Inst = support::endian::read32<Endianness>(FixupPtr); 290 support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) | 291 (Value & 0x03fffffc)); 292 if (K == CallBranchDeltaRestoreTOC) { 293 uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4); 294 assert(NopInst == 0x60000000 && 295 "NOP should be placed here for restoring r2"); 296 (void)NopInst; 297 // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`. 298 support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018); 299 } 300 break; 301 } 302 case Delta64: { 303 int64_t Value = S + A - P; 304 support::endian::write64<Endianness>(FixupPtr, Value); 305 break; 306 } 307 case Delta32: { 308 int64_t Value = S + A - P; 309 if (LLVM_UNLIKELY(!isInt<32>(Value))) { 310 return makeTargetOutOfRangeError(G, B, E); 311 } 312 support::endian::write32<Endianness>(FixupPtr, Value); 313 break; 314 } 315 case NegDelta32: { 316 int64_t Value = P - S + A; 317 if (LLVM_UNLIKELY(!isInt<32>(Value))) { 318 return makeTargetOutOfRangeError(G, B, E); 319 } 320 support::endian::write32<Endianness>(FixupPtr, Value); 321 break; 322 } 323 default: 324 return make_error<JITLinkError>( 325 "In graph " + G.getName() + ", section " + B.getSection().getName() + 326 " unsupported edge kind " + getEdgeKindName(E.getKind())); 327 } 328 return Error::success(); 329 } 330 331 } // end namespace llvm::jitlink::ppc64 332 333 #endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H 334