1 | //=--------- COFFLinkGraphBuilder.cpp - COFF LinkGraph builder ----------===// |
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 COFF LinkGraph building code. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | #include "COFFLinkGraphBuilder.h" |
13 | |
14 | #define DEBUG_TYPE "jitlink" |
15 | |
16 | static const char *CommonSectionName = "__common" ; |
17 | |
18 | namespace llvm { |
19 | namespace jitlink { |
20 | |
21 | static Triple createTripleWithCOFFFormat(Triple T) { |
22 | T.setObjectFormat(Triple::COFF); |
23 | return T; |
24 | } |
25 | |
26 | COFFLinkGraphBuilder::COFFLinkGraphBuilder( |
27 | const object::COFFObjectFile &Obj, Triple TT, SubtargetFeatures Features, |
28 | LinkGraph::GetEdgeKindNameFunction GetEdgeKindName) |
29 | : Obj(Obj), G(std::make_unique<LinkGraph>( |
30 | args: Obj.getFileName().str(), args: createTripleWithCOFFFormat(T: TT), |
31 | args: std::move(Features), args: getPointerSize(Obj), |
32 | args: getEndianness(Obj), args: std::move(GetEdgeKindName))) { |
33 | LLVM_DEBUG({ |
34 | dbgs() << "Created COFFLinkGraphBuilder for \"" << Obj.getFileName() |
35 | << "\"\n" ; |
36 | }); |
37 | } |
38 | |
39 | COFFLinkGraphBuilder::~COFFLinkGraphBuilder() = default; |
40 | |
41 | unsigned |
42 | COFFLinkGraphBuilder::getPointerSize(const object::COFFObjectFile &Obj) { |
43 | return Obj.getBytesInAddress(); |
44 | } |
45 | |
46 | llvm::endianness |
47 | COFFLinkGraphBuilder::getEndianness(const object::COFFObjectFile &Obj) { |
48 | return Obj.isLittleEndian() ? llvm::endianness::little |
49 | : llvm::endianness::big; |
50 | } |
51 | |
52 | uint64_t COFFLinkGraphBuilder::getSectionSize(const object::COFFObjectFile &Obj, |
53 | const object::coff_section *Sec) { |
54 | // Consider the difference between executable form and object form. |
55 | // More information is inside COFFObjectFile::getSectionSize |
56 | if (Obj.getDOSHeader()) |
57 | return std::min(a: Sec->VirtualSize, b: Sec->SizeOfRawData); |
58 | return Sec->SizeOfRawData; |
59 | } |
60 | |
61 | uint64_t |
62 | COFFLinkGraphBuilder::getSectionAddress(const object::COFFObjectFile &Obj, |
63 | const object::coff_section *Section) { |
64 | return Section->VirtualAddress + Obj.getImageBase(); |
65 | } |
66 | |
67 | bool COFFLinkGraphBuilder::isComdatSection( |
68 | const object::coff_section *Section) { |
69 | return Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT; |
70 | } |
71 | |
72 | Section &COFFLinkGraphBuilder::getCommonSection() { |
73 | if (!CommonSection) |
74 | CommonSection = &G->createSection(Name: CommonSectionName, |
75 | Prot: orc::MemProt::Read | orc::MemProt::Write); |
76 | return *CommonSection; |
77 | } |
78 | |
79 | Expected<std::unique_ptr<LinkGraph>> COFFLinkGraphBuilder::buildGraph() { |
80 | if (!Obj.isRelocatableObject()) |
81 | return make_error<JITLinkError>(Args: "Object is not a relocatable COFF file" ); |
82 | |
83 | if (auto Err = graphifySections()) |
84 | return std::move(Err); |
85 | |
86 | if (auto Err = graphifySymbols()) |
87 | return std::move(Err); |
88 | |
89 | if (auto Err = addRelocations()) |
90 | return std::move(Err); |
91 | |
92 | return std::move(G); |
93 | } |
94 | |
95 | StringRef |
96 | COFFLinkGraphBuilder::getCOFFSectionName(COFFSectionIndex SectionIndex, |
97 | const object::coff_section *Sec, |
98 | object::COFFSymbolRef Sym) { |
99 | switch (SectionIndex) { |
100 | case COFF::IMAGE_SYM_UNDEFINED: { |
101 | if (Sym.getValue()) |
102 | return "(common)" ; |
103 | else |
104 | return "(external)" ; |
105 | } |
106 | case COFF::IMAGE_SYM_ABSOLUTE: |
107 | return "(absolute)" ; |
108 | case COFF::IMAGE_SYM_DEBUG: { |
109 | // Used with .file symbol |
110 | return "(debug)" ; |
111 | } |
112 | default: { |
113 | // Non reserved regular section numbers |
114 | if (Expected<StringRef> SecNameOrErr = Obj.getSectionName(Sec)) |
115 | return *SecNameOrErr; |
116 | } |
117 | } |
118 | return "" ; |
119 | } |
120 | |
121 | Error COFFLinkGraphBuilder::graphifySections() { |
122 | LLVM_DEBUG(dbgs() << " Creating graph sections...\n" ); |
123 | |
124 | GraphBlocks.resize(new_size: Obj.getNumberOfSections() + 1); |
125 | // For each section... |
126 | for (COFFSectionIndex SecIndex = 1; |
127 | SecIndex <= static_cast<COFFSectionIndex>(Obj.getNumberOfSections()); |
128 | SecIndex++) { |
129 | Expected<const object::coff_section *> Sec = Obj.getSection(index: SecIndex); |
130 | if (!Sec) |
131 | return Sec.takeError(); |
132 | |
133 | StringRef SectionName; |
134 | if (Expected<StringRef> SecNameOrErr = Obj.getSectionName(Sec: *Sec)) |
135 | SectionName = *SecNameOrErr; |
136 | |
137 | // FIXME: Skip debug info sections |
138 | if (SectionName == ".voltbl" ) { |
139 | LLVM_DEBUG({ |
140 | dbgs() << " " |
141 | << "Skipping section \"" << SectionName << "\"\n" ; |
142 | }); |
143 | continue; |
144 | } |
145 | |
146 | LLVM_DEBUG({ |
147 | dbgs() << " " |
148 | << "Creating section for \"" << SectionName << "\"\n" ; |
149 | }); |
150 | |
151 | // Get the section's memory protection flags. |
152 | orc::MemProt Prot = orc::MemProt::Read; |
153 | if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE) |
154 | Prot |= orc::MemProt::Exec; |
155 | if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_READ) |
156 | Prot |= orc::MemProt::Read; |
157 | if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_WRITE) |
158 | Prot |= orc::MemProt::Write; |
159 | |
160 | // Look for existing sections first. |
161 | auto *GraphSec = G->findSectionByName(Name: SectionName); |
162 | if (!GraphSec) { |
163 | GraphSec = &G->createSection(Name: SectionName, Prot); |
164 | if ((*Sec)->Characteristics & COFF::IMAGE_SCN_LNK_REMOVE) |
165 | GraphSec->setMemLifetime(orc::MemLifetime::NoAlloc); |
166 | } |
167 | if (GraphSec->getMemProt() != Prot) |
168 | return make_error<JITLinkError>(Args: "MemProt should match" ); |
169 | |
170 | Block *B = nullptr; |
171 | if ((*Sec)->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) |
172 | B = &G->createZeroFillBlock( |
173 | Parent&: *GraphSec, Size: getSectionSize(Obj, Sec: *Sec), |
174 | Address: orc::ExecutorAddr(getSectionAddress(Obj, Section: *Sec)), |
175 | Alignment: (*Sec)->getAlignment(), AlignmentOffset: 0); |
176 | else { |
177 | ArrayRef<uint8_t> Data; |
178 | if (auto Err = Obj.getSectionContents(Sec: *Sec, Res&: Data)) |
179 | return Err; |
180 | |
181 | auto CharData = ArrayRef<char>( |
182 | reinterpret_cast<const char *>(Data.data()), Data.size()); |
183 | |
184 | if (SectionName == getDirectiveSectionName()) |
185 | if (auto Err = handleDirectiveSection( |
186 | Str: StringRef(CharData.data(), CharData.size()))) |
187 | return Err; |
188 | |
189 | B = &G->createContentBlock( |
190 | Parent&: *GraphSec, Content: CharData, Address: orc::ExecutorAddr(getSectionAddress(Obj, Section: *Sec)), |
191 | Alignment: (*Sec)->getAlignment(), AlignmentOffset: 0); |
192 | } |
193 | |
194 | setGraphBlock(SecIndex, B); |
195 | } |
196 | |
197 | return Error::success(); |
198 | } |
199 | |
200 | Error COFFLinkGraphBuilder::graphifySymbols() { |
201 | LLVM_DEBUG(dbgs() << " Creating graph symbols...\n" ); |
202 | |
203 | SymbolSets.resize(new_size: Obj.getNumberOfSections() + 1); |
204 | PendingComdatExports.resize(new_size: Obj.getNumberOfSections() + 1); |
205 | GraphSymbols.resize(new_size: Obj.getNumberOfSymbols()); |
206 | |
207 | for (COFFSymbolIndex SymIndex = 0; |
208 | SymIndex < static_cast<COFFSymbolIndex>(Obj.getNumberOfSymbols()); |
209 | SymIndex++) { |
210 | Expected<object::COFFSymbolRef> Sym = Obj.getSymbol(index: SymIndex); |
211 | if (!Sym) |
212 | return Sym.takeError(); |
213 | |
214 | StringRef SymbolName; |
215 | if (Expected<StringRef> SymNameOrErr = Obj.getSymbolName(Symbol: *Sym)) |
216 | SymbolName = *SymNameOrErr; |
217 | |
218 | COFFSectionIndex SectionIndex = Sym->getSectionNumber(); |
219 | const object::coff_section *Sec = nullptr; |
220 | |
221 | if (!COFF::isReservedSectionNumber(SectionNumber: SectionIndex)) { |
222 | auto SecOrErr = Obj.getSection(index: SectionIndex); |
223 | if (!SecOrErr) |
224 | return make_error<JITLinkError>( |
225 | Args: "Invalid COFF section number:" + formatv(Fmt: "{0:d}: " , Vals&: SectionIndex) + |
226 | " (" + toString(E: SecOrErr.takeError()) + ")" ); |
227 | Sec = *SecOrErr; |
228 | } |
229 | |
230 | // Create jitlink symbol |
231 | jitlink::Symbol *GSym = nullptr; |
232 | if (Sym->isFileRecord()) |
233 | LLVM_DEBUG({ |
234 | dbgs() << " " << SymIndex << ": Skipping FileRecord symbol \"" |
235 | << SymbolName << "\" in " |
236 | << getCOFFSectionName(SectionIndex, Sec, *Sym) |
237 | << " (index: " << SectionIndex << ") \n" ; |
238 | }); |
239 | else if (Sym->isUndefined()) { |
240 | GSym = createExternalSymbol(SymIndex, SymbolName, Symbol: *Sym, Section: Sec); |
241 | } else if (Sym->isWeakExternal()) { |
242 | auto *WeakExternal = Sym->getAux<object::coff_aux_weak_external>(); |
243 | COFFSymbolIndex TagIndex = WeakExternal->TagIndex; |
244 | uint32_t Characteristics = WeakExternal->Characteristics; |
245 | WeakExternalRequests.push_back( |
246 | x: {.Alias: SymIndex, .Target: TagIndex, .Characteristics: Characteristics, .SymbolName: SymbolName}); |
247 | } else { |
248 | Expected<jitlink::Symbol *> NewGSym = |
249 | createDefinedSymbol(SymIndex, SymbolName, Symbol: *Sym, Section: Sec); |
250 | if (!NewGSym) |
251 | return NewGSym.takeError(); |
252 | GSym = *NewGSym; |
253 | if (GSym) { |
254 | LLVM_DEBUG({ |
255 | dbgs() << " " << SymIndex |
256 | << ": Creating defined graph symbol for COFF symbol \"" |
257 | << SymbolName << "\" in " |
258 | << getCOFFSectionName(SectionIndex, Sec, *Sym) |
259 | << " (index: " << SectionIndex << ") \n" ; |
260 | dbgs() << " " << *GSym << "\n" ; |
261 | }); |
262 | } |
263 | } |
264 | |
265 | // Register the symbol |
266 | if (GSym) |
267 | setGraphSymbol(SecIndex: SectionIndex, SymIndex, Sym&: *GSym); |
268 | SymIndex += Sym->getNumberOfAuxSymbols(); |
269 | } |
270 | |
271 | if (auto Err = flushWeakAliasRequests()) |
272 | return Err; |
273 | |
274 | if (auto Err = handleAlternateNames()) |
275 | return Err; |
276 | |
277 | if (auto Err = calculateImplicitSizeOfSymbols()) |
278 | return Err; |
279 | |
280 | return Error::success(); |
281 | } |
282 | |
283 | Error COFFLinkGraphBuilder::handleDirectiveSection(StringRef Str) { |
284 | auto Parsed = DirectiveParser.parse(Str); |
285 | if (!Parsed) |
286 | return Parsed.takeError(); |
287 | for (auto *Arg : *Parsed) { |
288 | StringRef S = Arg->getValue(); |
289 | switch (Arg->getOption().getID()) { |
290 | case COFF_OPT_alternatename: { |
291 | StringRef From, To; |
292 | std::tie(args&: From, args&: To) = S.split(Separator: '='); |
293 | if (From.empty() || To.empty()) |
294 | return make_error<JITLinkError>( |
295 | Args: "Invalid COFF /alternatename directive" ); |
296 | AlternateNames[From] = To; |
297 | break; |
298 | } |
299 | case COFF_OPT_incl: { |
300 | auto DataCopy = G->allocateContent(Source: S); |
301 | StringRef StrCopy(DataCopy.data(), DataCopy.size()); |
302 | ExternalSymbols[StrCopy] = &G->addExternalSymbol(Name: StrCopy, Size: 0, IsWeaklyReferenced: false); |
303 | ExternalSymbols[StrCopy]->setLive(true); |
304 | break; |
305 | } |
306 | case COFF_OPT_export: |
307 | break; |
308 | default: { |
309 | LLVM_DEBUG({ |
310 | dbgs() << "Unknown coff directive: " << Arg->getSpelling() << "\n" ; |
311 | }); |
312 | break; |
313 | } |
314 | } |
315 | } |
316 | return Error::success(); |
317 | } |
318 | |
319 | Error COFFLinkGraphBuilder::flushWeakAliasRequests() { |
320 | // Export the weak external symbols and alias it |
321 | for (auto &WeakExternal : WeakExternalRequests) { |
322 | if (auto *Target = getGraphSymbol(SymIndex: WeakExternal.Target)) { |
323 | Expected<object::COFFSymbolRef> AliasSymbol = |
324 | Obj.getSymbol(index: WeakExternal.Alias); |
325 | if (!AliasSymbol) |
326 | return AliasSymbol.takeError(); |
327 | |
328 | // FIXME: IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY and |
329 | // IMAGE_WEAK_EXTERN_SEARCH_LIBRARY are handled in the same way. |
330 | Scope S = |
331 | WeakExternal.Characteristics == COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS |
332 | ? Scope::Default |
333 | : Scope::Local; |
334 | |
335 | auto NewSymbol = |
336 | createAliasSymbol(SymbolName: WeakExternal.SymbolName, L: Linkage::Weak, S, Target&: *Target); |
337 | if (!NewSymbol) |
338 | return NewSymbol.takeError(); |
339 | setGraphSymbol(SecIndex: AliasSymbol->getSectionNumber(), SymIndex: WeakExternal.Alias, |
340 | Sym&: **NewSymbol); |
341 | LLVM_DEBUG({ |
342 | dbgs() << " " << WeakExternal.Alias |
343 | << ": Creating weak external symbol for COFF symbol \"" |
344 | << WeakExternal.SymbolName << "\" in section " |
345 | << AliasSymbol->getSectionNumber() << "\n" ; |
346 | dbgs() << " " << **NewSymbol << "\n" ; |
347 | }); |
348 | } else |
349 | return make_error<JITLinkError>(Args: "Weak symbol alias requested but actual " |
350 | "symbol not found for symbol " + |
351 | formatv(Fmt: "{0:d}" , Vals&: WeakExternal.Alias)); |
352 | } |
353 | return Error::success(); |
354 | } |
355 | |
356 | Error COFFLinkGraphBuilder::handleAlternateNames() { |
357 | for (auto &KeyValue : AlternateNames) |
358 | if (DefinedSymbols.count(Val: KeyValue.second) && |
359 | ExternalSymbols.count(Val: KeyValue.first)) { |
360 | auto *Target = DefinedSymbols[KeyValue.second]; |
361 | auto *Alias = ExternalSymbols[KeyValue.first]; |
362 | G->makeDefined(Sym&: *Alias, Content&: Target->getBlock(), Offset: Target->getOffset(), |
363 | Size: Target->getSize(), L: Linkage::Weak, S: Scope::Local, IsLive: false); |
364 | } |
365 | return Error::success(); |
366 | } |
367 | |
368 | Symbol *COFFLinkGraphBuilder::createExternalSymbol( |
369 | COFFSymbolIndex SymIndex, StringRef SymbolName, |
370 | object::COFFSymbolRef Symbol, const object::coff_section *Section) { |
371 | if (!ExternalSymbols.count(Val: SymbolName)) |
372 | ExternalSymbols[SymbolName] = |
373 | &G->addExternalSymbol(Name: SymbolName, Size: Symbol.getValue(), IsWeaklyReferenced: false); |
374 | |
375 | LLVM_DEBUG({ |
376 | dbgs() << " " << SymIndex |
377 | << ": Creating external graph symbol for COFF symbol \"" |
378 | << SymbolName << "\" in " |
379 | << getCOFFSectionName(Symbol.getSectionNumber(), Section, Symbol) |
380 | << " (index: " << Symbol.getSectionNumber() << ") \n" ; |
381 | }); |
382 | return ExternalSymbols[SymbolName]; |
383 | } |
384 | |
385 | Expected<Symbol *> COFFLinkGraphBuilder::createAliasSymbol(StringRef SymbolName, |
386 | Linkage L, Scope S, |
387 | Symbol &Target) { |
388 | if (!Target.isDefined()) { |
389 | // FIXME: Support this when there's a way to handle this. |
390 | return make_error<JITLinkError>(Args: "Weak external symbol with external " |
391 | "symbol as alternative not supported." ); |
392 | } |
393 | return &G->addDefinedSymbol(Content&: Target.getBlock(), Offset: Target.getOffset(), Name: SymbolName, |
394 | Size: Target.getSize(), L, S, IsCallable: Target.isCallable(), |
395 | IsLive: false); |
396 | } |
397 | |
398 | // In COFF, most of the defined symbols don't contain the size information. |
399 | // Hence, we calculate the "implicit" size of symbol by taking the delta of |
400 | // offsets of consecutive symbols within a block. We maintain a balanced tree |
401 | // set of symbols sorted by offset per each block in order to achieve |
402 | // logarithmic time complexity of sorted symbol insertion. Symbol is inserted to |
403 | // the set once it's processed in graphifySymbols. In this function, we iterate |
404 | // each collected symbol in sorted order and calculate the implicit size. |
405 | Error COFFLinkGraphBuilder::calculateImplicitSizeOfSymbols() { |
406 | for (COFFSectionIndex SecIndex = 1; |
407 | SecIndex <= static_cast<COFFSectionIndex>(Obj.getNumberOfSections()); |
408 | SecIndex++) { |
409 | auto &SymbolSet = SymbolSets[SecIndex]; |
410 | if (SymbolSet.empty()) |
411 | continue; |
412 | jitlink::Block *B = getGraphBlock(SecIndex); |
413 | orc::ExecutorAddrDiff LastOffset = B->getSize(); |
414 | orc::ExecutorAddrDiff LastDifferentOffset = B->getSize(); |
415 | orc::ExecutorAddrDiff LastSize = 0; |
416 | for (auto It = SymbolSet.rbegin(); It != SymbolSet.rend(); It++) { |
417 | orc::ExecutorAddrDiff Offset = It->first; |
418 | jitlink::Symbol *Symbol = It->second; |
419 | orc::ExecutorAddrDiff CandSize; |
420 | // Last offset can be same when aliasing happened |
421 | if (Symbol->getOffset() == LastOffset) |
422 | CandSize = LastSize; |
423 | else |
424 | CandSize = LastOffset - Offset; |
425 | |
426 | LLVM_DEBUG({ |
427 | if (Offset + Symbol->getSize() > LastDifferentOffset) |
428 | dbgs() << " Overlapping symbol range generated for the following " |
429 | "symbol:" |
430 | << "\n" |
431 | << " " << *Symbol << "\n" ; |
432 | }); |
433 | (void)LastDifferentOffset; |
434 | if (LastOffset != Offset) |
435 | LastDifferentOffset = Offset; |
436 | LastSize = CandSize; |
437 | LastOffset = Offset; |
438 | if (Symbol->getSize()) { |
439 | // Non empty symbol can happen in COMDAT symbol. |
440 | // We don't consider the possibility of overlapping symbol range that |
441 | // could be introduced by disparity between inferred symbol size and |
442 | // defined symbol size because symbol size information is currently only |
443 | // used by jitlink-check where we have control to not make overlapping |
444 | // ranges. |
445 | continue; |
446 | } |
447 | |
448 | LLVM_DEBUG({ |
449 | if (!CandSize) |
450 | dbgs() << " Empty implicit symbol size generated for the following " |
451 | "symbol:" |
452 | << "\n" |
453 | << " " << *Symbol << "\n" ; |
454 | }); |
455 | |
456 | Symbol->setSize(CandSize); |
457 | } |
458 | } |
459 | return Error::success(); |
460 | } |
461 | |
462 | Expected<Symbol *> COFFLinkGraphBuilder::createDefinedSymbol( |
463 | COFFSymbolIndex SymIndex, StringRef SymbolName, |
464 | object::COFFSymbolRef Symbol, const object::coff_section *Section) { |
465 | if (Symbol.isCommon()) { |
466 | // FIXME: correct alignment |
467 | return &G->addDefinedSymbol( |
468 | Content&: G->createZeroFillBlock(Parent&: getCommonSection(), Size: Symbol.getValue(), |
469 | Address: orc::ExecutorAddr(), Alignment: Symbol.getValue(), AlignmentOffset: 0), |
470 | Offset: 0, Name: SymbolName, Size: Symbol.getValue(), L: Linkage::Strong, S: Scope::Default, |
471 | IsCallable: false, IsLive: false); |
472 | } |
473 | if (Symbol.isAbsolute()) |
474 | return &G->addAbsoluteSymbol(Name: SymbolName, |
475 | Address: orc::ExecutorAddr(Symbol.getValue()), Size: 0, |
476 | L: Linkage::Strong, S: Scope::Local, IsLive: false); |
477 | |
478 | if (llvm::COFF::isReservedSectionNumber(SectionNumber: Symbol.getSectionNumber())) |
479 | return make_error<JITLinkError>( |
480 | Args: "Reserved section number used in regular symbol " + |
481 | formatv(Fmt: "{0:d}" , Vals&: SymIndex)); |
482 | |
483 | Block *B = getGraphBlock(SecIndex: Symbol.getSectionNumber()); |
484 | if (!B) { |
485 | LLVM_DEBUG({ |
486 | dbgs() << " " << SymIndex |
487 | << ": Skipping graph symbol since section was not created for " |
488 | "COFF symbol \"" |
489 | << SymbolName << "\" in section " << Symbol.getSectionNumber() |
490 | << "\n" ; |
491 | }); |
492 | return nullptr; |
493 | } |
494 | |
495 | if (Symbol.isExternal()) { |
496 | // This is not a comdat sequence, export the symbol as it is |
497 | if (!isComdatSection(Section)) { |
498 | auto GSym = &G->addDefinedSymbol( |
499 | Content&: *B, Offset: Symbol.getValue(), Name: SymbolName, Size: 0, L: Linkage::Strong, S: Scope::Default, |
500 | IsCallable: Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, IsLive: false); |
501 | DefinedSymbols[SymbolName] = GSym; |
502 | return GSym; |
503 | } else { |
504 | if (!PendingComdatExports[Symbol.getSectionNumber()]) |
505 | return make_error<JITLinkError>(Args: "No pending COMDAT export for symbol " + |
506 | formatv(Fmt: "{0:d}" , Vals&: SymIndex)); |
507 | |
508 | return exportCOMDATSymbol(SymIndex, SymbolName, Symbol); |
509 | } |
510 | } |
511 | |
512 | if (Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC || |
513 | Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL) { |
514 | const object::coff_aux_section_definition *Definition = |
515 | Symbol.getSectionDefinition(); |
516 | if (!Definition || !isComdatSection(Section)) { |
517 | // Handle typical static symbol |
518 | return &G->addDefinedSymbol( |
519 | Content&: *B, Offset: Symbol.getValue(), Name: SymbolName, Size: 0, L: Linkage::Strong, S: Scope::Local, |
520 | IsCallable: Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, IsLive: false); |
521 | } |
522 | if (Definition->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { |
523 | auto Target = Definition->getNumber(IsBigObj: Symbol.isBigObj()); |
524 | auto GSym = &G->addDefinedSymbol( |
525 | Content&: *B, Offset: Symbol.getValue(), Name: SymbolName, Size: 0, L: Linkage::Strong, S: Scope::Local, |
526 | IsCallable: Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, IsLive: false); |
527 | getGraphBlock(SecIndex: Target)->addEdge(K: Edge::KeepAlive, Offset: 0, Target&: *GSym, Addend: 0); |
528 | return GSym; |
529 | } |
530 | if (PendingComdatExports[Symbol.getSectionNumber()]) |
531 | return make_error<JITLinkError>( |
532 | Args: "COMDAT export request already exists before symbol " + |
533 | formatv(Fmt: "{0:d}" , Vals&: SymIndex)); |
534 | return createCOMDATExportRequest(SymIndex, Symbol, Definition); |
535 | } |
536 | return make_error<JITLinkError>(Args: "Unsupported storage class " + |
537 | formatv(Fmt: "{0:d}" , Vals: Symbol.getStorageClass()) + |
538 | " in symbol " + formatv(Fmt: "{0:d}" , Vals&: SymIndex)); |
539 | } |
540 | |
541 | // COMDAT handling: |
542 | // When IMAGE_SCN_LNK_COMDAT flag is set in the flags of a section, |
543 | // the section is called a COMDAT section. It contains two symbols |
544 | // in a sequence that specifes the behavior. First symbol is the section |
545 | // symbol which contains the size and name of the section. It also contains |
546 | // selection type that specifies how duplicate of the symbol is handled. |
547 | // Second symbol is COMDAT symbol which usually defines the external name and |
548 | // data type. |
549 | // |
550 | // Since two symbols always come in a specific order, we initiate pending COMDAT |
551 | // export request when we encounter the first symbol and actually exports it |
552 | // when we process the second symbol. |
553 | // |
554 | // Process the first symbol of COMDAT sequence. |
555 | Expected<Symbol *> COFFLinkGraphBuilder::createCOMDATExportRequest( |
556 | COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol, |
557 | const object::coff_aux_section_definition *Definition) { |
558 | Linkage L = Linkage::Strong; |
559 | switch (Definition->Selection) { |
560 | case COFF::IMAGE_COMDAT_SELECT_NODUPLICATES: { |
561 | L = Linkage::Strong; |
562 | break; |
563 | } |
564 | case COFF::IMAGE_COMDAT_SELECT_ANY: { |
565 | L = Linkage::Weak; |
566 | break; |
567 | } |
568 | case COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH: |
569 | case COFF::IMAGE_COMDAT_SELECT_SAME_SIZE: { |
570 | // FIXME: Implement size/content validation when LinkGraph is able to |
571 | // handle this. |
572 | L = Linkage::Weak; |
573 | break; |
574 | } |
575 | case COFF::IMAGE_COMDAT_SELECT_LARGEST: { |
576 | // FIXME: Support IMAGE_COMDAT_SELECT_LARGEST properly when LinkGraph is |
577 | // able to handle this. |
578 | LLVM_DEBUG({ |
579 | dbgs() << " " << SymIndex |
580 | << ": Partially supported IMAGE_COMDAT_SELECT_LARGEST was used" |
581 | " in section " |
582 | << Symbol.getSectionNumber() << " (size: " << Definition->Length |
583 | << ")\n" ; |
584 | }); |
585 | L = Linkage::Weak; |
586 | break; |
587 | } |
588 | case COFF::IMAGE_COMDAT_SELECT_NEWEST: { |
589 | // Even link.exe doesn't support this selection properly. |
590 | return make_error<JITLinkError>( |
591 | Args: "IMAGE_COMDAT_SELECT_NEWEST is not supported." ); |
592 | } |
593 | default: { |
594 | return make_error<JITLinkError>(Args: "Invalid comdat selection type: " + |
595 | formatv(Fmt: "{0:d}" , Vals: Definition->Selection)); |
596 | } |
597 | } |
598 | PendingComdatExports[Symbol.getSectionNumber()] = {.SymbolIndex: SymIndex, .Linkage: L, |
599 | .Size: Definition->Length}; |
600 | return nullptr; |
601 | } |
602 | |
603 | // Process the second symbol of COMDAT sequence. |
604 | Expected<Symbol *> |
605 | COFFLinkGraphBuilder::exportCOMDATSymbol(COFFSymbolIndex SymIndex, |
606 | StringRef SymbolName, |
607 | object::COFFSymbolRef Symbol) { |
608 | Block *B = getGraphBlock(SecIndex: Symbol.getSectionNumber()); |
609 | auto &PendingComdatExport = PendingComdatExports[Symbol.getSectionNumber()]; |
610 | // NOTE: ComdatDef->Length is the size of "section" not size of symbol. |
611 | // We use zero symbol size to not reach out of bound of block when symbol |
612 | // offset is non-zero. |
613 | auto GSym = &G->addDefinedSymbol( |
614 | Content&: *B, Offset: Symbol.getValue(), Name: SymbolName, Size: 0, L: PendingComdatExport->Linkage, |
615 | S: Scope::Default, IsCallable: Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, |
616 | IsLive: false); |
617 | LLVM_DEBUG({ |
618 | dbgs() << " " << SymIndex |
619 | << ": Exporting COMDAT graph symbol for COFF symbol \"" << SymbolName |
620 | << "\" in section " << Symbol.getSectionNumber() << "\n" ; |
621 | dbgs() << " " << *GSym << "\n" ; |
622 | }); |
623 | setGraphSymbol(SecIndex: Symbol.getSectionNumber(), SymIndex: PendingComdatExport->SymbolIndex, |
624 | Sym&: *GSym); |
625 | DefinedSymbols[SymbolName] = GSym; |
626 | PendingComdatExport = std::nullopt; |
627 | return GSym; |
628 | } |
629 | |
630 | } // namespace jitlink |
631 | } // namespace llvm |
632 | |