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, GOTSymbol: 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 | RelSec: RelSect, Instance: this, Method: &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(Reloc: Rel); |
69 | auto SymbolIt = Rel.getSymbol(); |
70 | if (SymbolIt == getObject().symbol_end()) { |
71 | return make_error<StringError>( |
72 | Args: formatv(Fmt: "Invalid symbol index in relocation entry. " |
73 | "index: {0}, section: {1}" , |
74 | Vals: COFFRel->SymbolTableIndex, Vals: FixupSect.getIndex()), |
75 | Args: inconvertibleErrorCode()); |
76 | } |
77 | |
78 | object::COFFSymbolRef COFFSymbol = getObject().getCOFFSymbol(Symbol: *SymbolIt); |
79 | COFFSymbolIndex SymIndex = getObject().getSymbolIndex(Symbol: COFFSymbol); |
80 | |
81 | Symbol *GraphSymbol = getGraphSymbol(SymIndex); |
82 | if (!GraphSymbol) |
83 | return make_error<StringError>( |
84 | Args: formatv(Fmt: "Could not find symbol at given index, did you add it to " |
85 | "JITSymbolTable? index: {0}, section: {1}" , |
86 | Vals&: SymIndex, Vals: FixupSect.getIndex()), |
87 | Args: 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 | Name: "secidx" , Address: orc::ExecutorAddr(SectionIdx), Size: 2, L: Linkage::Strong, |
153 | S: Scope::Local, IsLive: 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>(Args: "Unsupported x86_64 relocation:" + |
167 | formatv(Fmt: "{0:d}" , Vals: 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(E: 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(Sec&: 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(Val: &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 | LC: createLookupContinuation(Cont: [&](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&: *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(K: 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(Object: 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(x: std::move(MarkLive)); |
336 | Config.PrePrunePasses.push_back(x: SEHFrameKeepAlivePass(".pdata" )); |
337 | } else |
338 | Config.PrePrunePasses.push_back(x: markAllSymbolsLive); |
339 | |
340 | // Add COFF edge lowering passes. |
341 | JITLinkContext *CtxPtr = Ctx.get(); |
342 | Config.PreFixupPasses.push_back( |
343 | x: [CtxPtr](LinkGraph &G) { return lowerEdges_COFF_x86_64(G, Ctx: CtxPtr); }); |
344 | } |
345 | |
346 | if (auto Err = Ctx->modifyPassConfig(G&: *G, Config)) |
347 | return Ctx->notifyFailed(Err: std::move(Err)); |
348 | |
349 | COFFJITLinker_x86_64::link(Args: std::move(Ctx), Args: std::move(G), Args: std::move(Config)); |
350 | } |
351 | |
352 | } // namespace jitlink |
353 | } // namespace llvm |
354 | |