Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp
35271 views
//=--------- COFFLinkGraphBuilder.cpp - COFF LinkGraph builder ----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Generic COFF LinkGraph building code.9//10//===----------------------------------------------------------------------===//11#include "COFFLinkGraphBuilder.h"1213#define DEBUG_TYPE "jitlink"1415static const char *CommonSectionName = "__common";1617namespace llvm {18namespace jitlink {1920static Triple createTripleWithCOFFFormat(Triple T) {21T.setObjectFormat(Triple::COFF);22return T;23}2425COFFLinkGraphBuilder::COFFLinkGraphBuilder(26const object::COFFObjectFile &Obj, Triple TT, SubtargetFeatures Features,27LinkGraph::GetEdgeKindNameFunction GetEdgeKindName)28: Obj(Obj), G(std::make_unique<LinkGraph>(29Obj.getFileName().str(), createTripleWithCOFFFormat(TT),30std::move(Features), getPointerSize(Obj),31getEndianness(Obj), std::move(GetEdgeKindName))) {32LLVM_DEBUG({33dbgs() << "Created COFFLinkGraphBuilder for \"" << Obj.getFileName()34<< "\"\n";35});36}3738COFFLinkGraphBuilder::~COFFLinkGraphBuilder() = default;3940unsigned41COFFLinkGraphBuilder::getPointerSize(const object::COFFObjectFile &Obj) {42return Obj.getBytesInAddress();43}4445llvm::endianness46COFFLinkGraphBuilder::getEndianness(const object::COFFObjectFile &Obj) {47return Obj.isLittleEndian() ? llvm::endianness::little48: llvm::endianness::big;49}5051uint64_t COFFLinkGraphBuilder::getSectionSize(const object::COFFObjectFile &Obj,52const object::coff_section *Sec) {53// Consider the difference between executable form and object form.54// More information is inside COFFObjectFile::getSectionSize55if (Obj.getDOSHeader())56return std::min(Sec->VirtualSize, Sec->SizeOfRawData);57return Sec->SizeOfRawData;58}5960uint64_t61COFFLinkGraphBuilder::getSectionAddress(const object::COFFObjectFile &Obj,62const object::coff_section *Section) {63return Section->VirtualAddress + Obj.getImageBase();64}6566bool COFFLinkGraphBuilder::isComdatSection(67const object::coff_section *Section) {68return Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT;69}7071Section &COFFLinkGraphBuilder::getCommonSection() {72if (!CommonSection)73CommonSection = &G->createSection(CommonSectionName,74orc::MemProt::Read | orc::MemProt::Write);75return *CommonSection;76}7778Expected<std::unique_ptr<LinkGraph>> COFFLinkGraphBuilder::buildGraph() {79if (!Obj.isRelocatableObject())80return make_error<JITLinkError>("Object is not a relocatable COFF file");8182if (auto Err = graphifySections())83return std::move(Err);8485if (auto Err = graphifySymbols())86return std::move(Err);8788if (auto Err = addRelocations())89return std::move(Err);9091return std::move(G);92}9394StringRef95COFFLinkGraphBuilder::getCOFFSectionName(COFFSectionIndex SectionIndex,96const object::coff_section *Sec,97object::COFFSymbolRef Sym) {98switch (SectionIndex) {99case COFF::IMAGE_SYM_UNDEFINED: {100if (Sym.getValue())101return "(common)";102else103return "(external)";104}105case COFF::IMAGE_SYM_ABSOLUTE:106return "(absolute)";107case COFF::IMAGE_SYM_DEBUG: {108// Used with .file symbol109return "(debug)";110}111default: {112// Non reserved regular section numbers113if (Expected<StringRef> SecNameOrErr = Obj.getSectionName(Sec))114return *SecNameOrErr;115}116}117return "";118}119120Error COFFLinkGraphBuilder::graphifySections() {121LLVM_DEBUG(dbgs() << " Creating graph sections...\n");122123GraphBlocks.resize(Obj.getNumberOfSections() + 1);124// For each section...125for (COFFSectionIndex SecIndex = 1;126SecIndex <= static_cast<COFFSectionIndex>(Obj.getNumberOfSections());127SecIndex++) {128Expected<const object::coff_section *> Sec = Obj.getSection(SecIndex);129if (!Sec)130return Sec.takeError();131132StringRef SectionName;133if (Expected<StringRef> SecNameOrErr = Obj.getSectionName(*Sec))134SectionName = *SecNameOrErr;135136// FIXME: Skip debug info sections137if (SectionName == ".voltbl") {138LLVM_DEBUG({139dbgs() << " "140<< "Skipping section \"" << SectionName << "\"\n";141});142continue;143}144145LLVM_DEBUG({146dbgs() << " "147<< "Creating section for \"" << SectionName << "\"\n";148});149150// Get the section's memory protection flags.151orc::MemProt Prot = orc::MemProt::Read;152if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE)153Prot |= orc::MemProt::Exec;154if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_READ)155Prot |= orc::MemProt::Read;156if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_WRITE)157Prot |= orc::MemProt::Write;158159// Look for existing sections first.160auto *GraphSec = G->findSectionByName(SectionName);161if (!GraphSec) {162GraphSec = &G->createSection(SectionName, Prot);163if ((*Sec)->Characteristics & COFF::IMAGE_SCN_LNK_REMOVE)164GraphSec->setMemLifetime(orc::MemLifetime::NoAlloc);165}166if (GraphSec->getMemProt() != Prot)167return make_error<JITLinkError>("MemProt should match");168169Block *B = nullptr;170if ((*Sec)->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)171B = &G->createZeroFillBlock(172*GraphSec, getSectionSize(Obj, *Sec),173orc::ExecutorAddr(getSectionAddress(Obj, *Sec)),174(*Sec)->getAlignment(), 0);175else {176ArrayRef<uint8_t> Data;177if (auto Err = Obj.getSectionContents(*Sec, Data))178return Err;179180auto CharData = ArrayRef<char>(181reinterpret_cast<const char *>(Data.data()), Data.size());182183if (SectionName == getDirectiveSectionName())184if (auto Err = handleDirectiveSection(185StringRef(CharData.data(), CharData.size())))186return Err;187188B = &G->createContentBlock(189*GraphSec, CharData, orc::ExecutorAddr(getSectionAddress(Obj, *Sec)),190(*Sec)->getAlignment(), 0);191}192193setGraphBlock(SecIndex, B);194}195196return Error::success();197}198199Error COFFLinkGraphBuilder::graphifySymbols() {200LLVM_DEBUG(dbgs() << " Creating graph symbols...\n");201202SymbolSets.resize(Obj.getNumberOfSections() + 1);203PendingComdatExports.resize(Obj.getNumberOfSections() + 1);204GraphSymbols.resize(Obj.getNumberOfSymbols());205206for (COFFSymbolIndex SymIndex = 0;207SymIndex < static_cast<COFFSymbolIndex>(Obj.getNumberOfSymbols());208SymIndex++) {209Expected<object::COFFSymbolRef> Sym = Obj.getSymbol(SymIndex);210if (!Sym)211return Sym.takeError();212213StringRef SymbolName;214if (Expected<StringRef> SymNameOrErr = Obj.getSymbolName(*Sym))215SymbolName = *SymNameOrErr;216217COFFSectionIndex SectionIndex = Sym->getSectionNumber();218const object::coff_section *Sec = nullptr;219220if (!COFF::isReservedSectionNumber(SectionIndex)) {221auto SecOrErr = Obj.getSection(SectionIndex);222if (!SecOrErr)223return make_error<JITLinkError>(224"Invalid COFF section number:" + formatv("{0:d}: ", SectionIndex) +225" (" + toString(SecOrErr.takeError()) + ")");226Sec = *SecOrErr;227}228229// Create jitlink symbol230jitlink::Symbol *GSym = nullptr;231if (Sym->isFileRecord())232LLVM_DEBUG({233dbgs() << " " << SymIndex << ": Skipping FileRecord symbol \""234<< SymbolName << "\" in "235<< getCOFFSectionName(SectionIndex, Sec, *Sym)236<< " (index: " << SectionIndex << ") \n";237});238else if (Sym->isUndefined()) {239GSym = createExternalSymbol(SymIndex, SymbolName, *Sym, Sec);240} else if (Sym->isWeakExternal()) {241auto *WeakExternal = Sym->getAux<object::coff_aux_weak_external>();242COFFSymbolIndex TagIndex = WeakExternal->TagIndex;243uint32_t Characteristics = WeakExternal->Characteristics;244WeakExternalRequests.push_back(245{SymIndex, TagIndex, Characteristics, SymbolName});246} else {247Expected<jitlink::Symbol *> NewGSym =248createDefinedSymbol(SymIndex, SymbolName, *Sym, Sec);249if (!NewGSym)250return NewGSym.takeError();251GSym = *NewGSym;252if (GSym) {253LLVM_DEBUG({254dbgs() << " " << SymIndex255<< ": Creating defined graph symbol for COFF symbol \""256<< SymbolName << "\" in "257<< getCOFFSectionName(SectionIndex, Sec, *Sym)258<< " (index: " << SectionIndex << ") \n";259dbgs() << " " << *GSym << "\n";260});261}262}263264// Register the symbol265if (GSym)266setGraphSymbol(SectionIndex, SymIndex, *GSym);267SymIndex += Sym->getNumberOfAuxSymbols();268}269270if (auto Err = flushWeakAliasRequests())271return Err;272273if (auto Err = handleAlternateNames())274return Err;275276if (auto Err = calculateImplicitSizeOfSymbols())277return Err;278279return Error::success();280}281282Error COFFLinkGraphBuilder::handleDirectiveSection(StringRef Str) {283auto Parsed = DirectiveParser.parse(Str);284if (!Parsed)285return Parsed.takeError();286for (auto *Arg : *Parsed) {287StringRef S = Arg->getValue();288switch (Arg->getOption().getID()) {289case COFF_OPT_alternatename: {290StringRef From, To;291std::tie(From, To) = S.split('=');292if (From.empty() || To.empty())293return make_error<JITLinkError>(294"Invalid COFF /alternatename directive");295AlternateNames[From] = To;296break;297}298case COFF_OPT_incl: {299auto DataCopy = G->allocateContent(S);300StringRef StrCopy(DataCopy.data(), DataCopy.size());301ExternalSymbols[StrCopy] = &G->addExternalSymbol(StrCopy, 0, false);302ExternalSymbols[StrCopy]->setLive(true);303break;304}305case COFF_OPT_export:306break;307default: {308LLVM_DEBUG({309dbgs() << "Unknown coff directive: " << Arg->getSpelling() << "\n";310});311break;312}313}314}315return Error::success();316}317318Error COFFLinkGraphBuilder::flushWeakAliasRequests() {319// Export the weak external symbols and alias it320for (auto &WeakExternal : WeakExternalRequests) {321if (auto *Target = getGraphSymbol(WeakExternal.Target)) {322Expected<object::COFFSymbolRef> AliasSymbol =323Obj.getSymbol(WeakExternal.Alias);324if (!AliasSymbol)325return AliasSymbol.takeError();326327// FIXME: IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY and328// IMAGE_WEAK_EXTERN_SEARCH_LIBRARY are handled in the same way.329Scope S =330WeakExternal.Characteristics == COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS331? Scope::Default332: Scope::Local;333334auto NewSymbol =335createAliasSymbol(WeakExternal.SymbolName, Linkage::Weak, S, *Target);336if (!NewSymbol)337return NewSymbol.takeError();338setGraphSymbol(AliasSymbol->getSectionNumber(), WeakExternal.Alias,339**NewSymbol);340LLVM_DEBUG({341dbgs() << " " << WeakExternal.Alias342<< ": Creating weak external symbol for COFF symbol \""343<< WeakExternal.SymbolName << "\" in section "344<< AliasSymbol->getSectionNumber() << "\n";345dbgs() << " " << **NewSymbol << "\n";346});347} else348return make_error<JITLinkError>("Weak symbol alias requested but actual "349"symbol not found for symbol " +350formatv("{0:d}", WeakExternal.Alias));351}352return Error::success();353}354355Error COFFLinkGraphBuilder::handleAlternateNames() {356for (auto &KeyValue : AlternateNames)357if (DefinedSymbols.count(KeyValue.second) &&358ExternalSymbols.count(KeyValue.first)) {359auto *Target = DefinedSymbols[KeyValue.second];360auto *Alias = ExternalSymbols[KeyValue.first];361G->makeDefined(*Alias, Target->getBlock(), Target->getOffset(),362Target->getSize(), Linkage::Weak, Scope::Local, false);363}364return Error::success();365}366367Symbol *COFFLinkGraphBuilder::createExternalSymbol(368COFFSymbolIndex SymIndex, StringRef SymbolName,369object::COFFSymbolRef Symbol, const object::coff_section *Section) {370if (!ExternalSymbols.count(SymbolName))371ExternalSymbols[SymbolName] =372&G->addExternalSymbol(SymbolName, Symbol.getValue(), false);373374LLVM_DEBUG({375dbgs() << " " << SymIndex376<< ": Creating external graph symbol for COFF symbol \""377<< SymbolName << "\" in "378<< getCOFFSectionName(Symbol.getSectionNumber(), Section, Symbol)379<< " (index: " << Symbol.getSectionNumber() << ") \n";380});381return ExternalSymbols[SymbolName];382}383384Expected<Symbol *> COFFLinkGraphBuilder::createAliasSymbol(StringRef SymbolName,385Linkage L, Scope S,386Symbol &Target) {387if (!Target.isDefined()) {388// FIXME: Support this when there's a way to handle this.389return make_error<JITLinkError>("Weak external symbol with external "390"symbol as alternative not supported.");391}392return &G->addDefinedSymbol(Target.getBlock(), Target.getOffset(), SymbolName,393Target.getSize(), L, S, Target.isCallable(),394false);395}396397// In COFF, most of the defined symbols don't contain the size information.398// Hence, we calculate the "implicit" size of symbol by taking the delta of399// offsets of consecutive symbols within a block. We maintain a balanced tree400// set of symbols sorted by offset per each block in order to achieve401// logarithmic time complexity of sorted symbol insertion. Symbol is inserted to402// the set once it's processed in graphifySymbols. In this function, we iterate403// each collected symbol in sorted order and calculate the implicit size.404Error COFFLinkGraphBuilder::calculateImplicitSizeOfSymbols() {405for (COFFSectionIndex SecIndex = 1;406SecIndex <= static_cast<COFFSectionIndex>(Obj.getNumberOfSections());407SecIndex++) {408auto &SymbolSet = SymbolSets[SecIndex];409if (SymbolSet.empty())410continue;411jitlink::Block *B = getGraphBlock(SecIndex);412orc::ExecutorAddrDiff LastOffset = B->getSize();413orc::ExecutorAddrDiff LastDifferentOffset = B->getSize();414orc::ExecutorAddrDiff LastSize = 0;415for (auto It = SymbolSet.rbegin(); It != SymbolSet.rend(); It++) {416orc::ExecutorAddrDiff Offset = It->first;417jitlink::Symbol *Symbol = It->second;418orc::ExecutorAddrDiff CandSize;419// Last offset can be same when aliasing happened420if (Symbol->getOffset() == LastOffset)421CandSize = LastSize;422else423CandSize = LastOffset - Offset;424425LLVM_DEBUG({426if (Offset + Symbol->getSize() > LastDifferentOffset)427dbgs() << " Overlapping symbol range generated for the following "428"symbol:"429<< "\n"430<< " " << *Symbol << "\n";431});432(void)LastDifferentOffset;433if (LastOffset != Offset)434LastDifferentOffset = Offset;435LastSize = CandSize;436LastOffset = Offset;437if (Symbol->getSize()) {438// Non empty symbol can happen in COMDAT symbol.439// We don't consider the possibility of overlapping symbol range that440// could be introduced by disparity between inferred symbol size and441// defined symbol size because symbol size information is currently only442// used by jitlink-check where we have control to not make overlapping443// ranges.444continue;445}446447LLVM_DEBUG({448if (!CandSize)449dbgs() << " Empty implicit symbol size generated for the following "450"symbol:"451<< "\n"452<< " " << *Symbol << "\n";453});454455Symbol->setSize(CandSize);456}457}458return Error::success();459}460461Expected<Symbol *> COFFLinkGraphBuilder::createDefinedSymbol(462COFFSymbolIndex SymIndex, StringRef SymbolName,463object::COFFSymbolRef Symbol, const object::coff_section *Section) {464if (Symbol.isCommon()) {465// FIXME: correct alignment466return &G->addDefinedSymbol(467G->createZeroFillBlock(getCommonSection(), Symbol.getValue(),468orc::ExecutorAddr(), Symbol.getValue(), 0),4690, SymbolName, Symbol.getValue(), Linkage::Strong, Scope::Default,470false, false);471}472if (Symbol.isAbsolute())473return &G->addAbsoluteSymbol(SymbolName,474orc::ExecutorAddr(Symbol.getValue()), 0,475Linkage::Strong, Scope::Local, false);476477if (llvm::COFF::isReservedSectionNumber(Symbol.getSectionNumber()))478return make_error<JITLinkError>(479"Reserved section number used in regular symbol " +480formatv("{0:d}", SymIndex));481482Block *B = getGraphBlock(Symbol.getSectionNumber());483if (!B) {484LLVM_DEBUG({485dbgs() << " " << SymIndex486<< ": Skipping graph symbol since section was not created for "487"COFF symbol \""488<< SymbolName << "\" in section " << Symbol.getSectionNumber()489<< "\n";490});491return nullptr;492}493494if (Symbol.isExternal()) {495// This is not a comdat sequence, export the symbol as it is496if (!isComdatSection(Section)) {497auto GSym = &G->addDefinedSymbol(498*B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Default,499Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false);500DefinedSymbols[SymbolName] = GSym;501return GSym;502} else {503if (!PendingComdatExports[Symbol.getSectionNumber()])504return make_error<JITLinkError>("No pending COMDAT export for symbol " +505formatv("{0:d}", SymIndex));506507return exportCOMDATSymbol(SymIndex, SymbolName, Symbol);508}509}510511if (Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC ||512Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL) {513const object::coff_aux_section_definition *Definition =514Symbol.getSectionDefinition();515if (!Definition || !isComdatSection(Section)) {516// Handle typical static symbol517return &G->addDefinedSymbol(518*B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Local,519Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false);520}521if (Definition->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {522auto Target = Definition->getNumber(Symbol.isBigObj());523auto GSym = &G->addDefinedSymbol(524*B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Local,525Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false);526getGraphBlock(Target)->addEdge(Edge::KeepAlive, 0, *GSym, 0);527return GSym;528}529if (PendingComdatExports[Symbol.getSectionNumber()])530return make_error<JITLinkError>(531"COMDAT export request already exists before symbol " +532formatv("{0:d}", SymIndex));533return createCOMDATExportRequest(SymIndex, Symbol, Definition);534}535return make_error<JITLinkError>("Unsupported storage class " +536formatv("{0:d}", Symbol.getStorageClass()) +537" in symbol " + formatv("{0:d}", SymIndex));538}539540// COMDAT handling:541// When IMAGE_SCN_LNK_COMDAT flag is set in the flags of a section,542// the section is called a COMDAT section. It contains two symbols543// in a sequence that specifes the behavior. First symbol is the section544// symbol which contains the size and name of the section. It also contains545// selection type that specifies how duplicate of the symbol is handled.546// Second symbol is COMDAT symbol which usually defines the external name and547// data type.548//549// Since two symbols always come in a specific order, we initiate pending COMDAT550// export request when we encounter the first symbol and actually exports it551// when we process the second symbol.552//553// Process the first symbol of COMDAT sequence.554Expected<Symbol *> COFFLinkGraphBuilder::createCOMDATExportRequest(555COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol,556const object::coff_aux_section_definition *Definition) {557Linkage L = Linkage::Strong;558switch (Definition->Selection) {559case COFF::IMAGE_COMDAT_SELECT_NODUPLICATES: {560L = Linkage::Strong;561break;562}563case COFF::IMAGE_COMDAT_SELECT_ANY: {564L = Linkage::Weak;565break;566}567case COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH:568case COFF::IMAGE_COMDAT_SELECT_SAME_SIZE: {569// FIXME: Implement size/content validation when LinkGraph is able to570// handle this.571L = Linkage::Weak;572break;573}574case COFF::IMAGE_COMDAT_SELECT_LARGEST: {575// FIXME: Support IMAGE_COMDAT_SELECT_LARGEST properly when LinkGraph is576// able to handle this.577LLVM_DEBUG({578dbgs() << " " << SymIndex579<< ": Partially supported IMAGE_COMDAT_SELECT_LARGEST was used"580" in section "581<< Symbol.getSectionNumber() << " (size: " << Definition->Length582<< ")\n";583});584L = Linkage::Weak;585break;586}587case COFF::IMAGE_COMDAT_SELECT_NEWEST: {588// Even link.exe doesn't support this selection properly.589return make_error<JITLinkError>(590"IMAGE_COMDAT_SELECT_NEWEST is not supported.");591}592default: {593return make_error<JITLinkError>("Invalid comdat selection type: " +594formatv("{0:d}", Definition->Selection));595}596}597PendingComdatExports[Symbol.getSectionNumber()] = {SymIndex, L,598Definition->Length};599return nullptr;600}601602// Process the second symbol of COMDAT sequence.603Expected<Symbol *>604COFFLinkGraphBuilder::exportCOMDATSymbol(COFFSymbolIndex SymIndex,605StringRef SymbolName,606object::COFFSymbolRef Symbol) {607Block *B = getGraphBlock(Symbol.getSectionNumber());608auto &PendingComdatExport = PendingComdatExports[Symbol.getSectionNumber()];609// NOTE: ComdatDef->Length is the size of "section" not size of symbol.610// We use zero symbol size to not reach out of bound of block when symbol611// offset is non-zero.612auto GSym = &G->addDefinedSymbol(613*B, Symbol.getValue(), SymbolName, 0, PendingComdatExport->Linkage,614Scope::Default, Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION,615false);616LLVM_DEBUG({617dbgs() << " " << SymIndex618<< ": Exporting COMDAT graph symbol for COFF symbol \"" << SymbolName619<< "\" in section " << Symbol.getSectionNumber() << "\n";620dbgs() << " " << *GSym << "\n";621});622setGraphSymbol(Symbol.getSectionNumber(), PendingComdatExport->SymbolIndex,623*GSym);624DefinedSymbols[SymbolName] = GSym;625PendingComdatExport = std::nullopt;626return GSym;627}628629} // namespace jitlink630} // namespace llvm631632633