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 };
33 
34 class COFFJITLinker_x86_64 : public JITLinker<COFFJITLinker_x86_64> {
35   friend class JITLinker<COFFJITLinker_x86_64>;
36 
37 public:
38   COFFJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,
39                        std::unique_ptr<LinkGraph> G,
40                        PassConfiguration PassConfig)
41       : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
42 
43 private:
44   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
45     return x86_64::applyFixup(G, B, E, nullptr);
46   }
47 };
48 
49 class COFFLinkGraphBuilder_x86_64 : public COFFLinkGraphBuilder {
50 private:
51   Error addRelocations() override {
52     LLVM_DEBUG(dbgs() << "Processing relocations:\n");
53 
54     for (const auto &RelSect : sections())
55       if (Error Err = COFFLinkGraphBuilder::forEachRelocation(
56               RelSect, this, &COFFLinkGraphBuilder_x86_64::addSingleRelocation))
57         return Err;
58 
59     return Error::success();
60   }
61 
62   Error addSingleRelocation(const object::RelocationRef &Rel,
63                             const object::SectionRef &FixupSect,
64                             Block &BlockToFix) {
65     const object::coff_relocation *COFFRel = getObject().getCOFFRelocation(Rel);
66     auto SymbolIt = Rel.getSymbol();
67     if (SymbolIt == getObject().symbol_end()) {
68       return make_error<StringError>(
69           formatv("Invalid symbol index in relocation entry. "
70                   "index: {0}, section: {1}",
71                   COFFRel->SymbolTableIndex, FixupSect.getIndex()),
72           inconvertibleErrorCode());
73     }
74 
75     object::COFFSymbolRef COFFSymbol = getObject().getCOFFSymbol(*SymbolIt);
76     COFFSymbolIndex SymIndex = getObject().getSymbolIndex(COFFSymbol);
77 
78     Symbol *GraphSymbol = getGraphSymbol(SymIndex);
79     if (!GraphSymbol)
80       return make_error<StringError>(
81           formatv("Could not find symbol at given index, did you add it to "
82                   "JITSymbolTable? index: {0}, section: {1}",
83                   SymIndex, FixupSect.getIndex()),
84           inconvertibleErrorCode());
85 
86     int64_t Addend = 0;
87     orc::ExecutorAddr FixupAddress =
88         orc::ExecutorAddr(FixupSect.getAddress()) + Rel.getOffset();
89     Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
90 
91     Edge::Kind Kind = Edge::Invalid;
92     const char *FixupPtr = BlockToFix.getContent().data() + Offset;
93 
94     switch (Rel.getType()) {
95     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR32NB: {
96       Kind = EdgeKind_coff_x86_64::Pointer32NB;
97       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
98       break;
99     }
100     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32: {
101       Kind = EdgeKind_coff_x86_64::PCRel32;
102       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
103       break;
104     }
105     case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_1: {
106       Kind = EdgeKind_coff_x86_64::PCRel32;
107       Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
108       Addend -= 1;
109       break;
110     }
111     default: {
112       return make_error<JITLinkError>("Unsupported x86_64 relocation:" +
113                                       formatv("{0:d}", Rel.getType()));
114     }
115     };
116 
117     Edge GE(Kind, Offset, *GraphSymbol, Addend);
118     LLVM_DEBUG({
119       dbgs() << "    ";
120       printEdge(dbgs(), BlockToFix, GE, getCOFFX86RelocationKindName(Kind));
121       dbgs() << "\n";
122     });
123 
124     BlockToFix.addEdge(std::move(GE));
125 
126     return Error::success();
127   }
128 
129 public:
130   COFFLinkGraphBuilder_x86_64(const object::COFFObjectFile &Obj, const Triple T)
131       : COFFLinkGraphBuilder(Obj, std::move(T), getCOFFX86RelocationKindName) {}
132 };
133 
134 class COFFLinkGraphLowering_x86_64 {
135 public:
136   // Lowers COFF x86_64 specific edges to generic x86_64 edges.
137   Error lowerCOFFRelocationEdges(LinkGraph &G, JITLinkContext &Ctx) {
138     for (auto *B : G.blocks()) {
139       for (auto &E : B->edges()) {
140         switch (E.getKind()) {
141         case EdgeKind_coff_x86_64::Pointer32NB: {
142           auto ImageBase = getImageBaseAddress(G, Ctx);
143           if (!ImageBase)
144             return ImageBase.takeError();
145           E.setAddend(E.getAddend() - *ImageBase);
146           E.setKind(x86_64::Pointer32);
147           break;
148         }
149         case EdgeKind_coff_x86_64::PCRel32: {
150           E.setKind(x86_64::PCRel32);
151           break;
152         }
153         default:
154           break;
155         }
156       }
157     }
158     return Error::success();
159   }
160 
161 private:
162   static StringRef getImageBaseSymbolName() { return "__ImageBase"; }
163   Expected<JITTargetAddress> getImageBaseAddress(LinkGraph &G,
164                                                  JITLinkContext &Ctx) {
165     if (this->ImageBase)
166       return this->ImageBase;
167     for (auto *S : G.defined_symbols())
168       if (S->getName() == getImageBaseSymbolName()) {
169         this->ImageBase = S->getAddress().getValue();
170         return this->ImageBase;
171       }
172 
173     JITLinkContext::LookupMap Symbols;
174     Symbols[getImageBaseSymbolName()] = SymbolLookupFlags::RequiredSymbol;
175     JITTargetAddress ImageBase;
176     Error Err = Error::success();
177     Ctx.lookup(Symbols,
178                createLookupContinuation([&](Expected<AsyncLookupResult> LR) {
179                  ErrorAsOutParameter EAO(&Err);
180                  if (!LR) {
181                    Err = LR.takeError();
182                    return;
183                  }
184                  auto &ImageBaseSymbol = LR->begin()->second;
185                  ImageBase = ImageBaseSymbol.getAddress();
186                }));
187     if (Err)
188       return std::move(Err);
189     this->ImageBase = ImageBase;
190     return ImageBase;
191   }
192   JITTargetAddress ImageBase = 0;
193 };
194 
195 Error lowerEdges_COFF_x86_64(LinkGraph &G, JITLinkContext *Ctx) {
196   LLVM_DEBUG(dbgs() << "Lowering COFF x86_64 edges:\n");
197   COFFLinkGraphLowering_x86_64 GraphLowering;
198 
199   if (auto Err = GraphLowering.lowerCOFFRelocationEdges(G, *Ctx))
200     return Err;
201 
202   return Error::success();
203 }
204 } // namespace
205 
206 namespace llvm {
207 namespace jitlink {
208 
209 /// Return the string name of the given COFF x86_64 edge kind.
210 const char *getCOFFX86RelocationKindName(Edge::Kind R) {
211   switch (R) {
212   case PCRel32:
213     return "PCRel32";
214   case Pointer32NB:
215     return "Pointer32NB";
216   default:
217     return x86_64::getEdgeKindName(R);
218   }
219 }
220 
221 Expected<std::unique_ptr<LinkGraph>>
222 createLinkGraphFromCOFFObject_x86_64(MemoryBufferRef ObjectBuffer) {
223   LLVM_DEBUG({
224     dbgs() << "Building jitlink graph for new input "
225            << ObjectBuffer.getBufferIdentifier() << "...\n";
226   });
227 
228   auto COFFObj = object::ObjectFile::createCOFFObjectFile(ObjectBuffer);
229   if (!COFFObj)
230     return COFFObj.takeError();
231 
232   return COFFLinkGraphBuilder_x86_64(**COFFObj, (*COFFObj)->makeTriple())
233       .buildGraph();
234 }
235 
236 void link_COFF_x86_64(std::unique_ptr<LinkGraph> G,
237                       std::unique_ptr<JITLinkContext> Ctx) {
238   PassConfiguration Config;
239   const Triple &TT = G->getTargetTriple();
240   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
241     // Add a mark-live pass.
242     if (auto MarkLive = Ctx->getMarkLivePass(TT)) {
243       Config.PrePrunePasses.push_back(std::move(MarkLive));
244       Config.PrePrunePasses.push_back(SEHFrameKeepAlivePass(".pdata"));
245     } else
246       Config.PrePrunePasses.push_back(markAllSymbolsLive);
247 
248     // Add COFF edge lowering passes.
249     JITLinkContext *CtxPtr = Ctx.get();
250     Config.PreFixupPasses.push_back(
251         [CtxPtr](LinkGraph &G) { return lowerEdges_COFF_x86_64(G, CtxPtr); });
252   }
253 
254   if (auto Err = Ctx->modifyPassConfig(*G, Config))
255     return Ctx->notifyFailed(std::move(Err));
256 
257   COFFJITLinker_x86_64::link(std::move(Ctx), std::move(G), std::move(Config));
258 }
259 
260 } // namespace jitlink
261 } // namespace llvm
262