| //=--------- COFFLinkGraphBuilder.cpp - COFF LinkGraph builder ----------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Generic COFF LinkGraph buliding code. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "COFFLinkGraphBuilder.h" | |
| #define DEBUG_TYPE "jitlink" | |
| static const char *CommonSectionName = "__common"; | |
| namespace llvm { | |
| namespace jitlink { | |
| static Triple createTripleWithCOFFFormat(Triple T) { | |
| T.setObjectFormat(Triple::COFF); | |
| return T; | |
| } | |
| COFFLinkGraphBuilder::COFFLinkGraphBuilder( | |
| const object::COFFObjectFile &Obj, Triple TT, | |
| LinkGraph::GetEdgeKindNameFunction GetEdgeKindName) | |
| : Obj(Obj), | |
| G(std::make_unique<LinkGraph>(Obj.getFileName().str(), | |
| createTripleWithCOFFFormat(TT), | |
| getPointerSize(Obj), getEndianness(Obj), | |
| std::move(GetEdgeKindName))) { | |
| LLVM_DEBUG({ | |
| dbgs() << "Created COFFLinkGraphBuilder for \"" << Obj.getFileName() | |
| << "\"\n"; | |
| }); | |
| } | |
| COFFLinkGraphBuilder::~COFFLinkGraphBuilder() = default; | |
| unsigned | |
| COFFLinkGraphBuilder::getPointerSize(const object::COFFObjectFile &Obj) { | |
| return Obj.getBytesInAddress(); | |
| } | |
| support::endianness | |
| COFFLinkGraphBuilder::getEndianness(const object::COFFObjectFile &Obj) { | |
| return Obj.isLittleEndian() ? support::little : support::big; | |
| } | |
| uint64_t COFFLinkGraphBuilder::getSectionSize(const object::COFFObjectFile &Obj, | |
| const object::coff_section *Sec) { | |
| // Consider the difference between executable form and object form. | |
| // More information is inside COFFObjectFile::getSectionSize | |
| if (Obj.getDOSHeader()) | |
| return std::min(Sec->VirtualSize, Sec->SizeOfRawData); | |
| return Sec->SizeOfRawData; | |
| } | |
| uint64_t | |
| COFFLinkGraphBuilder::getSectionAddress(const object::COFFObjectFile &Obj, | |
| const object::coff_section *Section) { | |
| return Section->VirtualAddress + Obj.getImageBase(); | |
| } | |
| bool COFFLinkGraphBuilder::isComdatSection( | |
| const object::coff_section *Section) { | |
| return Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT; | |
| } | |
| Section &COFFLinkGraphBuilder::getCommonSection() { | |
| if (!CommonSection) | |
| CommonSection = &G->createSection(CommonSectionName, | |
| orc::MemProt::Read | orc::MemProt::Write); | |
| return *CommonSection; | |
| } | |
| Expected<std::unique_ptr<LinkGraph>> COFFLinkGraphBuilder::buildGraph() { | |
| if (!Obj.isRelocatableObject()) | |
| return make_error<JITLinkError>("Object is not a relocatable COFF file"); | |
| if (auto Err = graphifySections()) | |
| return std::move(Err); | |
| if (auto Err = graphifySymbols()) | |
| return std::move(Err); | |
| if (auto Err = addRelocations()) | |
| return std::move(Err); | |
| return std::move(G); | |
| } | |
| StringRef | |
| COFFLinkGraphBuilder::getCOFFSectionName(COFFSectionIndex SectionIndex, | |
| const object::coff_section *Sec, | |
| object::COFFSymbolRef Sym) { | |
| switch (SectionIndex) { | |
| case COFF::IMAGE_SYM_UNDEFINED: { | |
| if (Sym.getValue()) | |
| return "(common)"; | |
| else | |
| return "(external)"; | |
| } | |
| case COFF::IMAGE_SYM_ABSOLUTE: | |
| return "(absolute)"; | |
| case COFF::IMAGE_SYM_DEBUG: { | |
| // Used with .file symbol | |
| return "(debug)"; | |
| } | |
| default: { | |
| // Non reserved regular section numbers | |
| if (Expected<StringRef> SecNameOrErr = Obj.getSectionName(Sec)) | |
| return *SecNameOrErr; | |
| } | |
| } | |
| return ""; | |
| } | |
| Error COFFLinkGraphBuilder::graphifySections() { | |
| LLVM_DEBUG(dbgs() << " Creating graph sections...\n"); | |
| GraphBlocks.resize(Obj.getNumberOfSections() + 1); | |
| // For each section... | |
| for (COFFSectionIndex SecIndex = 1; | |
| SecIndex <= static_cast<COFFSectionIndex>(Obj.getNumberOfSections()); | |
| SecIndex++) { | |
| Expected<const object::coff_section *> Sec = Obj.getSection(SecIndex); | |
| if (!Sec) | |
| return Sec.takeError(); | |
| StringRef SectionName; | |
| if (Expected<StringRef> SecNameOrErr = Obj.getSectionName(*Sec)) | |
| SectionName = *SecNameOrErr; | |
| // FIXME: Skip debug info sections | |
| LLVM_DEBUG({ | |
| dbgs() << " " | |
| << "Creating section for \"" << SectionName << "\"\n"; | |
| }); | |
| // Get the section's memory protection flags. | |
| orc::MemProt Prot = orc::MemProt::Read; | |
| if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE) | |
| Prot |= orc::MemProt::Exec; | |
| if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_READ) | |
| Prot |= orc::MemProt::Read; | |
| if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_WRITE) | |
| Prot |= orc::MemProt::Write; | |
| // Look for existing sections first. | |
| auto *GraphSec = G->findSectionByName(SectionName); | |
| if (!GraphSec) | |
| GraphSec = &G->createSection(SectionName, Prot); | |
| if (GraphSec->getMemProt() != Prot) | |
| return make_error<JITLinkError>("MemProt should match"); | |
| Block *B = nullptr; | |
| if ((*Sec)->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) | |
| B = &G->createZeroFillBlock( | |
| *GraphSec, getSectionSize(Obj, *Sec), | |
| orc::ExecutorAddr(getSectionAddress(Obj, *Sec)), | |
| (*Sec)->getAlignment(), 0); | |
| else { | |
| ArrayRef<uint8_t> Data; | |
| if (auto Err = Obj.getSectionContents(*Sec, Data)) | |
| return Err; | |
| auto CharData = ArrayRef<char>( | |
| reinterpret_cast<const char *>(Data.data()), Data.size()); | |
| if (SectionName == getDirectiveSectionName()) | |
| if (auto Err = handleDirectiveSection( | |
| StringRef(CharData.data(), CharData.size()))) | |
| return Err; | |
| B = &G->createContentBlock( | |
| *GraphSec, CharData, orc::ExecutorAddr(getSectionAddress(Obj, *Sec)), | |
| (*Sec)->getAlignment(), 0); | |
| } | |
| setGraphBlock(SecIndex, B); | |
| } | |
| return Error::success(); | |
| } | |
| Error COFFLinkGraphBuilder::graphifySymbols() { | |
| LLVM_DEBUG(dbgs() << " Creating graph symbols...\n"); | |
| SymbolSets.resize(Obj.getNumberOfSections() + 1); | |
| PendingComdatExports.resize(Obj.getNumberOfSections() + 1); | |
| GraphSymbols.resize(Obj.getNumberOfSymbols()); | |
| for (COFFSymbolIndex SymIndex = 0; | |
| SymIndex < static_cast<COFFSymbolIndex>(Obj.getNumberOfSymbols()); | |
| SymIndex++) { | |
| Expected<object::COFFSymbolRef> Sym = Obj.getSymbol(SymIndex); | |
| if (!Sym) | |
| return Sym.takeError(); | |
| StringRef SymbolName; | |
| if (Expected<StringRef> SymNameOrErr = Obj.getSymbolName(*Sym)) | |
| SymbolName = *SymNameOrErr; | |
| COFFSectionIndex SectionIndex = Sym->getSectionNumber(); | |
| const object::coff_section *Sec = nullptr; | |
| if (!COFF::isReservedSectionNumber(SectionIndex)) { | |
| auto SecOrErr = Obj.getSection(SectionIndex); | |
| if (!SecOrErr) | |
| return make_error<JITLinkError>( | |
| "Invalid COFF section number:" + formatv("{0:d}: ", SectionIndex) + | |
| " (" + toString(SecOrErr.takeError()) + ")"); | |
| Sec = *SecOrErr; | |
| } | |
| // Create jitlink symbol | |
| jitlink::Symbol *GSym = nullptr; | |
| if (Sym->isFileRecord()) | |
| LLVM_DEBUG({ | |
| dbgs() << " " << SymIndex << ": Skipping FileRecord symbol \"" | |
| << SymbolName << "\" in " | |
| << getCOFFSectionName(SectionIndex, Sec, *Sym) | |
| << " (index: " << SectionIndex << ") \n"; | |
| }); | |
| else if (Sym->isUndefined()) { | |
| GSym = createExternalSymbol(SymIndex, SymbolName, *Sym, Sec); | |
| } else if (Sym->isWeakExternal()) { | |
| auto *WeakExternal = Sym->getAux<object::coff_aux_weak_external>(); | |
| COFFSymbolIndex TagIndex = WeakExternal->TagIndex; | |
| uint32_t Characteristics = WeakExternal->Characteristics; | |
| WeakExternalRequests.push_back( | |
| {SymIndex, TagIndex, Characteristics, SymbolName}); | |
| } else { | |
| Expected<jitlink::Symbol *> NewGSym = | |
| createDefinedSymbol(SymIndex, SymbolName, *Sym, Sec); | |
| if (!NewGSym) | |
| return NewGSym.takeError(); | |
| GSym = *NewGSym; | |
| if (GSym) { | |
| LLVM_DEBUG({ | |
| dbgs() << " " << SymIndex | |
| << ": Creating defined graph symbol for COFF symbol \"" | |
| << SymbolName << "\" in " | |
| << getCOFFSectionName(SectionIndex, Sec, *Sym) | |
| << " (index: " << SectionIndex << ") \n"; | |
| dbgs() << " " << *GSym << "\n"; | |
| }); | |
| } | |
| } | |
| // Register the symbol | |
| if (GSym) | |
| setGraphSymbol(SectionIndex, SymIndex, *GSym); | |
| SymIndex += Sym->getNumberOfAuxSymbols(); | |
| } | |
| if (auto Err = flushWeakAliasRequests()) | |
| return Err; | |
| if (auto Err = handleAlternateNames()) | |
| return Err; | |
| if (auto Err = calculateImplicitSizeOfSymbols()) | |
| return Err; | |
| return Error::success(); | |
| } | |
| Error COFFLinkGraphBuilder::handleDirectiveSection(StringRef Str) { | |
| auto Parsed = DirectiveParser.parse(Str); | |
| if (!Parsed) | |
| return Parsed.takeError(); | |
| for (auto *Arg : *Parsed) { | |
| StringRef S = Arg->getValue(); | |
| switch (Arg->getOption().getID()) { | |
| case COFF_OPT_alternatename: { | |
| StringRef From, To; | |
| std::tie(From, To) = S.split('='); | |
| if (From.empty() || To.empty()) | |
| return make_error<JITLinkError>( | |
| "Invalid COFF /alternatename directive"); | |
| AlternateNames[From] = To; | |
| break; | |
| } | |
| case COFF_OPT_incl: { | |
| auto DataCopy = G->allocateString(S); | |
| StringRef StrCopy(DataCopy.data(), DataCopy.size()); | |
| ExternalSymbols[StrCopy] = &G->addExternalSymbol(StrCopy, 0, false); | |
| ExternalSymbols[StrCopy]->setLive(true); | |
| break; | |
| } | |
| case COFF_OPT_export: | |
| break; | |
| default: { | |
| LLVM_DEBUG({ | |
| dbgs() << "Unknown coff directive: " << Arg->getSpelling() << "\n"; | |
| }); | |
| break; | |
| } | |
| } | |
| } | |
| return Error::success(); | |
| } | |
| Error COFFLinkGraphBuilder::flushWeakAliasRequests() { | |
| // Export the weak external symbols and alias it | |
| for (auto &WeakExternal : WeakExternalRequests) { | |
| if (auto *Target = getGraphSymbol(WeakExternal.Target)) { | |
| Expected<object::COFFSymbolRef> AliasSymbol = | |
| Obj.getSymbol(WeakExternal.Alias); | |
| if (!AliasSymbol) | |
| return AliasSymbol.takeError(); | |
| // FIXME: IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY and | |
| // IMAGE_WEAK_EXTERN_SEARCH_LIBRARY are handled in the same way. | |
| Scope S = | |
| WeakExternal.Characteristics == COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS | |
| ? Scope::Default | |
| : Scope::Local; | |
| auto NewSymbol = | |
| createAliasSymbol(WeakExternal.SymbolName, Linkage::Weak, S, *Target); | |
| if (!NewSymbol) | |
| return NewSymbol.takeError(); | |
| setGraphSymbol(AliasSymbol->getSectionNumber(), WeakExternal.Alias, | |
| **NewSymbol); | |
| LLVM_DEBUG({ | |
| dbgs() << " " << WeakExternal.Alias | |
| << ": Creating weak external symbol for COFF symbol \"" | |
| << WeakExternal.SymbolName << "\" in section " | |
| << AliasSymbol->getSectionNumber() << "\n"; | |
| dbgs() << " " << **NewSymbol << "\n"; | |
| }); | |
| } else | |
| return make_error<JITLinkError>("Weak symbol alias requested but actual " | |
| "symbol not found for symbol " + | |
| formatv("{0:d}", WeakExternal.Alias)); | |
| } | |
| return Error::success(); | |
| } | |
| Error COFFLinkGraphBuilder::handleAlternateNames() { | |
| for (auto &KeyValue : AlternateNames) | |
| if (DefinedSymbols.count(KeyValue.second) && | |
| ExternalSymbols.count(KeyValue.first)) { | |
| auto *Target = DefinedSymbols[KeyValue.second]; | |
| auto *Alias = ExternalSymbols[KeyValue.first]; | |
| G->makeDefined(*Alias, Target->getBlock(), Target->getOffset(), | |
| Target->getSize(), Linkage::Weak, Scope::Local, false); | |
| } | |
| return Error::success(); | |
| } | |
| Symbol *COFFLinkGraphBuilder::createExternalSymbol( | |
| COFFSymbolIndex SymIndex, StringRef SymbolName, | |
| object::COFFSymbolRef Symbol, const object::coff_section *Section) { | |
| if (!ExternalSymbols.count(SymbolName)) | |
| ExternalSymbols[SymbolName] = | |
| &G->addExternalSymbol(SymbolName, Symbol.getValue(), false); | |
| LLVM_DEBUG({ | |
| dbgs() << " " << SymIndex | |
| << ": Creating external graph symbol for COFF symbol \"" | |
| << SymbolName << "\" in " | |
| << getCOFFSectionName(Symbol.getSectionNumber(), Section, Symbol) | |
| << " (index: " << Symbol.getSectionNumber() << ") \n"; | |
| }); | |
| return ExternalSymbols[SymbolName]; | |
| } | |
| Expected<Symbol *> COFFLinkGraphBuilder::createAliasSymbol(StringRef SymbolName, | |
| Linkage L, Scope S, | |
| Symbol &Target) { | |
| if (!Target.isDefined()) { | |
| // FIXME: Support this when there's a way to handle this. | |
| return make_error<JITLinkError>("Weak external symbol with external " | |
| "symbol as alternative not supported."); | |
| } | |
| return &G->addDefinedSymbol(Target.getBlock(), Target.getOffset(), SymbolName, | |
| Target.getSize(), L, S, Target.isCallable(), | |
| false); | |
| } | |
| // In COFF, most of the defined symbols don't contain the size information. | |
| // Hence, we calculate the "implicit" size of symbol by taking the delta of | |
| // offsets of consecutive symbols within a block. We maintain a balanced tree | |
| // set of symbols sorted by offset per each block in order to achieve | |
| // logarithmic time complexity of sorted symbol insertion. Symbol is inserted to | |
| // the set once it's processed in graphifySymbols. In this function, we iterate | |
| // each collected symbol in sorted order and calculate the implicit size. | |
| Error COFFLinkGraphBuilder::calculateImplicitSizeOfSymbols() { | |
| for (COFFSectionIndex SecIndex = 1; | |
| SecIndex <= static_cast<COFFSectionIndex>(Obj.getNumberOfSections()); | |
| SecIndex++) { | |
| auto &SymbolSet = SymbolSets[SecIndex]; | |
| if (SymbolSet.empty()) | |
| continue; | |
| jitlink::Block *B = getGraphBlock(SecIndex); | |
| orc::ExecutorAddrDiff LastOffset = B->getSize(); | |
| orc::ExecutorAddrDiff LastDifferentOffset = B->getSize(); | |
| orc::ExecutorAddrDiff LastSize = 0; | |
| for (auto It = SymbolSet.rbegin(); It != SymbolSet.rend(); It++) { | |
| orc::ExecutorAddrDiff Offset = It->first; | |
| jitlink::Symbol *Symbol = It->second; | |
| orc::ExecutorAddrDiff CandSize; | |
| // Last offset can be same when aliasing happened | |
| if (Symbol->getOffset() == LastOffset) | |
| CandSize = LastSize; | |
| else | |
| CandSize = LastOffset - Offset; | |
| LLVM_DEBUG({ | |
| if (Offset + Symbol->getSize() > LastDifferentOffset) | |
| dbgs() << " Overlapping symbol range generated for the following " | |
| "symbol:" | |
| << "\n" | |
| << " " << *Symbol << "\n"; | |
| }); | |
| (void)LastDifferentOffset; | |
| if (LastOffset != Offset) | |
| LastDifferentOffset = Offset; | |
| LastSize = CandSize; | |
| LastOffset = Offset; | |
| if (Symbol->getSize()) { | |
| // Non empty symbol can happen in COMDAT symbol. | |
| // We don't consider the possibility of overlapping symbol range that | |
| // could be introduced by disparity between inferred symbol size and | |
| // defined symbol size because symbol size information is currently only | |
| // used by jitlink-check where we have control to not make overlapping | |
| // ranges. | |
| continue; | |
| } | |
| LLVM_DEBUG({ | |
| if (!CandSize) | |
| dbgs() << " Empty implicit symbol size generated for the following " | |
| "symbol:" | |
| << "\n" | |
| << " " << *Symbol << "\n"; | |
| }); | |
| Symbol->setSize(CandSize); | |
| } | |
| } | |
| return Error::success(); | |
| } | |
| Expected<Symbol *> COFFLinkGraphBuilder::createDefinedSymbol( | |
| COFFSymbolIndex SymIndex, StringRef SymbolName, | |
| object::COFFSymbolRef Symbol, const object::coff_section *Section) { | |
| if (Symbol.isCommon()) { | |
| // FIXME: correct alignment | |
| return &G->addDefinedSymbol( | |
| G->createZeroFillBlock(getCommonSection(), Symbol.getValue(), | |
| orc::ExecutorAddr(), Symbol.getValue(), 0), | |
| 0, SymbolName, Symbol.getValue(), Linkage::Strong, Scope::Default, | |
| false, false); | |
| } | |
| if (Symbol.isAbsolute()) | |
| return &G->addAbsoluteSymbol(SymbolName, | |
| orc::ExecutorAddr(Symbol.getValue()), 0, | |
| Linkage::Strong, Scope::Local, false); | |
| if (llvm::COFF::isReservedSectionNumber(Symbol.getSectionNumber())) | |
| return make_error<JITLinkError>( | |
| "Reserved section number used in regular symbol " + | |
| formatv("{0:d}", SymIndex)); | |
| Block *B = getGraphBlock(Symbol.getSectionNumber()); | |
| if (!B) { | |
| LLVM_DEBUG({ | |
| dbgs() << " " << SymIndex | |
| << ": Skipping graph symbol since section was not created for " | |
| "COFF symbol \"" | |
| << SymbolName << "\" in section " << Symbol.getSectionNumber() | |
| << "\n"; | |
| }); | |
| return nullptr; | |
| } | |
| if (Symbol.isExternal()) { | |
| // This is not a comdat sequence, export the symbol as it is | |
| if (!isComdatSection(Section)) { | |
| auto GSym = &G->addDefinedSymbol( | |
| *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Default, | |
| Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); | |
| DefinedSymbols[SymbolName] = GSym; | |
| return GSym; | |
| } else { | |
| if (!PendingComdatExports[Symbol.getSectionNumber()]) | |
| return make_error<JITLinkError>("No pending COMDAT export for symbol " + | |
| formatv("{0:d}", SymIndex)); | |
| return exportCOMDATSymbol(SymIndex, SymbolName, Symbol); | |
| } | |
| } | |
| if (Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC || | |
| Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL) { | |
| const object::coff_aux_section_definition *Definition = | |
| Symbol.getSectionDefinition(); | |
| if (!Definition || !isComdatSection(Section)) { | |
| // Handle typical static symbol | |
| return &G->addDefinedSymbol( | |
| *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Local, | |
| Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); | |
| } | |
| if (Definition->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { | |
| auto Target = Definition->getNumber(Symbol.isBigObj()); | |
| auto GSym = &G->addDefinedSymbol( | |
| *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Local, | |
| Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); | |
| getGraphBlock(Target)->addEdge(Edge::KeepAlive, 0, *GSym, 0); | |
| return GSym; | |
| } | |
| if (PendingComdatExports[Symbol.getSectionNumber()]) | |
| return make_error<JITLinkError>( | |
| "COMDAT export request already exists before symbol " + | |
| formatv("{0:d}", SymIndex)); | |
| return createCOMDATExportRequest(SymIndex, Symbol, Definition); | |
| } | |
| return make_error<JITLinkError>("Unsupported storage class " + | |
| formatv("{0:d}", Symbol.getStorageClass()) + | |
| " in symbol " + formatv("{0:d}", SymIndex)); | |
| } | |
| // COMDAT handling: | |
| // When IMAGE_SCN_LNK_COMDAT flag is set in the flags of a section, | |
| // the section is called a COMDAT section. It contains two symbols | |
| // in a sequence that specifes the behavior. First symbol is the section | |
| // symbol which contains the size and name of the section. It also contains | |
| // selection type that specifies how duplicate of the symbol is handled. | |
| // Second symbol is COMDAT symbol which usually defines the external name and | |
| // data type. | |
| // | |
| // Since two symbols always come in a specific order, we initiate pending COMDAT | |
| // export request when we encounter the first symbol and actually exports it | |
| // when we process the second symbol. | |
| // | |
| // Process the first symbol of COMDAT sequence. | |
| Expected<Symbol *> COFFLinkGraphBuilder::createCOMDATExportRequest( | |
| COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol, | |
| const object::coff_aux_section_definition *Definition) { | |
| Linkage L = Linkage::Strong; | |
| switch (Definition->Selection) { | |
| case COFF::IMAGE_COMDAT_SELECT_NODUPLICATES: { | |
| L = Linkage::Strong; | |
| break; | |
| } | |
| case COFF::IMAGE_COMDAT_SELECT_ANY: { | |
| L = Linkage::Weak; | |
| break; | |
| } | |
| case COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH: | |
| case COFF::IMAGE_COMDAT_SELECT_SAME_SIZE: { | |
| // FIXME: Implement size/content validation when LinkGraph is able to | |
| // handle this. | |
| L = Linkage::Weak; | |
| break; | |
| } | |
| case COFF::IMAGE_COMDAT_SELECT_LARGEST: { | |
| // FIXME: Support IMAGE_COMDAT_SELECT_LARGEST properly when LinkGraph is | |
| // able to handle this. | |
| LLVM_DEBUG({ | |
| dbgs() << " " << SymIndex | |
| << ": Partially supported IMAGE_COMDAT_SELECT_LARGEST was used" | |
| " in section " | |
| << Symbol.getSectionNumber() << " (size: " << Definition->Length | |
| << ")\n"; | |
| }); | |
| L = Linkage::Weak; | |
| break; | |
| } | |
| case COFF::IMAGE_COMDAT_SELECT_NEWEST: { | |
| // Even link.exe doesn't support this selection properly. | |
| return make_error<JITLinkError>( | |
| "IMAGE_COMDAT_SELECT_NEWEST is not supported."); | |
| } | |
| default: { | |
| return make_error<JITLinkError>("Invalid comdat selection type: " + | |
| formatv("{0:d}", Definition->Selection)); | |
| } | |
| } | |
| PendingComdatExports[Symbol.getSectionNumber()] = {SymIndex, L, | |
| Definition->Length}; | |
| return nullptr; | |
| } | |
| // Process the second symbol of COMDAT sequence. | |
| Expected<Symbol *> | |
| COFFLinkGraphBuilder::exportCOMDATSymbol(COFFSymbolIndex SymIndex, | |
| StringRef SymbolName, | |
| object::COFFSymbolRef Symbol) { | |
| Block *B = getGraphBlock(Symbol.getSectionNumber()); | |
| auto &PendingComdatExport = PendingComdatExports[Symbol.getSectionNumber()]; | |
| // NOTE: ComdatDef->Legnth is the size of "section" not size of symbol. | |
| // We use zero symbol size to not reach out of bound of block when symbol | |
| // offset is non-zero. | |
| auto GSym = &G->addDefinedSymbol( | |
| *B, Symbol.getValue(), SymbolName, 0, PendingComdatExport->Linkage, | |
| Scope::Default, Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, | |
| false); | |
| LLVM_DEBUG({ | |
| dbgs() << " " << SymIndex | |
| << ": Exporting COMDAT graph symbol for COFF symbol \"" << SymbolName | |
| << "\" in section " << Symbol.getSectionNumber() << "\n"; | |
| dbgs() << " " << *GSym << "\n"; | |
| }); | |
| setGraphSymbol(Symbol.getSectionNumber(), PendingComdatExport->SymbolIndex, | |
| *GSym); | |
| DefinedSymbols[SymbolName] = GSym; | |
| PendingComdatExport = std::nullopt; | |
| return GSym; | |
| } | |
| } // namespace jitlink | |
| } // namespace llvm |