Index: llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h =================================================================== --- llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h +++ llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h @@ -1,4 +1,4 @@ -//===--- ELF_x86_64.h - JIT link functions for ELF/x86-64 ---*- C++ -*-===// +//===--- COFF_x86_64.h - JIT link functions for COFF/x86-64 ---*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// jit-link functions for ELF/x86-64. +// jit-link functions for COFF/x86-64. // //===----------------------------------------------------------------------===// Index: llvm/lib/ExecutionEngine/JITLink/COFF.cpp =================================================================== --- llvm/lib/ExecutionEngine/JITLink/COFF.cpp +++ llvm/lib/ExecutionEngine/JITLink/COFF.cpp @@ -27,27 +27,96 @@ namespace llvm { namespace jitlink { +static StringRef getMachineName(uint16_t Machine) { + switch (Machine) { + case COFF::IMAGE_FILE_MACHINE_I386: + return "i386"; + case COFF::IMAGE_FILE_MACHINE_AMD64: + return "x86_64"; + case COFF::IMAGE_FILE_MACHINE_ARMNT: + return "ARM"; + case COFF::IMAGE_FILE_MACHINE_ARM64: + return "ARM64"; + default: + return "unknown"; + } +} + Expected> createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer) { - StringRef Buffer = ObjectBuffer.getBuffer(); - /* - std::error_code EC; - if (Buffer.size() < sizeof(object::coff_file_header)) - return make_error("Truncated COFF buffer"); - - Expected TargetMachineArch = readTargetMachineArch(Buffer); - if (!TargetMachineArch) - return TargetMachineArch.takeError();*/ - /* - switch (*TargetMachineArch) { - case ELF::EM_X86_64: - return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer); + StringRef Data = ObjectBuffer.getBuffer(); + + // Check magic + auto Magic = identify_magic(ObjectBuffer.getBuffer()); + if (Magic != file_magic::coff_object) + return make_error("Invalid COFF buffer"); + + if (Data.size() < sizeof(object::coff_file_header)) + return make_error("Truncated COFF buffer"); + + uint64_t CurPtr = 0; + bool IsPE = false; + + // Check if this is a PE/COFF file. + if (Data.size() >= sizeof(object::dos_header) + sizeof(COFF::PEMagic)) { + const auto *DH = + reinterpret_cast(Data.data() + CurPtr); + if (DH->Magic[0] == 'M' && DH->Magic[1] == 'Z') { + // Check the PE magic bytes. ("PE\0\0") + CurPtr = DH->AddressOfNewExeHeader; + if (memcmp(Data.data() + CurPtr, COFF::PEMagic, sizeof(COFF::PEMagic)) != + 0) { + return make_error("Incorrect PE magic"); + } + CurPtr += sizeof(COFF::PEMagic); + IsPE = true; + } + } + if (Data.size() < CurPtr + sizeof(object::coff_file_header)) + return make_error("Truncated COFF buffer"); + + const object::coff_file_header *COFFHeader = + reinterpret_cast(Data.data() + CurPtr); + const object::coff_bigobj_file_header *COFFBigObjHeader = nullptr; + + // Deal with bigobj file + if (!IsPE && COFFHeader->Machine == COFF::IMAGE_FILE_MACHINE_UNKNOWN && + COFFHeader->NumberOfSections == uint16_t(0xffff) && + Data.size() >= sizeof(object::coff_bigobj_file_header)) { + if (Data.size() < sizeof(object::coff_file_header)) { + return make_error("Truncated COFF buffer"); + } + COFFBigObjHeader = + reinterpret_cast(Data.data() + + CurPtr); + + // Verify that we are dealing with bigobj. + if (COFFBigObjHeader->Version >= COFF::BigObjHeader::MinBigObjectVersion && + std::memcmp(COFFBigObjHeader->UUID, COFF::BigObjMagic, + sizeof(COFF::BigObjMagic)) == 0) { + COFFHeader = nullptr; + CurPtr += sizeof(object::coff_bigobj_file_header); + } else + COFFBigObjHeader = nullptr; + } + + uint16_t Machine = + COFFHeader ? COFFHeader->Machine : COFFBigObjHeader->Machine; + LLVM_DEBUG({ + dbgs() << "jitLink_COFF: PE = " << (IsPE ? "yes" : "no") + << ", bigobj = " << (COFFBigObjHeader ? "yes" : "no") + << ", identifier = \"" << ObjectBuffer.getBufferIdentifier() << "\" " + << "machine = " << getMachineName(Machine) << "\n"; + }); + + switch (Machine) { + case COFF::IMAGE_FILE_MACHINE_AMD64: + return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer); default: - return make_error( - "Unsupported target machine architecture in COFF object " + - ObjectBuffer.getBufferIdentifier()); - }*/ - return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer); + return make_error( + "Unsupported target machine architecture in COFF object " + + ObjectBuffer.getBufferIdentifier() + ": " + getMachineName(Machine)); + } } void link_COFF(std::unique_ptr G, Index: llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h =================================================================== --- llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h +++ llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h @@ -49,9 +49,12 @@ Error graphifySections(); Error graphifySymbols(); - void setGraphSymbol(COFFSymbolIndex SymIndex, Symbol &Sym) { + void setGraphSymbol(COFFSectionIndex SecIndex, COFFSymbolIndex SymIndex, + Symbol &Sym) { assert(!GraphSymbols[SymIndex] && "Duplicate symbol at index"); GraphSymbols[SymIndex] = &Sym; + if (!COFF::isReservedSectionNumber(SecIndex)) + SymbolSets[SecIndex].insert({Sym.getOffset(), &Sym}); } Symbol *getGraphSymbol(COFFSymbolIndex SymIndex) const { @@ -77,7 +80,7 @@ /// Traverse all matching relocation records in the given section. The handler /// function Func should be callable with this signature: - /// Error(const COFF::relocation&, + /// Error(const object::RelocationRef&, /// const object::SectionRef&, Section &) /// template @@ -101,25 +104,53 @@ } private: - static uint64_t getSectionAddress(const object::COFFObjectFile &Obj, - const object::coff_section *Section); - static uint64_t getSectionSize(const object::COFFObjectFile &Obj, - const object::coff_section *Section); - static bool isComdatSection(const object::coff_section *Section); - static unsigned getPointerSize(const object::COFFObjectFile &Obj); - static support::endianness getEndianness(const object::COFFObjectFile &Obj); + // The first symbol of COMDAT sequence. + // This symbol contains the actual definition of the symbol and proper linkage + // mode specified by the selection type. This symbol will be exported when + // second symbol of COMDAT sequence is processed which contains the external + // name of the symbol. + struct ComdatLeader { + COFFSymbolIndex Index; + Linkage Linkage; + }; + std::vector> ComdatLeaders; + // This represents a pending request to create a weak external symbol with a + // name. struct WeakAliasRequest { COFFSymbolIndex Alias; COFFSymbolIndex Target; StringRef SymbolName; }; + std::vector WeakAliasRequests; + + // Per COFF section jitlink symbol set sorted by offset. + // Used for calculating implicit size of defined symbols. + using SymbolSet = std::set>; + std::vector SymbolSets; Section &getCommonSection(); + Expected createDefinedSymbol(COFFSymbolIndex SymIndex, StringRef SymbolName, object::COFFSymbolRef Symbol, const object::coff_section *Section); + Expected createCOMDATLeaderSymbol( + COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol, + const object::coff_aux_section_definition *Definition); + Expected exportCOMDATSymbol(COFFSymbolIndex SymIndex, + StringRef SymbolName, + object::COFFSymbolRef Symbol); + Error flushWeakAliasRequests(); + Error calculateImplicitSizeOfSymbols(); + + static uint64_t getSectionAddress(const object::COFFObjectFile &Obj, + const object::coff_section *Section); + static uint64_t getSectionSize(const object::COFFObjectFile &Obj, + const object::coff_section *Section); + static bool isComdatSection(const object::coff_section *Section); + static unsigned getPointerSize(const object::COFFObjectFile &Obj); + static support::endianness getEndianness(const object::COFFObjectFile &Obj); const object::COFFObjectFile &Obj; std::unique_ptr G; @@ -127,8 +158,6 @@ Section *CommonSection = nullptr; std::vector GraphBlocks; std::vector GraphSymbols; - std::vector>> ComdatLeaders; - std::vector WeakAliases; }; template @@ -138,7 +167,7 @@ auto COFFRelSect = Obj.getCOFFSection(RelSec); - // Target sections have names in valid ELF object files. + // Target sections have names in valid COFF object files. Expected Name = Obj.getSectionName(COFFRelSect); if (!Name) return Name.takeError(); Index: llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp =================================================================== --- llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp +++ llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp @@ -27,7 +27,7 @@ Obj(Obj) { LLVM_DEBUG({ dbgs() << "Created COFFLinkGraphBuilder for \"" << Obj.getFileName() - << "\""; + << "\"\n"; }); } @@ -35,7 +35,7 @@ unsigned COFFLinkGraphBuilder::getPointerSize(const object::COFFObjectFile &Obj) { - return Obj.is64() ? 8 : 4; + return Obj.getBytesInAddress(); } support::endianness @@ -102,22 +102,17 @@ if (Expected SecNameOrErr = Obj.getSectionName(*Sec)) SectionName = *SecNameOrErr; - // Avoid loading zero-sized COFF sections. - /*bool HasContent = getSectionSize(Obj, *Sec) > 0; bool IsDiscardable = (*Sec)->Characteristics & (COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_LNK_INFO); - dbgs() << getSectionSize(Obj, *Sec) << "\n"; - if (!HasContent || IsDiscardable) { - LLVM_DEBUG( - dbgs() << " " << SecIndex << ": \"" << SectionName - << "\" is either empty or discardable: " - "No graph section will be created.\n" - ); - continue; - }*/ + if (IsDiscardable) { + LLVM_DEBUG(dbgs() << " " << SecIndex << ": \"" << SectionName + << "\" is discardable: " + "No graph section will be created.\n"); + continue; + } - // FIXME: Possibly skip debug info sections + // FIXME: Skip debug info sections LLVM_DEBUG({ dbgs() << " " @@ -137,7 +132,8 @@ auto *GraphSec = G->findSectionByName(SectionName); if (!GraphSec) GraphSec = &G->createSection(SectionName, Prot); - assert(GraphSec->getMemProt() == Prot && "MemProt should match"); + if (GraphSec->getMemProt() != Prot) + return make_error("MemProt should match"); Block *B = nullptr; if ((*Sec)->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) @@ -168,7 +164,9 @@ LLVM_DEBUG(dbgs() << " Creating graph symbols...\n"); ComdatLeaders.resize(Obj.getNumberOfSections() + 1); + SymbolSets.resize(Obj.getNumberOfSections() + 1); GraphSymbols.resize(Obj.getNumberOfSymbols()); + for (COFFSymbolIndex SymIndex = 0; SymIndex < Obj.getNumberOfSymbols(); SymIndex++) { Expected Sym = Obj.getSymbol(SymIndex); @@ -179,7 +177,7 @@ if (Expected SymNameOrErr = Obj.getSymbolName(*Sym)) SymbolName = *SymNameOrErr; - const COFFSectionIndex SectionIndex = Sym->getSectionNumber(); + COFFSectionIndex SectionIndex = Sym->getSectionNumber(); const object::coff_section *Sec = nullptr; // Get section name @@ -191,9 +189,10 @@ SectionName = "(external)"; } else if (SectionIndex == COFF::IMAGE_SYM_ABSOLUTE) SectionName = "(absolute)"; - else if (SectionIndex == COFF::IMAGE_SYM_DEBUG) + else if (SectionIndex == COFF::IMAGE_SYM_DEBUG) { + // Used with .file symbol SectionName = "(debug)"; - else { + } else { // Non reserved regular section number Expected SecOrErr = Obj.getSection(SectionIndex); @@ -205,30 +204,36 @@ Sec = *SecOrErr; } + // Create jitlink symbol jitlink::Symbol *GSym = nullptr; if (Sym->isFileRecord()) LLVM_DEBUG({ - dbgs() << " " << SymIndex << ": Skipping FileRecord symbol \"" - << SymbolName << "\" in " << SectionName << "\n"; + dbgs() << " " << SymIndex << ": Skipping FileRecord symbol \"" + << SymbolName << "\" in " << SectionName + << " (index: " << SectionIndex << ") \n"; }); else if (Sym->isUndefined()) { LLVM_DEBUG({ - dbgs() << " " << SymIndex + dbgs() << " " << SymIndex << ": Creating external graph symbol for COFF symbol \"" - << SymbolName << "\" in " << SectionName << "\n"; + << SymbolName << "\" in " << SectionName + << " (index: " << SectionIndex << ") \n"; }); GSym = &G->addExternalSymbol(SymbolName, Sym->getValue(), Linkage::Strong); } else if (Sym->isWeakExternal()) { - const COFFSymbolIndex TagIndex = + COFFSymbolIndex TagIndex = Sym->getAux()->TagIndex; - WeakAliases.push_back({SymIndex, TagIndex, SymbolName}); + assert(Sym->getAux()->Characteristics != + COFF::IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY && + "IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY is not supported."); + WeakAliasRequests.push_back({SymIndex, TagIndex, SymbolName}); } else { Expected NewGSym = createDefinedSymbol(SymIndex, SymbolName, *Sym, Sec); if (!NewGSym) { LLVM_DEBUG({ - dbgs() << " " << SymIndex + dbgs() << " " << SymIndex << ": Not creating graph symbol for COFF symbol \"" << SymbolName << "\" in " << SectionName << " with an error : " << NewGSym.takeError() << "\n"; @@ -237,38 +242,123 @@ continue; } GSym = *NewGSym; - if (GSym) + if (GSym) { LLVM_DEBUG({ - dbgs() << " " << SymIndex + dbgs() << " " << SymIndex << ": Creating defined graph symbol for COFF symbol \"" - << SymbolName << "\" in " << SectionName << "\n"; + << SymbolName << "\" in " << SectionName + << " (index: " << SectionIndex << ") \n"; + dbgs() << " " << *GSym << "\n"; }); + } } + // Register the symbol if (GSym) - setGraphSymbol(SymIndex, *GSym); + setGraphSymbol(SectionIndex, SymIndex, *GSym); SymIndex += Sym->getNumberOfAuxSymbols(); } + if (auto Err = flushWeakAliasRequests()) + return Err; + + if (auto Err = calculateImplicitSizeOfSymbols()) + return Err; + + return Error::success(); +} + +Error COFFLinkGraphBuilder::flushWeakAliasRequests() { // Export the weak external symbols and alias it - for (auto &WeakAlias : WeakAliases) { - if (auto Target = getGraphSymbol(WeakAlias.Target)) { - assert((llvm::count_if(G->defined_symbols(), - [&](const jitlink::Symbol *Sym) { - return Sym->getName() == WeakAlias.SymbolName; - }) == 0) && - "Duplicate defined symbol"); - - Target->setLinkage(Linkage::Weak); - Target->setScope(Scope::Default); - Target->setName(WeakAlias.SymbolName); - setGraphSymbol(WeakAlias.Alias, *Target); + for (auto &WeakAlias : WeakAliasRequests) { + if (auto *Target = getGraphSymbol(WeakAlias.Target)) { + Expected AliasSymbol = + Obj.getSymbol(WeakAlias.Alias); + if (!AliasSymbol) + return AliasSymbol.takeError(); + + // FIXME: Support this when there's a way to handle this. + if (!Target->isDefined()) + return make_error("Weak external symbol with external " + "symbol as alternative not supported."); + + jitlink::Symbol *NewSymbol = &G->addDefinedSymbol( + Target->getBlock(), Target->getOffset(), WeakAlias.SymbolName, + Target->getSize(), Linkage::Weak, Scope::Default, + Target->isCallable(), false); + setGraphSymbol(AliasSymbol->getSectionNumber(), WeakAlias.Alias, + *NewSymbol); + LLVM_DEBUG({ + dbgs() << " " << WeakAlias.Alias + << ": Creating weak external symbol for COFF symbol \"" + << WeakAlias.SymbolName << "\" in section " + << AliasSymbol->getSectionNumber() << "\n"; + dbgs() << " " << *NewSymbol << "\n"; + }); } else return make_error("Weak symbol alias requested but actual " - "symbol not defined in symbol " + - WeakAlias.Alias); + "symbol not found for symbol " + + formatv("{0:d}", WeakAlias.Alias)); } + return Error::success(); +} + +// 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 <= Obj.getNumberOfSections(); + SecIndex++) { + auto &SymbolSet = SymbolSets[SecIndex]; + 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"; + }); + 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; + } + if (!CandSize) + LLVM_DEBUG({ + dbgs() << " Empty implicit symbol size generated for the following " + "symbol:" + << "\n" + << " " << *Symbol << "\n"; + }); + + Symbol->setSize(CandSize); + } + } return Error::success(); } @@ -286,23 +376,11 @@ orc::ExecutorAddr(Symbol.getValue()), 0, Linkage::Strong, Scope::Local, false); - const COFFSectionIndex SectionIndex = Symbol.getSectionNumber(); - if (llvm::COFF::isReservedSectionNumber(SectionIndex)) + if (llvm::COFF::isReservedSectionNumber(Symbol.getSectionNumber())) return make_error( "Reserved section number used in regular symbol " + formatv("{0:d}", SymIndex)); - // 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 - // selectino type that specifies how duplicates 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 store the first - // symbol to ComdatLeaders table and emits it as a external symbol when - // we encounter the COMDAT symbol. Block *B = getGraphBlock(Symbol.getSectionNumber()); if (Symbol.isExternal()) { // This is not a comdat sequence, export the symbol as it is @@ -310,83 +388,118 @@ return &G->addDefinedSymbol( *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Default, Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); - else { - // This is the second symbol of a COMDAT sequence that contains the name - // and type of the symbol. - auto Leader = *ComdatLeaders[SectionIndex]; - const COFFSymbolIndex TargetIndex = Leader.first; - const Linkage L = Leader.second; - jitlink::Symbol *Target = getGraphSymbol(TargetIndex); - assert(Target && "COMDAT leaader is invalid."); - assert((llvm::count_if(G->defined_symbols(), - [&](const jitlink::Symbol *Sym) { - return Sym->getName() == SymbolName; - }) == 0) && - "Duplicate defined symbol"); - Target->setName(SymbolName); - Target->setLinkage(L); - Target->setCallable(Symbol.getComplexType() == - COFF::IMAGE_SYM_DTYPE_FUNCTION); - Target->setScope(Scope::Default); - setGraphSymbol(SymIndex, *Target); - return nullptr; - } + else + return exportCOMDATSymbol(SymIndex, SymbolName, Symbol); } if (Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC) { - const object::coff_aux_section_definition *Def = + const object::coff_aux_section_definition *Definition = Symbol.getSectionDefinition(); - if (!Def || !isComdatSection(Section) || ComdatLeaders[SectionIndex]) { + if (!Definition || !isComdatSection(Section) || + ComdatLeaders[Symbol.getSectionNumber()]) { // 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 (Def->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { - // Dead strip should handle this implictly - return nullptr; - } - Linkage L = Linkage::Strong; - jitlink::Symbol *GSym = &G->addAnonymousSymbol(*B, Symbol.getValue(), - Def->Length, false, false); - switch (Def->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 when LinkGraph is able to - // handle this. - return make_error( - "IMAGE_COMDAT_SELECT_LARGEST is not supported."); - } - case COFF::IMAGE_COMDAT_SELECT_NEWEST: { - // Even link.exe doesn't support this selection properly. - return make_error( - "IMAGE_COMDAT_SELECT_NEWEST is not supported."); - } - default: { - return make_error("Invalid comdat selection type: " + - formatv("{0:d}", Def->Selection)); - } + if (Definition->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + // FIXME: don't dead strip this when parent section is alive + return &G->addDefinedSymbol( + *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Local, + Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); } - ComdatLeaders[SectionIndex] = {SymIndex, L}; - return GSym; + + return createCOMDATLeaderSymbol(SymIndex, Symbol, Definition); } return make_error("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 store the first +// symbol to ComdatLeaders table and emits it as a external symbol when +// we encounter the COMDAT symbol. + +// Process the first symbol of COMDAT sequence. +Expected COFFLinkGraphBuilder::createCOMDATLeaderSymbol( + COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol, + const object::coff_aux_section_definition *Definition) { + Block *B = getGraphBlock(Symbol.getSectionNumber()); + 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 when LinkGraph is able to + // handle this. + return make_error( + "IMAGE_COMDAT_SELECT_LARGEST is not supported."); + } + case COFF::IMAGE_COMDAT_SELECT_NEWEST: { + // Even link.exe doesn't support this selection properly. + return make_error( + "IMAGE_COMDAT_SELECT_NEWEST is not supported."); + } + default: { + return make_error("Invalid comdat selection type: " + + formatv("{0:d}", Definition->Selection)); + } + } + ComdatLeaders[Symbol.getSectionNumber()] = {SymIndex, L}; + return &G->addAnonymousSymbol(*B, Symbol.getValue(), Definition->Length, + false, false); +} + +// Process the second symbol of COMDAT sequence. +Expected +COFFLinkGraphBuilder::exportCOMDATSymbol(COFFSymbolIndex SymIndex, + StringRef SymbolName, + object::COFFSymbolRef Symbol) { + auto Leader = *ComdatLeaders[Symbol.getSectionNumber()]; + COFFSymbolIndex TargetIndex = Leader.Index; + Linkage L = Leader.Linkage; + jitlink::Symbol *Target = getGraphSymbol(TargetIndex); + assert(Target && "COMDAT leaader is invalid."); + assert((llvm::count_if(G->defined_symbols(), + [&](const jitlink::Symbol *Sym) { + return Sym->getName() == SymbolName; + }) == 0) && + "Duplicate defined symbol"); + Target->setName(SymbolName); + Target->setLinkage(L); + Target->setCallable(Symbol.getComplexType() == + COFF::IMAGE_SYM_DTYPE_FUNCTION); + Target->setScope(Scope::Default); + LLVM_DEBUG({ + dbgs() << " " << SymIndex + << ": Exporting COMDAT graph symbol for COFF symbol \"" << SymbolName + << "\" in section " << Symbol.getSectionNumber() << "\n"; + dbgs() << " " << *Target << "\n"; + }); + return Target; +} + } // namespace jitlink } // namespace llvm \ No newline at end of file Index: llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp =================================================================== --- llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp +++ llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp @@ -1,4 +1,4 @@ -//===----- COFF_x86_64.cpp - JIT linker implementation for ELF/aarch64 ----===// +//===----- COFF_x86_64.cpp - JIT linker implementation for COFF/x86_64 ----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -43,8 +43,10 @@ class COFFLinkGraphBuilder_x86_64 : public COFFLinkGraphBuilder { private: + uint64_t ImageBase = 0; enum COFFX86RelocationKind : Edge::Kind { COFFAddr32NB = Edge::FirstRelocation, + COFFRel32, }; static Expected @@ -52,6 +54,8 @@ switch (Type) { case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR32NB: return COFFAddr32NB; + case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32: + return COFFRel32; } return make_error("Unsupported x86_64 relocation:" + @@ -70,12 +74,23 @@ return Error::success(); } + uint64_t getImageBase() { + if (!ImageBase) { + ImageBase = std::numeric_limits::max(); + for (const auto &Block : getGraph().blocks()) { + if (Block->getAddress().getValue()) + ImageBase = std::min(ImageBase, Block->getAddress().getValue()); + } + } + return ImageBase; + } + Error addSingleRelocation(const object::RelocationRef &Rel, const object::SectionRef &FixupSect, Block &BlockToFix) { const object::coff_relocation *COFFRel = getObject().getCOFFRelocation(Rel); - const auto SymbolIt = Rel.getSymbol(); + auto SymbolIt = Rel.getSymbol(); if (SymbolIt == getObject().symbol_end()) { return make_error( formatv("Invalid symbol index in relocation entry. " @@ -84,9 +99,8 @@ inconvertibleErrorCode()); } - const object::COFFSymbolRef COFFSymbol = - getObject().getCOFFSymbol(*SymbolIt); - const COFFSymbolIndex SymIndex = getObject().getSymbolIndex(COFFSymbol); + object::COFFSymbolRef COFFSymbol = getObject().getCOFFSymbol(*SymbolIt); + COFFSymbolIndex SymIndex = getObject().getSymbolIndex(COFFSymbol); Symbol *GraphSymbol = getGraphSymbol(SymIndex); if (!GraphSymbol) @@ -113,9 +127,13 @@ Edge::Kind Kind = Edge::Invalid; switch (*RelocKind) { - case COFFAddr32NB: { Kind = x86_64::Pointer32; + Offset -= getImageBase(); + break; + } + case COFFRel32: { + Kind = x86_64::BranchPCRel32; break; } }; @@ -131,11 +149,13 @@ return Error::success(); } - /// Return the string name of the given ELF aarch64 edge kind. + /// Return the string name of the given COFF x86_64 edge kind. const char *getCOFFX86RelocationKindName(Edge::Kind R) { switch (R) { case COFFAddr32NB: return "COFFAddr32NB"; + case COFFRel32: + return "COFFRel32"; default: return getGenericEdgeKindName(static_cast(R)); } @@ -146,6 +166,14 @@ : COFFLinkGraphBuilder(Obj, std::move(T), x86_64::getEdgeKindName) {} }; +Error buildTables_COFF_x86_64(LinkGraph &G) { + LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); + + x86_64::GOTTableManager GOT; + x86_64::PLTTableManager PLT(GOT); + visitExistingEdges(G, GOT, PLT); + return Error::success(); +} } // namespace namespace llvm { @@ -176,6 +204,12 @@ Config.PrePrunePasses.push_back(std::move(MarkLive)); else Config.PrePrunePasses.push_back(markAllSymbolsLive); + + // Add an in-place GOT/Stubs/TLSInfoEntry build pass. + Config.PostPrunePasses.push_back(buildTables_COFF_x86_64); + + // Add GOT/Stubs optimizer pass. + Config.PreFixupPasses.push_back(x86_64::optimizeGOTAndStubAccesses); } if (auto Err = Ctx->modifyPassConfig(*G, Config)) Index: llvm/lib/ExecutionEngine/Orc/Core.cpp =================================================================== --- llvm/lib/ExecutionEngine/Orc/Core.cpp +++ llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -2910,11 +2910,7 @@ #ifndef NDEBUG for (auto &KV : Symbols) { auto WeakFlags = JITSymbolFlags::Weak | JITSymbolFlags::Common; - dbgs() << KV.first << "\n"; - - dbgs() << KV.second.getFlags() << "\n"; auto I = MR.SymbolFlags.find(KV.first); - dbgs() << I->second << "\n"; assert(I != MR.SymbolFlags.end() && "Resolving symbol outside this responsibility set"); assert(!I->second.hasMaterializationSideEffectsOnly() && Index: llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp =================================================================== --- llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp +++ llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp @@ -179,24 +179,18 @@ auto SymFlags = JITSymbolFlags::fromObjectSymbol(Sym); if (!SymFlags) return SymFlags.takeError(); - + *SymFlags |= JITSymbolFlags::Exported; auto COFFSym = Obj.getCOFFSymbol(Sym); - if (!COFFSym.isUndefined() && COFFSym.isExternal()) { - *SymFlags |= JITSymbolFlags::Exported; + + // Weak external is always a function + if (COFFSym.isWeakExternal()) { + *SymFlags |= JITSymbolFlags::Callable; } I.SymbolFlags[ES.intern(*Name)] = std::move(*SymFlags); } - SymbolStringPtr InitSymbol; - for (auto &Sec : Obj.sections()) { - if (auto SecName = Sec.getName()) { - if (ELFNixPlatform::isInitializerSection(*SecName)) { - addInitSymbol(I, ES, Obj.getFileName()); - break; - } - } - } + // FIXME: handle init symbols return I; } Index: llvm/lib/Object/ObjectFile.cpp =================================================================== --- llvm/lib/Object/ObjectFile.cpp +++ llvm/lib/Object/ObjectFile.cpp @@ -120,7 +120,6 @@ const auto COFFObj = cast(this); if (COFFObj->getArch() == Triple::thumb) TheTriple.setTriple("thumbv7-windows"); - TheTriple.setObjectFormat(Triple::COFF); } else if (isXCOFF()) { // XCOFF implies AIX. TheTriple.setOS(Triple::AIX); Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_abs.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_abs.s @@ -0,0 +1,25 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check absolute symbol is created with a correct value. +# +# CHECK: Creating graph symbols... +# CHECK: 6: Creating defined graph symbol for COFF symbol "abs" in (absolute) (index: -1) +# CHECK-NEXT: 0x53 (addressable + 0x00000000): size: 0x00000000, linkage: strong, scope: local, dead - abs + + .text + .def abs; + .scl 3; + .type 0; + .endef + .globl abs +.set abs, 0x53 + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s =================================================================== --- llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s @@ -1,5 +1,7 @@ -// RUN: llvm-mc -filetype=obj -triple x86_64-windows-msvc %s -o %t -// RUN: llvm-jitlink -noexec %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -noexec %t +# +# Check a basic COFF object file loads successfully. .text .def @feat.00; Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_any.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_any.test @@ -0,0 +1,63 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a weak symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_ANY selection type. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_associative.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_associative.test @@ -0,0 +1,82 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 +# +# Check COMDAT associative symbol is emitted as local symbol. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func +# CHECK-NEXT: 5: Creating defined graph symbol for COFF symbol ".xdata" in .xdata (index: 3) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: strong, scope: local, dead - .xdata + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .xdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0100000000000000' +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .xdata + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 3433693342 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_exact_match.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_exact_match.test @@ -0,0 +1,64 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a weak symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_EXACT_MATCH selection type. +# Doesn't check the content validation. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_EXACT_MATCH + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_largest.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_largest.test @@ -0,0 +1,59 @@ +# XFAIL: * +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 +# +# Check jitlink return an error when IMAGE_COMDAT_SELECT_LARGEST selection type is encountered. +# + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_LARGEST + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_noduplicate.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_noduplicate.test @@ -0,0 +1,63 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a strong symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_NODUPLICATES selection type. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_NODUPLICATES + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_same_size.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_same_size.test @@ -0,0 +1,64 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a weak symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_SAME_SIZE selection type. +# Doesn't check the size validation. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_SAME_SIZE + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_weak.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_weak.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a COMDAT any symbol is exported as a weak symbol. +# +# CHECK: Creating graph symbols... +# CHECK: 6: Creating defined graph symbol for COFF symbol ".text" in .text (index: 4) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 8: Exporting COMDAT graph symbol for COFF symbol "func" in section 4 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + + .text + + .def func; + .scl 2; + .type 32; + .endef + .section .text,"xr",discard,func + .globl func + .p2align 4, 0x90 +func: + retq + + .def main; + .scl 2; + .type 32; + .endef + .text + .globl main + .p2align 4, 0x90 +main: + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_common_symbol.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_common_symbol.s @@ -0,0 +1,22 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a common symbol is created. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating defined graph symbol for COFF symbol "var" in (common) (index: 0) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000004, linkage: weak, scope: default, dead - var + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4 +main: + movl var(%rip), %eax + retq + + .comm var,4,2 Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_external_func.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_external_func.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs func=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check an external symbol to a functon is created. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating external graph symbol for COFF symbol "func" in (external) (index: 0) + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + callq func + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_external_var.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_external_var.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs var=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check an external symbol to a variable is created. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating external graph symbol for COFF symbol "var" in (external) (index: 0) + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + movl var(%rip), %eax + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_file_debug.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_file_debug.s @@ -0,0 +1,21 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs func=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a file debug symbol is skipped. +# +# CHECK: Creating graph symbols... +# CHECK: 8: Skipping FileRecord symbol ".file" in (debug) (index: -2) + + .text + + .file "skip_this_file_symbol" + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + callq func + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_static_var.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_static_var.s @@ -0,0 +1,24 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs var=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a local symbol is created for a static variable. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating defined graph symbol for COFF symbol "var" in .data (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: strong, scope: local, dead - var + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq + + .data + .p2align 2 +var: + .long 53 Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_weak_external.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_weak_external.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs var=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a default symbol is aliased as a weak external symbol. +# +# CHECK: Creating graph symbols... +# CHECK: 8: Creating defined graph symbol for COFF symbol ".weak.func.default.main" in .text (index: 1) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: strong, scope: default, dead - .weak.func.default.main +# CHECK-NEXT: 9: Creating defined graph symbol for COFF symbol "main" in .text (index: 1) +# CHECK-NEXT: 0x10 (block + 0x00000010): size: 0x00000000, linkage: strong, scope: default, dead - main +# CHECK-NEXT: 6: Creating weak external symbol for COFF symbol "func" in section 0 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: weak, scope: default, dead - func + + .text + + .def func; + .scl 2; + .type 32; + .endef + .weak func + .p2align 4, 0x90 +func: + retq + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_x86-64_small_pic_relocations.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_x86-64_small_pic_relocations.s @@ -0,0 +1,78 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc -triple=x86_64-windows-msvc -relax-relocations=false \ +# RUN: -position-independent -filetype=obj -o %t/coff_sm_reloc.o %s +# RUN: llvm-jitlink -noexec \ +# RUN: -slab-allocate 100Kb -slab-address 0xfff00000 -slab-page-size 4096 \ +# RUN: -abs external_data=0xdeadbeef \ +# RUN: -abs extern_out_of_range32=0x7fff00000000 \ +# RUN: -check %s %t/coff_sm_reloc.o + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq + +# Check a IMAGE_REL_AMD64_REL32 relocation to local function symbol. +# jitlink-check: decode_operand(test_rel32_func, 0) = named_func - next_pc(test_rel32_func) + .def test_rel32_func; + .scl 2; + .type 32; + .endef + .globl test_rel32_func + .p2align 4, 0x90 +test_rel32_func: + callq named_func + +# Check a IMAGE_REL_AMD64_REL32 relocation to local data symbol. +# jitlink-check: decode_operand(test_rel32_data, 4) = named_data - next_pc(test_rel32_data) + .def test_rel32_data; + .scl 2; + .type 32; + .endef + .globl test_rel32_data + .p2align 4, 0x90 +test_rel32_data: + leaq named_data(%rip), %rax + +# Check that calls to external functions out-of-range from the callsite trigger +# the generation of stubs and GOT entries. This produces a BranchPCRel32 edge, +# but STUB table manager will create a STUB sequence because external function +# is out-of-range from the callsite. +# +# jitlink-check: decode_operand(test_call_extern_out_of_range32, 0) = \ +# jitlink-check: stub_addr(coff_sm_reloc.o, extern_out_of_range32) - \ +# jitlink-check: next_pc(test_call_extern_out_of_range32) +# jitlink-check: *{8}(got_addr(coff_sm_reloc.o, extern_out_of_range32)) = \ +# jitlink-check: extern_out_of_range32 + .def test_call_extern_out_of_range32; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +test_call_extern_out_of_range32: + callq extern_out_of_range32 + retq + +# Local named data/func that is used in conjunction with other test cases + .text + .def named_func; + .scl 2; + .type 32; + .endef + .globl named_func + .p2align 4, 0x90 +named_func: + retq + + .data + .p2align 3 +named_data: + .quad 53 + Index: llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp =================================================================== --- llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp +++ llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp @@ -1,5 +1,4 @@ -//===---- llvm-jitlink-coff.cpp -- COFF parsing support for llvm-jitlink -//----===// +//===--- llvm-jitlink-coff.cpp -- COFF parsing support for llvm-jitlink ---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -160,6 +159,7 @@ return make_error("Mixed zero-fill and content sections not " "supported yet", inconvertibleErrorCode()); + if (SectionContainsZeroFill) FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr.getValue()}; else Index: llvm/tools/llvm-jitlink/llvm-jitlink.cpp =================================================================== --- llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -1085,7 +1085,7 @@ Err = P.takeError(); return; } - } else if (!TT.isOSBinFormatCOFF() && !TT.isOSBinFormatMachO()) { + } else if (TT.isOSBinFormatELF()) { if (!NoExec) ObjLayer.addPlugin(std::make_unique( ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES)))); @@ -1144,7 +1144,7 @@ if (EPC.getTargetTriple().getObjectFormat() == Triple::MachO) return registerMachOGraphInfo(*this, G); - if (EPC.getTargetTriple().getObjectFormat() == Triple::COFF) + if (EPC.getTargetTriple().isOSWindows()) return registerCOFFGraphInfo(*this, G); return make_error("Unsupported object format for GOT/stub " @@ -1255,14 +1255,17 @@ assert(!InputFiles.empty() && "InputFiles can not be empty"); for (auto InputFile : InputFiles) { auto ObjBuffer = ExitOnErr(getFile(InputFile)); - auto kk = identify_magic(ObjBuffer->getBuffer()); - switch (identify_magic(ObjBuffer->getBuffer())) { + file_magic Magic = identify_magic(ObjBuffer->getBuffer()); + switch (Magic) { + case file_magic::coff_object: case file_magic::elf_relocatable: - case file_magic::macho_object: - case file_magic::coff_object: { + case file_magic::macho_object: { auto Obj = ExitOnErr( object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); - return Obj->makeTriple(); + Triple TT = Obj->makeTriple(); + if (Magic == file_magic::coff_object) + TT.setOS(Triple::OSType::Win32); + return TT; } default: break;