diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -1,3 +1,7 @@ +set(LLVM_TARGET_DEFINITIONS COFFOptions.td) +tablegen(LLVM COFFOptions.inc -gen-opt-parser-defs) +add_public_tablegen_target(JITLinkTableGen) + add_llvm_component_library(LLVMJITLink DWARFRecordSectionSplitter.cpp EHFrameSupport.cpp @@ -23,6 +27,7 @@ # COFF COFF.cpp + COFFDirectiveParser.cpp COFFLinkGraphBuilder.cpp COFF_x86_64.cpp @@ -36,10 +41,12 @@ DEPENDS intrinsics_gen + JITLinkTableGen LINK_COMPONENTS BinaryFormat Object + Option OrcTargetProcess Support ) diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.h b/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.h new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.h @@ -0,0 +1,48 @@ +//===--- COFFDirectiveParser.h - JITLink coff directive parser --*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// MSVC COFF directive parser +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_COFFDIRECTIVEPARSER_H +#define LLVM_EXECUTIONENGINE_JITLINK_COFFDIRECTIVEPARSER_H + +#include "llvm/ADT/Triple.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/StringSaver.h" + +namespace llvm { +namespace jitlink { + +enum { + COFF_OPT_INVALID = 0, +#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) COFF_OPT_##ID, +#include "COFFOptions.inc" +#undef OPTION +}; + +/// Parser for the MSVC specific preprocessor directives. +/// https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160 +class COFFDirectiveParser { +public: + Expected parse(StringRef Str); + +private: + llvm::BumpPtrAllocator bAlloc; + llvm::StringSaver saver{bAlloc}; +}; + +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_COFFDIRECTIVEPARSER_H \ No newline at end of file diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.cpp b/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/COFFDirectiveParser.cpp @@ -0,0 +1,75 @@ +//===-- COFFDirectiveParser.cpp - JITLink coff directive parser --*- C++ -*===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// MSVC COFF directive parser +// +//===----------------------------------------------------------------------===// + +#include "COFFDirectiveParser.h" + +using namespace llvm; +using namespace jitlink; + +#define DEBUG_TYPE "jitlink" + +// Create prefix string literals used in Options.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "COFFOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in COFFOptions.td +static const opt::OptTable::Info infoTable[] = { +#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ + {X1, \ + X2, \ + X10, \ + X11, \ + COFF_OPT_##ID, \ + opt::Option::KIND##Class, \ + X9, \ + X8, \ + COFF_OPT_##GROUP, \ + COFF_OPT_##ALIAS, \ + X7, \ + X12}, +#include "COFFOptions.inc" +#undef OPTION +}; + +class COFFOptTable : public opt::OptTable { +public: + COFFOptTable() : OptTable(infoTable, true) {} +}; + +static COFFOptTable optTable; + +Expected COFFDirectiveParser::parse(StringRef Str) { + SmallVector Tokens; + SmallVector Buffer; + cl::TokenizeWindowsCommandLineNoCopy(Str, saver, Tokens); + for (StringRef Tok : Tokens) { + bool HasNul = Tok.end() != Str.end() && Tok.data()[Tok.size()] == '\0'; + Buffer.push_back(HasNul ? Tok.data() : saver.save(Tok).data()); + } + + unsigned missingIndex; + unsigned missingCount; + + auto Result = optTable.ParseArgs(Buffer, missingIndex, missingCount); + + if (missingCount) + return make_error(Twine("COFF directive parsing failed: ") + + Result.getArgString(missingIndex) + + " missing argument"); + LLVM_DEBUG({ + for (auto *arg : Result.filtered(COFF_OPT_UNKNOWN)) + dbgs() << "Unknown coff option argument: " << arg->getAsString(Result) + << "\n"; + }); + return Result; +} diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h --- a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h +++ b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h @@ -18,6 +18,7 @@ #include "llvm/ExecutionEngine/JITLink/JITLink.h" #include "llvm/Object/COFF.h" +#include "COFFDirectiveParser.h" #include "EHFrameSupportImpl.h" #include "JITLinkGeneric.h" @@ -133,6 +134,11 @@ Section &getCommonSection(); + Symbol *createExternalSymbol(COFFSymbolIndex SymIndex, StringRef SymbolName, + object::COFFSymbolRef Symbol, + const object::coff_section *Section); + Expected createAliasSymbol(StringRef SymbolName, Linkage L, Scope S, + Symbol &Target); Expected createDefinedSymbol(COFFSymbolIndex SymIndex, StringRef SymbolName, object::COFFSymbolRef Symbol, @@ -143,7 +149,10 @@ Expected exportCOMDATSymbol(COFFSymbolIndex SymIndex, StringRef SymbolName, object::COFFSymbolRef Symbol); + + Error handleDirectiveSection(StringRef Str); Error flushWeakAliasRequests(); + Error handleAlternateNames(); Error calculateImplicitSizeOfSymbols(); static uint64_t getSectionAddress(const object::COFFObjectFile &Obj, @@ -154,18 +163,22 @@ static unsigned getPointerSize(const object::COFFObjectFile &Obj); static support::endianness getEndianness(const object::COFFObjectFile &Obj); static StringRef getDLLImportStubPrefix() { return "__imp_"; } + static StringRef getDirectiveSectionName() { return ".drectve"; } StringRef getCOFFSectionName(COFFSectionIndex SectionIndex, const object::coff_section *Sec, object::COFFSymbolRef Sym); const object::COFFObjectFile &Obj; std::unique_ptr G; + COFFDirectiveParser DirectiveParser; Section *CommonSection = nullptr; std::vector GraphBlocks; std::vector GraphSymbols; + DenseMap AlternateNames; DenseMap ExternalSymbols; + DenseMap DefinedSymbols; }; template diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp --- a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp @@ -170,11 +170,16 @@ if (auto Err = Obj.getSectionContents(*Sec, Data)) return Err; + auto CharData = ArrayRef( + reinterpret_cast(Data.data()), Data.size()); + + if (SectionName == getDirectiveSectionName()) + if (auto Err = handleDirectiveSection( + StringRef(CharData.data(), CharData.size()))) + return Err; + B = &G->createContentBlock( - *GraphSec, - ArrayRef(reinterpret_cast(Data.data()), - Data.size()), - orc::ExecutorAddr(getSectionAddress(Obj, *Sec)), + *GraphSec, CharData, orc::ExecutorAddr(getSectionAddress(Obj, *Sec)), (*Sec)->getAlignment(), 0); } @@ -224,30 +229,17 @@ << " (index: " << SectionIndex << ") \n"; }); else if (Sym->isUndefined()) { - auto CreateExternalSymbol = [&](StringRef SymbolName) { - if (!ExternalSymbols.count(SymbolName)) - ExternalSymbols[SymbolName] = &G->addExternalSymbol( - SymbolName, Sym->getValue(), Linkage::Strong); - - LLVM_DEBUG({ - dbgs() << " " << SymIndex - << ": Creating external graph symbol for COFF symbol \"" - << SymbolName << "\" in " - << getCOFFSectionName(SectionIndex, Sec, *Sym) - << " (index: " << SectionIndex << ") \n"; - }); - return ExternalSymbols[SymbolName]; - }; if (SymbolName.startswith(getDLLImportStubPrefix())) { if (Sym->getValue() != 0) return make_error( "DLL import symbol has non-zero offset"); - auto ExternalSym = CreateExternalSymbol( - SymbolName.drop_front(getDLLImportStubPrefix().size())); + auto ExternalSym = createExternalSymbol( + SymIndex, SymbolName.drop_front(getDLLImportStubPrefix().size()), + *Sym, Sec); GSym = &createDLLImportEntry(SymbolName, *ExternalSym); } else - GSym = CreateExternalSymbol(SymbolName); + GSym = createExternalSymbol(SymIndex, SymbolName, *Sym, Sec); } else if (Sym->isWeakExternal()) { auto *WeakExternal = Sym->getAux(); COFFSymbolIndex TagIndex = WeakExternal->TagIndex; @@ -281,12 +273,52 @@ 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( + "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, Linkage::Strong); + 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) { @@ -303,22 +335,18 @@ ? Scope::Default : Scope::Local; - // 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(), WeakExternal.SymbolName, - Target->getSize(), Linkage::Weak, S, Target->isCallable(), false); + auto NewSymbol = + createAliasSymbol(WeakExternal.SymbolName, Linkage::Weak, S, *Target); + if (!NewSymbol) + return NewSymbol.takeError(); setGraphSymbol(AliasSymbol->getSectionNumber(), WeakExternal.Alias, - *NewSymbol); + **NewSymbol); LLVM_DEBUG({ dbgs() << " " << WeakExternal.Alias << ": Creating weak external symbol for COFF symbol \"" << WeakExternal.SymbolName << "\" in section " << AliasSymbol->getSectionNumber() << "\n"; - dbgs() << " " << *NewSymbol << "\n"; + dbgs() << " " << **NewSymbol << "\n"; }); } else return make_error("Weak symbol alias requested but actual " @@ -328,6 +356,49 @@ return Error::success(); } +Error COFFLinkGraphBuilder::handleAlternateNames() { + // FIXME: Use proper alias + 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(), Linkage::Strong); + + 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 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("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 @@ -426,10 +497,11 @@ if (Symbol.isExternal()) { // This is not a comdat sequence, export the symbol as it is if (!isComdatSection(Section)) { - - return &G->addDefinedSymbol( + 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("No pending COMDAT export for symbol " + @@ -557,8 +629,9 @@ dbgs() << " " << *Target << "\n"; }); PendingComdatExport = None; + DefinedSymbols[SymbolName] = Target; return Target; } } // namespace jitlink -} // namespace llvm +} // namespace llvm \ No newline at end of file diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFOptions.td b/llvm/lib/ExecutionEngine/JITLink/COFFOptions.td new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/COFFOptions.td @@ -0,0 +1,21 @@ +include "llvm/Option/OptParser.td" + +// link.exe accepts options starting with either a dash or a slash. + +// Flag that takes no arguments. +class F : Flag<["/", "-", "/?", "-?"], name>; + +// Flag that takes one argument after ":". +class P : + Joined<["/", "-", "/?", "-?"], name#":">; + +// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the +// flag on and using it suffixed by ":no" turns it off. +multiclass B_priv { + def "" : F; + def _no : F; +} + +def export : P<"export">; +def alternatename : P<"alternatename">; +def incl : Joined<["/", "-", "/?", "-?"], "include:">; \ No newline at end of file diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_alternatename.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_alternatename.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_alternatename.s @@ -0,0 +1,30 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink --debug-only=jitlink -noexec %t +# +# Check object with alternatename directive does not fail, because +# foo external symbol was resolved to foo_def. +# + + .text + + .def foo_def; + .scl 2; + .type 32; + .endef + .globl foo_def + .p2align 4, 0x90 +foo_def: + retq + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + callq foo + retq + +.section .drectve,"yn" +.ascii "/alternatename:foo=foo_def" diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_alternatename_fail.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_alternatename_fail.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_alternatename_fail.s @@ -0,0 +1,30 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: not llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check object without alternatename directive fails because of +# external symbol not found error. +# +# CHECK: error: Symbols not found: [ foo ] + .text + + .def foo_def; + .scl 2; + .type 32; + .endef + .globl foo_def + .p2align 4, 0x90 +foo_def: + retq + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + callq foo + retq + + + diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_include.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_include.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/X86/COFF_directive_include.s @@ -0,0 +1,22 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: not llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check an external symbol "foo" is generated and not dead-stripped +# because of include directive which turned into symbol not found error. +# +# CHECK: error: Symbols not found: [ foo ] + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq + + .section .drectve,"yn" + .ascii "/include:foo" +