1 //===----- COFF_x86_64.cpp - JIT linker implementation for COFF/x86_64 ----===//
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 // COFF/x86_64 jit-link implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/ExecutionEngine/JITLink/COFF_x86_64.h"
14 #include "COFFLinkGraphBuilder.h"
15 #include "JITLinkGeneric.h"
16 #include "SEHFrameSupport.h"
17 #include "llvm/BinaryFormat/COFF.h"
18 #include "llvm/ExecutionEngine/JITLink/x86_64.h"
19 #include "llvm/Object/COFF.h"
20 #include "llvm/Support/Endian.h"
21 
22 #define DEBUG_TYPE "jitlink"
23 
24 using namespace llvm;
25 using namespace llvm::jitlink;
26 
27 namespace {
28 
29 enum EdgeKind_coff_x86_64 : Edge::Kind {
30   PCRel32 = x86_64::FirstPlatformRelocation,
31   Pointer32NB,
32   Pointer64,
33   SectionIdx16,
34   SecRel32,
35 };
36 
37 class COFFJITLinker_x86_64 : public JITLinker<COFFJITLinker_x86_64> {
38   friend class JITLinker<COFFJITLinker_x86_64>;
39 
40 public:
41   COFFJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,
42                        std::unique_ptr<LinkGraph> G,
43                        PassConfiguration PassConfig)
44       : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
45 
46 private:
47   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
48     return x86_64::applyFixup(G, B, E, nullptr);
49   }
50 };
51 
52 class COFFLinkGraphBuilder_x86_64 : public COFFLinkGraphBuilder {
53 private:
54   Error addRelocations() override {
55     LLVM_DEBUG(dbgs() << "Processing relocations:\n");
56 
57     for (const auto &RelSect : sections())
58       if (Error Err = COFFLinkGraphBuilder::forEachRelocation(
59               RelSect, this, &COFFLinkGraphBuilder_x86_64::addSingleRelocation))
60         return Err;
61 
62     return Error::success();
63   }
64 
65   Error addSingleRelocation(const object::RelocationRef &Rel,
66                             const object::SectionRef &FixupSect,
67                             Block &BlockToFix) {
68     const object::coff_relocation *COFFRel = getObject().getCOFFRelocation(Rel);
69     auto SymbolIt = Rel.getSymbol();
70     if (SymbolIt == getObject().symbol_end()) {
71       return make_error<StringError>(
72           formatv("Invalid symbol index in relocation entry. "
73                   "index: {0}, section: {1}",
74                   COFFRel->SymbolTableIndex, FixupSect.getIndex()),
75           inconvertibleErrorCode());
76     }
77 
78     object::COFFSymbolRef COFFSymbol = getObject().getCOFFSymbol(*SymbolIt);
79     COFFSymbolIndex SymIndex = getObject().getSymbolIndex(COFFSymbol);
80 
81     Symbol *GraphSymbol = getGraphSymbol(SymIndex);
82     if (!GraphSymbol)
83       return make_error<StringError>(
84           formatv("Could not find symbol at given index, did you add it to "
85                   "JITSymbolTable? index: {0}, section: {1}",
86                   SymIndex, FixupSect.getIndex()),
87           inconvertibleErrorCode());
88 
89     int64_t Addend = 0;
90     orc::ExecutorAddr FixupAddress =
91         orc::ExecutorAddr(FixupSect.getAddress()) + Rel.getOffset();
92     Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
93 
94     Edge::Kind Kind = Edge::Invalid;
95     const char *FixupPtr = BlockToFix.getContent().data() + Offset;
96 
97     switch (Rel.getType()) {
98     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR32NB: {
99       Kind = EdgeKind_coff_x86_64::Pointer32NB;
100       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
101       break;
102     }
103     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32: {
104       Kind = EdgeKind_coff_x86_64::PCRel32;
105       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
106       break;
107     }
108     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_1: {
109       Kind = EdgeKind_coff_x86_64::PCRel32;
110       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
111       Addend -= 1;
112       break;
113     }
114     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_2: {
115       Kind = EdgeKind_coff_x86_64::PCRel32;
116       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
117       Addend -= 2;
118       break;
119     }
120     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_3: {
121       Kind = EdgeKind_coff_x86_64::PCRel32;
122       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
123       Addend -= 3;
124       break;
125     }
126     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_4: {
127       Kind = EdgeKind_coff_x86_64::PCRel32;
128       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
129       Addend -= 4;
130       break;
131     }
132     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_5: {
133       Kind = EdgeKind_coff_x86_64::PCRel32;
134       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
135       Addend -= 5;
136       break;
137     }
138     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR64: {
139       Kind = EdgeKind_coff_x86_64::Pointer64;
140       Addend = *reinterpret_cast<const support::little64_t *>(FixupPtr);
141       break;
142     }
143     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_SECTION: {
144       Kind = EdgeKind_coff_x86_64::SectionIdx16;
145       Addend = *reinterpret_cast<const support::little16_t *>(FixupPtr);
146       uint64_t SectionIdx = 0;
147       if (COFFSymbol.isAbsolute())
148         SectionIdx = getObject().getNumberOfSections() + 1;
149       else
150         SectionIdx = COFFSymbol.getSectionNumber();
151       auto *AbsSym = &getGraph().addAbsoluteSymbol(
152           "secidx", orc::ExecutorAddr(SectionIdx), 2, Linkage::Strong,
153           Scope::Local, false);
154       GraphSymbol = AbsSym;
155       break;
156     }
157     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_SECREL: {
158       // FIXME: SECREL to external symbol should be handled
159       if (!GraphSymbol->isDefined())
160         return Error::success();
161       Kind = EdgeKind_coff_x86_64::SecRel32;
162       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
163       break;
164     }
165     default: {
166       return make_error<JITLinkError>("Unsupported x86_64 relocation:" +
167                                       formatv("{0:d}", Rel.getType()));
168     }
169     };
170 
171     Edge GE(Kind, Offset, *GraphSymbol, Addend);
172     LLVM_DEBUG({
173       dbgs() << "    ";
174       printEdge(dbgs(), BlockToFix, GE, getCOFFX86RelocationKindName(Kind));
175       dbgs() << "\n";
176     });
177 
178     BlockToFix.addEdge(std::move(GE));
179 
180     return Error::success();
181   }
182 
183 public:
184   COFFLinkGraphBuilder_x86_64(const object::COFFObjectFile &Obj, const Triple T,
185                               const SubtargetFeatures Features)
186       : COFFLinkGraphBuilder(Obj, std::move(T), std::move(Features),
187                              getCOFFX86RelocationKindName) {}
188 };
189 
190 class COFFLinkGraphLowering_x86_64 {
191 public:
192   // Lowers COFF x86_64 specific edges to generic x86_64 edges.
193   Error lowerCOFFRelocationEdges(LinkGraph &G, JITLinkContext &Ctx) {
194     for (auto *B : G.blocks()) {
195       for (auto &E : B->edges()) {
196         switch (E.getKind()) {
197         case EdgeKind_coff_x86_64::Pointer32NB: {
198           auto ImageBase = getImageBaseAddress(G, Ctx);
199           if (!ImageBase)
200             return ImageBase.takeError();
201           E.setAddend(E.getAddend() - ImageBase->getValue());
202           E.setKind(x86_64::Pointer32);
203           break;
204         }
205         case EdgeKind_coff_x86_64::PCRel32: {
206           E.setKind(x86_64::PCRel32);
207           break;
208         }
209         case EdgeKind_coff_x86_64::Pointer64: {
210           E.setKind(x86_64::Pointer64);
211           break;
212         }
213         case EdgeKind_coff_x86_64::SectionIdx16: {
214           E.setKind(x86_64::Pointer16);
215           break;
216         }
217         case EdgeKind_coff_x86_64::SecRel32: {
218           E.setAddend(E.getAddend() -
219                       getSectionStart(E.getTarget().getBlock().getSection())
220                           .getValue());
221           E.setKind(x86_64::Pointer32);
222           break;
223         }
224         default:
225           break;
226         }
227       }
228     }
229     return Error::success();
230   }
231 
232 private:
233   static StringRef getImageBaseSymbolName() { return "__ImageBase"; }
234 
235   orc::ExecutorAddr getSectionStart(Section &Sec) {
236     if (!SectionStartCache.count(&Sec)) {
237       SectionRange Range(Sec);
238       SectionStartCache[&Sec] = Range.getStart();
239     }
240     return SectionStartCache[&Sec];
241   }
242 
243   Expected<orc::ExecutorAddr> getImageBaseAddress(LinkGraph &G,
244                                                   JITLinkContext &Ctx) {
245     if (this->ImageBase)
246       return this->ImageBase;
247     for (auto *S : G.defined_symbols())
248       if (S->getName() == getImageBaseSymbolName()) {
249         this->ImageBase = S->getAddress();
250         return this->ImageBase;
251       }
252 
253     JITLinkContext::LookupMap Symbols;
254     Symbols[getImageBaseSymbolName()] = SymbolLookupFlags::RequiredSymbol;
255     orc::ExecutorAddr ImageBase;
256     Error Err = Error::success();
257     Ctx.lookup(Symbols,
258                createLookupContinuation([&](Expected<AsyncLookupResult> LR) {
259                  ErrorAsOutParameter EAO(&Err);
260                  if (!LR) {
261                    Err = LR.takeError();
262                    return;
263                  }
264                  ImageBase = LR->begin()->second.getAddress();
265                }));
266     if (Err)
267       return std::move(Err);
268     this->ImageBase = ImageBase;
269     return ImageBase;
270   }
271 
272   DenseMap<Section *, orc::ExecutorAddr> SectionStartCache;
273   orc::ExecutorAddr ImageBase;
274 };
275 
276 Error lowerEdges_COFF_x86_64(LinkGraph &G, JITLinkContext *Ctx) {
277   LLVM_DEBUG(dbgs() << "Lowering COFF x86_64 edges:\n");
278   COFFLinkGraphLowering_x86_64 GraphLowering;
279 
280   if (auto Err = GraphLowering.lowerCOFFRelocationEdges(G, *Ctx))
281     return Err;
282 
283   return Error::success();
284 }
285 } // namespace
286 
287 namespace llvm {
288 namespace jitlink {
289 
290 /// Return the string name of the given COFF x86_64 edge kind.
291 const char *getCOFFX86RelocationKindName(Edge::Kind R) {
292   switch (R) {
293   case PCRel32:
294     return "PCRel32";
295   case Pointer32NB:
296     return "Pointer32NB";
297   case Pointer64:
298     return "Pointer64";
299   case SectionIdx16:
300     return "SectionIdx16";
301   case SecRel32:
302     return "SecRel32";
303   default:
304     return x86_64::getEdgeKindName(R);
305   }
306 }
307 
308 Expected<std::unique_ptr<LinkGraph>>
309 createLinkGraphFromCOFFObject_x86_64(MemoryBufferRef ObjectBuffer) {
310   LLVM_DEBUG({
311     dbgs() << "Building jitlink graph for new input "
312            << ObjectBuffer.getBufferIdentifier() << "...\n";
313   });
314 
315   auto COFFObj = object::ObjectFile::createCOFFObjectFile(ObjectBuffer);
316   if (!COFFObj)
317     return COFFObj.takeError();
318 
319   auto Features = (*COFFObj)->getFeatures();
320   if (!Features)
321     return Features.takeError();
322 
323   return COFFLinkGraphBuilder_x86_64(**COFFObj, (*COFFObj)->makeTriple(),
324                                      std::move(*Features))
325       .buildGraph();
326 }
327 
328 void link_COFF_x86_64(std::unique_ptr<LinkGraph> G,
329                       std::unique_ptr<JITLinkContext> Ctx) {
330   PassConfiguration Config;
331   const Triple &TT = G->getTargetTriple();
332   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
333     // Add a mark-live pass.
334     if (auto MarkLive = Ctx->getMarkLivePass(TT)) {
335       Config.PrePrunePasses.push_back(std::move(MarkLive));
336       Config.PrePrunePasses.push_back(SEHFrameKeepAlivePass(".pdata"));
337     } else
338       Config.PrePrunePasses.push_back(markAllSymbolsLive);
339 
340     // Add COFF edge lowering passes.
341     JITLinkContext *CtxPtr = Ctx.get();
342     Config.PreFixupPasses.push_back(
343         [CtxPtr](LinkGraph &G) { return lowerEdges_COFF_x86_64(G, CtxPtr); });
344   }
345 
346   if (auto Err = Ctx->modifyPassConfig(*G, Config))
347     return Ctx->notifyFailed(std::move(Err));
348 
349   COFFJITLinker_x86_64::link(std::move(Ctx), std::move(G), std::move(Config));
350 }
351 
352 } // namespace jitlink
353 } // namespace llvm
354