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
16static const char *CommonSectionName = "__common";
17
18namespace llvm {
19namespace jitlink {
20
21static Triple createTripleWithCOFFFormat(Triple T) {
22 T.setObjectFormat(Triple::COFF);
23 return T;
24}
25
26COFFLinkGraphBuilder::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
39COFFLinkGraphBuilder::~COFFLinkGraphBuilder() = default;
40
41unsigned
42COFFLinkGraphBuilder::getPointerSize(const object::COFFObjectFile &Obj) {
43 return Obj.getBytesInAddress();
44}
45
46llvm::endianness
47COFFLinkGraphBuilder::getEndianness(const object::COFFObjectFile &Obj) {
48 return Obj.isLittleEndian() ? llvm::endianness::little
49 : llvm::endianness::big;
50}
51
52uint64_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
61uint64_t
62COFFLinkGraphBuilder::getSectionAddress(const object::COFFObjectFile &Obj,
63 const object::coff_section *Section) {
64 return Section->VirtualAddress + Obj.getImageBase();
65}
66
67bool COFFLinkGraphBuilder::isComdatSection(
68 const object::coff_section *Section) {
69 return Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT;
70}
71
72Section &COFFLinkGraphBuilder::getCommonSection() {
73 if (!CommonSection)
74 CommonSection = &G->createSection(Name: CommonSectionName,
75 Prot: orc::MemProt::Read | orc::MemProt::Write);
76 return *CommonSection;
77}
78
79Expected<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
95StringRef
96COFFLinkGraphBuilder::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
121Error 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
200Error 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
283Error 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
319Error 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
356Error 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
368Symbol *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
385Expected<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.
405Error 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
462Expected<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.
555Expected<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.
604Expected<Symbol *>
605COFFLinkGraphBuilder::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