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 | Symbol *ImageBase = GetImageBaseSymbol()(getGraph()); |
97 | |
98 | switch (Rel.getType()) { |
99 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR32NB: { |
100 | if (!ImageBase) |
101 | ImageBase = &addImageBaseSymbol(); |
102 | Kind = EdgeKind_coff_x86_64::Pointer32NB; |
103 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
104 | break; |
105 | } |
106 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32: { |
107 | Kind = EdgeKind_coff_x86_64::PCRel32; |
108 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
109 | break; |
110 | } |
111 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_1: { |
112 | Kind = EdgeKind_coff_x86_64::PCRel32; |
113 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
114 | Addend -= 1; |
115 | break; |
116 | } |
117 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_2: { |
118 | Kind = EdgeKind_coff_x86_64::PCRel32; |
119 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
120 | Addend -= 2; |
121 | break; |
122 | } |
123 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_3: { |
124 | Kind = EdgeKind_coff_x86_64::PCRel32; |
125 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
126 | Addend -= 3; |
127 | break; |
128 | } |
129 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_4: { |
130 | Kind = EdgeKind_coff_x86_64::PCRel32; |
131 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
132 | Addend -= 4; |
133 | break; |
134 | } |
135 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_5: { |
136 | Kind = EdgeKind_coff_x86_64::PCRel32; |
137 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
138 | Addend -= 5; |
139 | break; |
140 | } |
141 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR64: { |
142 | Kind = EdgeKind_coff_x86_64::Pointer64; |
143 | Addend = *reinterpret_cast<const support::little64_t *>(FixupPtr); |
144 | break; |
145 | } |
146 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_SECTION: { |
147 | Kind = EdgeKind_coff_x86_64::SectionIdx16; |
148 | Addend = *reinterpret_cast<const support::little16_t *>(FixupPtr); |
149 | uint64_t SectionIdx = 0; |
150 | if (COFFSymbol.isAbsolute()) |
151 | SectionIdx = getObject().getNumberOfSections() + 1; |
152 | else |
153 | SectionIdx = COFFSymbol.getSectionNumber(); |
154 | |
155 | auto *AbsSym = &getGraph().addAbsoluteSymbol( |
156 | Name: "secidx" , Address: orc::ExecutorAddr(SectionIdx), Size: 2, L: Linkage::Strong, |
157 | S: Scope::Local, IsLive: false); |
158 | GraphSymbol = AbsSym; |
159 | break; |
160 | } |
161 | case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_SECREL: { |
162 | // FIXME: SECREL to external symbol should be handled |
163 | if (!GraphSymbol->isDefined()) |
164 | return Error::success(); |
165 | Kind = EdgeKind_coff_x86_64::SecRel32; |
166 | Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr); |
167 | break; |
168 | } |
169 | default: { |
170 | return make_error<JITLinkError>(Args: "Unsupported x86_64 relocation:" + |
171 | formatv(Fmt: "{0:d}" , Vals: Rel.getType())); |
172 | } |
173 | }; |
174 | |
175 | Edge GE(Kind, Offset, *GraphSymbol, Addend); |
176 | LLVM_DEBUG({ |
177 | dbgs() << " " ; |
178 | printEdge(dbgs(), BlockToFix, GE, getCOFFX86RelocationKindName(Kind)); |
179 | dbgs() << "\n" ; |
180 | }); |
181 | |
182 | BlockToFix.addEdge(E: std::move(GE)); |
183 | |
184 | return Error::success(); |
185 | } |
186 | |
187 | public: |
188 | COFFLinkGraphBuilder_x86_64(const object::COFFObjectFile &Obj, |
189 | std::shared_ptr<orc::SymbolStringPool> SSP, |
190 | const Triple T, const SubtargetFeatures Features) |
191 | : COFFLinkGraphBuilder(Obj, std::move(SSP), std::move(T), |
192 | std::move(Features), |
193 | getCOFFX86RelocationKindName) {} |
194 | }; |
195 | |
196 | class COFFLinkGraphLowering_x86_64 { |
197 | public: |
198 | // Lowers COFF x86_64 specific edges to generic x86_64 edges. |
199 | Error operator()(LinkGraph &G) { |
200 | for (auto *B : G.blocks()) { |
201 | for (auto &E : B->edges()) { |
202 | switch (E.getKind()) { |
203 | case EdgeKind_coff_x86_64::Pointer32NB: { |
204 | auto ImageBase = GetImageBase(G); |
205 | assert(ImageBase && "__ImageBase symbol must be defined" ); |
206 | E.setAddend(E.getAddend() - ImageBase->getAddress().getValue()); |
207 | E.setKind(x86_64::Pointer32); |
208 | break; |
209 | } |
210 | case EdgeKind_coff_x86_64::PCRel32: { |
211 | E.setKind(x86_64::PCRel32); |
212 | break; |
213 | } |
214 | case EdgeKind_coff_x86_64::Pointer64: { |
215 | E.setKind(x86_64::Pointer64); |
216 | break; |
217 | } |
218 | case EdgeKind_coff_x86_64::SectionIdx16: { |
219 | E.setKind(x86_64::Pointer16); |
220 | break; |
221 | } |
222 | case EdgeKind_coff_x86_64::SecRel32: { |
223 | E.setAddend(E.getAddend() - |
224 | getSectionStart(Sec&: E.getTarget().getSection()).getValue()); |
225 | E.setKind(x86_64::Pointer32); |
226 | break; |
227 | } |
228 | default: |
229 | break; |
230 | } |
231 | } |
232 | } |
233 | return Error::success(); |
234 | } |
235 | |
236 | private: |
237 | orc::ExecutorAddr getSectionStart(Section &Sec) { |
238 | auto [It, Inserted] = SectionStartCache.try_emplace(Key: &Sec); |
239 | if (Inserted) { |
240 | SectionRange Range(Sec); |
241 | It->second = Range.getStart(); |
242 | } |
243 | return It->second; |
244 | } |
245 | |
246 | GetImageBaseSymbol GetImageBase; |
247 | DenseMap<Section *, orc::ExecutorAddr> SectionStartCache; |
248 | }; |
249 | } // namespace |
250 | |
251 | namespace llvm { |
252 | namespace jitlink { |
253 | |
254 | /// Return the string name of the given COFF x86_64 edge kind. |
255 | const char *getCOFFX86RelocationKindName(Edge::Kind R) { |
256 | switch (R) { |
257 | case PCRel32: |
258 | return "PCRel32" ; |
259 | case Pointer32NB: |
260 | return "Pointer32NB" ; |
261 | case Pointer64: |
262 | return "Pointer64" ; |
263 | case SectionIdx16: |
264 | return "SectionIdx16" ; |
265 | case SecRel32: |
266 | return "SecRel32" ; |
267 | default: |
268 | return x86_64::getEdgeKindName(K: R); |
269 | } |
270 | } |
271 | |
272 | Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromCOFFObject_x86_64( |
273 | MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) { |
274 | LLVM_DEBUG({ |
275 | dbgs() << "Building jitlink graph for new input " |
276 | << ObjectBuffer.getBufferIdentifier() << "...\n" ; |
277 | }); |
278 | |
279 | auto COFFObj = object::ObjectFile::createCOFFObjectFile(Object: ObjectBuffer); |
280 | if (!COFFObj) |
281 | return COFFObj.takeError(); |
282 | |
283 | auto Features = (*COFFObj)->getFeatures(); |
284 | if (!Features) |
285 | return Features.takeError(); |
286 | |
287 | return COFFLinkGraphBuilder_x86_64(**COFFObj, std::move(SSP), |
288 | (*COFFObj)->makeTriple(), |
289 | std::move(*Features)) |
290 | .buildGraph(); |
291 | } |
292 | |
293 | void link_COFF_x86_64(std::unique_ptr<LinkGraph> G, |
294 | std::unique_ptr<JITLinkContext> Ctx) { |
295 | PassConfiguration Config; |
296 | const Triple &TT = G->getTargetTriple(); |
297 | if (Ctx->shouldAddDefaultTargetPasses(TT)) { |
298 | // Add a mark-live pass. |
299 | if (auto MarkLive = Ctx->getMarkLivePass(TT)) { |
300 | Config.PrePrunePasses.push_back(x: std::move(MarkLive)); |
301 | Config.PrePrunePasses.push_back(x: SEHFrameKeepAlivePass(".pdata" )); |
302 | } else |
303 | Config.PrePrunePasses.push_back(x: markAllSymbolsLive); |
304 | |
305 | // Add COFF edge lowering passes. |
306 | Config.PreFixupPasses.push_back(x: COFFLinkGraphLowering_x86_64()); |
307 | } |
308 | |
309 | if (auto Err = Ctx->modifyPassConfig(G&: *G, Config)) |
310 | return Ctx->notifyFailed(Err: std::move(Err)); |
311 | |
312 | COFFJITLinker_x86_64::link(Args: std::move(Ctx), Args: std::move(G), Args: std::move(Config)); |
313 | } |
314 | |
315 | } // namespace jitlink |
316 | } // namespace llvm |
317 | |