diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index e3ff647209e72..1855969c31acd 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -200,28 +200,13 @@ static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) { } } -void LinkerDriver::addFile(InputFile *file) { - Log(ctx) << "Reading " << toString(file); - if (file->lazy) { - if (auto *f = dyn_cast(file)) - f->parseLazy(); - else - cast(file)->parseLazy(); - } else { - file->parse(); - if (auto *f = dyn_cast(file)) { - ctx.objFileInstances.push_back(f); - } else if (auto *f = dyn_cast(file)) { - if (ltoCompilationDone) { - Err(ctx) << "LTO object file " << toString(file) - << " linked in after " - "doing LTO compilation."; - } - f->symtab.bitcodeFileInstances.push_back(f); - } else if (auto *f = dyn_cast(file)) { - ctx.importFileInstances.push_back(f); - } +void LinkerDriver::addFile(InputFile *file, CmdLineArchive *inCmdLineArchive) { + if (inCmdLineArchive) { + inCmdLineArchive->addInputFile(file); // schedule for lazy parsing + return; } + Log(ctx) << "Reading " << toString(file); + file->maybeParse(); MachineTypes mt = file->getMachineType(); // The ARM64EC target must be explicitly specified and cannot be inferred. @@ -259,17 +244,29 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr mb) { } void LinkerDriver::addBuffer(std::unique_ptr mb, - bool wholeArchive, bool lazy) { + bool wholeArchive, + CmdLineArchive *inCmdLineArchive) { StringRef filename = mb->getBufferIdentifier(); MemoryBufferRef mbref = takeBuffer(std::move(mb)); + auto maybePrintWarning = [&](StringRef type, StringRef message) { + if (inCmdLineArchive) + Warn(ctx) << type << " file provided between " + << inCmdLineArchive->startLibArg << "/" + << inCmdLineArchive->endLibArg << " " << message; + }; + // File type is detected by contents, not by file extension. switch (identify_magic(mbref.getBuffer())) { case file_magic::windows_resource: + maybePrintWarning(".res", "will not be lazy"); resources.push_back(mbref); break; case file_magic::archive: + // FIXME: We could later support --start-lib/--end-lib groups, to allow for + // "extending" an existing archive/LIB. + maybePrintWarning(".lib/.a", "has no effect"); if (wholeArchive) { std::unique_ptr file = CHECK(Archive::create(mbref), filename + ": failed to parse archive"); @@ -284,13 +281,14 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, addFile(make(ctx, mbref)); break; case file_magic::bitcode: - addFile(BitcodeFile::create(ctx, mbref, "", 0, lazy)); + addFile(BitcodeFile::create(ctx, mbref, "", 0), inCmdLineArchive); break; case file_magic::coff_object: case file_magic::coff_import_library: - addFile(ObjFile::create(ctx, mbref, lazy)); + addFile(ObjFile::create(ctx, mbref), inCmdLineArchive); break; case file_magic::pdb: + maybePrintWarning(".pdb", "will not be lazy"); addFile(make(ctx, mbref)); break; case file_magic::coff_cl_gl_object: @@ -299,6 +297,7 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, break; case file_magic::pecoff_executable: if (ctx.config.mingw) { + maybePrintWarning(".dll", "will not be lazy"); addFile(make(ctx.symtab, mbref)); break; } @@ -315,7 +314,9 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, } } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { +void LinkerDriver::enqueuePath( + StringRef path, bool wholeArchive, + std::optional> inCmdLineArchive) { auto future = std::make_shared>( createFutureForFile(std::string(path))); std::string pathStr = std::string(path); @@ -354,7 +355,8 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { else Err(ctx) << msg << "; did you mean '" << nearest << "'"; } else - ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy); + ctx.driver.addBuffer(std::move(mb), wholeArchive, + inCmdLineArchive ? **inCmdLineArchive : nullptr); }); } @@ -373,8 +375,7 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, if (magic == file_magic::coff_object) { obj = ObjFile::create(ctx, mb); } else if (magic == file_magic::bitcode) { - obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive, - /*lazy=*/false); + obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive); } else if (magic == file_magic::coff_cl_gl_object) { Err(ctx) << mb.getBufferIdentifier() << ": is not a native COFF file. Recompile without /GL?"; @@ -494,7 +495,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { break; case OPT_defaultlib: if (std::optional path = findLibIfNew(arg->getValue())) - enqueuePath(*path, false, false); + enqueuePath(*path); break; case OPT_entry: if (!arg->getValue()[0]) @@ -2172,37 +2173,78 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { return false; }; + // Store start_lib/end_lib arguments in order to render dignostics in the + // same way the flags are written on the command line. + llvm::opt::Arg *startLibArg = nullptr; + llvm::opt::Arg *endLibArg = nullptr; + auto endLibSpelling = [&]() { + return endLibArg ? endLibArg->getSpelling() + : lld::args::getOptionSpellingLikeArg( + ctx.optTable, OPT_end_lib, startLibArg, ctx.saver); + }; + // Create a list of input files. These can be given as OPT_INPUT options // and OPT_wholearchive_file options, and we also need to track OPT_start_lib // and OPT_end_lib. { llvm::TimeTraceScope timeScope2("Parse & queue inputs"); - bool inLib = false; + std::optional> inCmdLineArchive; + auto close = [&]() { + enqueueTask([=]() { + assert(inCmdLineArchive); + if (CmdLineArchive *a = **inCmdLineArchive) + a->maybeParse(); + }); + }; + for (auto *arg : args) { switch (arg->getOption().getID()) { case OPT_end_lib: - if (!inLib) + if (!inCmdLineArchive) { Err(ctx) << "stray " << arg->getSpelling(); - inLib = false; + } else { + endLibArg = arg; + close(); + inCmdLineArchive = std::nullopt; + } break; case OPT_start_lib: - if (inLib) + if (inCmdLineArchive) { Err(ctx) << "nested " << arg->getSpelling(); - inLib = true; + } else { + startLibArg = arg; + inCmdLineArchive = std::make_shared(); + enqueueTask([&, inCmdLineArchive, startLibArg, endLibArg]() { + // In is important to create a fake archive here so that we + // remember its placement on the command-line. This will be + // later needed to resolve symbols in the archive order required + // by the MSVC specification. + **inCmdLineArchive = make( + ctx.symtab, MemoryBufferRef({}, ""), + startLibArg->getSpelling(), endLibSpelling()); + }); + } break; case OPT_wholearchive_file: if (std::optional path = findFileIfNew(arg->getValue())) - enqueuePath(*path, true, inLib); + enqueuePath(*path, true, inCmdLineArchive); break; case OPT_INPUT: if (std::optional path = findFileIfNew(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path), inLib); + enqueuePath(*path, isWholeArchive(*path), inCmdLineArchive); break; default: // Ignore other options. break; } } + if (inCmdLineArchive) { + StringRef startLib = startLibArg->getSpelling(); + Warn(ctx) << startLib << " without " << endLibSpelling() + << "\nNOTE: all files provided after " << startLib + << " were lazy."; + close(); + } } // Read all input files given via the command line. @@ -2236,7 +2278,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // addWinSysRootLibSearchPaths(), which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (std::optional path = findLibIfNew(arg->getValue())) - enqueuePath(*path, false, false); + enqueuePath(*path); run(); if (errorCount()) return; @@ -2553,9 +2595,11 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (args.hasArg(OPT_include_optional)) { // Handle /includeoptional - for (auto *arg : args.filtered(OPT_include_optional)) - if (isa_and_nonnull(symtab.find(arg->getValue()))) + for (auto *arg : args.filtered(OPT_include_optional)) { + Symbol *sym = ctx.symtab.find(arg->getValue()); + if (sym && (isa(sym) || isa(sym))) symtab.addGCRoot(arg->getValue()); + } } }); } while (run()); @@ -2720,7 +2764,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // /manifestdependency: enables /manifest unless an explicit /manifest:no is // also passed. if (config->manifest == Configuration::Embed) - addBuffer(createManifestRes(), false, false); + addBuffer(createManifestRes()); else if (config->manifest == Configuration::SideBySide || (config->manifest == Configuration::Default && !config->manifestDependencies.empty())) diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index 14c97a98875bf..203cab930536e 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -22,6 +22,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/TarWriter.h" #include "llvm/WindowsDriver/MSVCPaths.h" +#include #include #include #include @@ -80,7 +81,7 @@ class LinkerDriver { void linkerMain(llvm::ArrayRef args); - void addFile(InputFile *file); + void addFile(InputFile *file, CmdLineArchive *inCmdLineArchive = nullptr); void addClangLibSearchPaths(const std::string &argv0); @@ -88,18 +89,23 @@ class LinkerDriver { void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym, StringRef parentName); - void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); } + void enqueuePDB(StringRef Path) { enqueuePath(Path); } MemoryBufferRef takeBuffer(std::unique_ptr mb); - void enqueuePath(StringRef path, bool wholeArchive, bool lazy); + // Schedule a input file for reading. + void enqueuePath(StringRef path, bool wholeArchive = false, + std::optional> + inCmdLineArchive = std::nullopt); + + void pullArm64ECIcallHelper(); // Returns a list of chunks of selected symbols. std::vector getChunks() const; std::unique_ptr tar; // for /linkrepro - void pullArm64ECIcallHelper(); + bool ltoCompilationDone = false; private: // Searches a file from search paths. @@ -169,8 +175,8 @@ class LinkerDriver { std::set visitedLibs; - void addBuffer(std::unique_ptr mb, bool wholeArchive, - bool lazy); + void addBuffer(std::unique_ptr mb, bool wholeArchive = false, + CmdLineArchive *inCmdLineArchive = nullptr); void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, StringRef parentName, uint64_t offsetInArchive); @@ -258,8 +264,6 @@ class LinkerDriver { // Create export thunks for exported and patchable Arm64EC function symbols. void createECExportThunks(); void maybeCreateECExportThunk(StringRef name, Symbol *&sym); - - bool ltoCompilationDone = false; }; // Create enum with OPT_xxx values for each option in Options.td diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index 7fb42bb681939..20c4613aa422e 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -226,8 +226,19 @@ lld::coff::getArchiveMembers(COFFLinkerContext &ctx, Archive *file) { return v; } -ObjFile::ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj, bool lazy) - : InputFile(symtab, ObjectKind, coffObj->getMemoryBufferRef(), lazy), +void CmdLineArchive::parse() { + for (InputFile *f : files) { + if (auto *o = dyn_cast(f)) + o->parseLazy(); + else if (auto *b = dyn_cast(f)) + b->parseLazy(); + } +} + +void CmdLineArchive::addInputFile(InputFile *f) { files.push_back(f); } + +ObjFile::ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj) + : InputFile(symtab, ObjectKind, coffObj->getMemoryBufferRef()), coffObj(coffObj) {} ObjFile *ObjFile::create(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy) { @@ -241,8 +252,7 @@ ObjFile *ObjFile::create(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy) { Fatal(ctx) << m.getBufferIdentifier() << " is not a COFF file"; bin->release(); - return make(ctx.getSymtab(MachineTypes(obj->getMachine())), obj, - lazy); + return make(ctx.getSymtab(MachineTypes(obj->getMachine())), obj); } void ObjFile::parseLazy() { @@ -257,8 +267,6 @@ void ObjFile::parseLazy() { if (coffSym.isAbsolute() && ignoredSymbolName(name)) continue; symtab.addLazyObject(this, name); - if (!lazy) - return; i += coffSym.getNumberOfAuxSymbols(); } } @@ -299,6 +307,8 @@ void ObjFile::initializeECThunks() { } void ObjFile::parse() { + symtab.ctx.objFileInstances.push_back(this); + // Read section and symbol tables. initializeChunks(); initializeSymbols(); @@ -1201,6 +1211,8 @@ ImportThunkChunk *ImportFile::makeImportThunk() { } void ImportFile::parse() { + symtab.ctx.importFileInstances.push_back(this); + const auto *hdr = reinterpret_cast(mb.getBufferStart()); @@ -1307,14 +1319,14 @@ void ImportFile::parse() { } BitcodeFile::BitcodeFile(SymbolTable &symtab, MemoryBufferRef mb, - std::unique_ptr &o, bool lazy) - : InputFile(symtab, BitcodeKind, mb, lazy) { + std::unique_ptr &o) + : InputFile(symtab, BitcodeKind, mb) { obj.swap(o); } BitcodeFile *BitcodeFile::create(COFFLinkerContext &ctx, MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive, bool lazy) { + uint64_t offsetInArchive) { std::string path = mb.getBufferIdentifier().str(); if (ctx.config.thinLTOIndexOnly) path = replaceThinLTOSuffix(mb.getBufferIdentifier(), @@ -1335,13 +1347,18 @@ BitcodeFile *BitcodeFile::create(COFFLinkerContext &ctx, MemoryBufferRef mb, utostr(offsetInArchive))); std::unique_ptr obj = check(lto::InputFile::create(mbref)); - return make(ctx.getSymtab(getMachineType(obj.get())), mb, obj, - lazy); + return make(ctx.getSymtab(getMachineType(obj.get())), mb, obj); } BitcodeFile::~BitcodeFile() = default; void BitcodeFile::parse() { + if (symtab.ctx.driver.ltoCompilationDone) { + Err(symtab.ctx) << "LTO object file " << toString(this) + << " linked in after doing LTO compilation."; + } + symtab.bitcodeFileInstances.push_back(this); + llvm::StringSaver &saver = lld::saver(); std::vector> comdat(obj->getComdatTable().size()); @@ -1406,11 +1423,8 @@ void BitcodeFile::parse() { void BitcodeFile::parseLazy() { for (const lto::InputFile::Symbol &sym : obj->symbols()) - if (!sym.isUndefined()) { + if (!sym.isUndefined()) symtab.addLazyObject(this, sym.getName()); - if (!lazy) - return; - } } MachineTypes BitcodeFile::getMachineType(const llvm::lto::InputFile *obj) { diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index 21b9aeef21d4f..6688a8344eb1a 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -71,6 +71,7 @@ class InputFile { public: enum Kind { ArchiveKind, + CmdLineArchiveKind, ObjectKind, PDBKind, ImportKind, @@ -83,14 +84,19 @@ class InputFile { // Returns the filename. StringRef getName() const { return mb.getBufferIdentifier(); } - // Reads a file (the constructor doesn't do that). - virtual void parse() = 0; - // Returns the CPU type this file was compiled to. virtual MachineTypes getMachineType() const { return IMAGE_FILE_MACHINE_UNKNOWN; } + // Parse the file if it wasn't already parsed. + void maybeParse() { + if (parsed) + return; + parsed = true; + parse(); + } + MemoryBufferRef mb; // An archive file name if this file is created from an archive. @@ -102,17 +108,18 @@ class InputFile { SymbolTable &symtab; protected: - InputFile(SymbolTable &s, Kind k, MemoryBufferRef m, bool lazy = false) - : mb(m), symtab(s), fileKind(k), lazy(lazy) {} + InputFile(SymbolTable &s, Kind k, MemoryBufferRef m) + : mb(m), symtab(s), fileKind(k) {} + + // Reads a file (the constructor doesn't do that). + virtual void parse() = 0; StringRef directives; + bool parsed = false; + private: const Kind fileKind; - -public: - // True if this is a lazy ObjFile or BitcodeFile. - bool lazy = false; }; // .lib or .a file. @@ -132,16 +139,35 @@ class ArchiveFile : public InputFile { llvm::DenseSet seen; }; +// A synthetic -start-lib/-end-lib archive. +class CmdLineArchive : public InputFile { +public: + explicit CmdLineArchive(SymbolTable &s, MemoryBufferRef m, StringRef startLib, + StringRef endLib) + : InputFile(s, CmdLineArchiveKind, m), startLibArg(startLib), + endLibArg(endLib) {} + static bool classof(const InputFile *f) { + return f->kind() == CmdLineArchiveKind; + } + void parse() override; + void addInputFile(InputFile *f); + + StringRef startLibArg; + StringRef endLibArg; + +private: + std::vector files; +}; + // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: static ObjFile *create(COFFLinkerContext &ctx, MemoryBufferRef mb, bool lazy = false); - explicit ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj, bool lazy); + explicit ObjFile(SymbolTable &symtab, COFFObjectFile *coffObj); static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; - void parseLazy(); MachineTypes getMachineType() const override; ArrayRef getChunks() { return chunks; } ArrayRef getDebugChunks() { return debugChunks; } @@ -191,6 +217,9 @@ class ObjFile : public InputFile { // True if this file was compiled with /guard:ehcont. bool hasGuardEHCont() { return feat00Flags & 0x4000; } + // Add lazy references to the symbols in this file. + void parseLazy(); + // Pointer to the PDB module descriptor builder. Various debug info records // will reference object files by "module index", which is here. Things like // source files and section contributions are also recorded here. Will be null @@ -391,24 +420,24 @@ class ImportFile : public InputFile { class BitcodeFile : public InputFile { public: explicit BitcodeFile(SymbolTable &symtab, MemoryBufferRef mb, - std::unique_ptr &obj, bool lazy); + std::unique_ptr &obj); ~BitcodeFile(); static BitcodeFile *create(COFFLinkerContext &ctx, MemoryBufferRef mb, - StringRef archiveName, uint64_t offsetInArchive, - bool lazy); + StringRef archiveName, uint64_t offsetInArchive); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() const override { return getMachineType(obj.get()); } static MachineTypes getMachineType(const llvm::lto::InputFile *obj); - void parseLazy(); + void parse() override; std::unique_ptr obj; -private: - void parse() override; + // Add lazy references to the symbols in this file. + void parseLazy(); +private: std::vector symbols; }; diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index 8fb0ee4e890d6..020b1b37cdb3c 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -47,6 +47,8 @@ static COFFSyncStream errorOrWarn(COFFLinkerContext &ctx) { // Causes the file associated with a lazy symbol to be linked in. static void forceLazy(Symbol *s) { + if (s->pendingArchiveLoad) + return; s->pendingArchiveLoad = true; switch (s->kind()) { case Symbol::Kind::LazyArchiveKind: { @@ -56,11 +58,6 @@ static void forceLazy(Symbol *s) { } case Symbol::Kind::LazyObjectKind: { InputFile *file = cast(s)->file; - // FIXME: Remove this once we resolve all defineds before all undefineds in - // ObjFile::initializeSymbols(). - if (!file->lazy) - return; - file->lazy = false; file->symtab.ctx.driver.addFile(file); break; } @@ -747,7 +744,6 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { } void SymbolTable::addLazyObject(InputFile *f, StringRef n) { - assert(f->lazy); if (isEC() && !checkLazyECPair(this, n, f)) return; auto [s, wasInserted] = insert(n, f); @@ -759,7 +755,6 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) { if (!u || (u->weakAlias && !u->isECAlias(machine)) || s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; - f->lazy = false; ctx.driver.addFile(f); } @@ -1372,8 +1367,7 @@ void SymbolTable::compileBitcodeFiles() { lto->add(*f); for (InputFile *newObj : lto->compile()) { ObjFile *obj = cast(newObj); - obj->parse(); - ctx.objFileInstances.push_back(obj); + obj->maybeParse(); } } diff --git a/lld/Common/Args.cpp b/lld/Common/Args.cpp index 5546b2aece641..e64769e09d13f 100644 --- a/lld/Common/Args.cpp +++ b/lld/Common/Args.cpp @@ -90,3 +90,13 @@ StringRef lld::args::getFilenameWithoutExe(StringRef path) { return sys::path::stem(path); return sys::path::filename(path); } + +StringRef lld::args::getOptionSpellingLikeArg(llvm::opt::OptTable &optTable, + llvm::opt::OptSpecifier opt, + llvm::opt::Arg *arg, + llvm::StringSaver &saver) { + StringRef prefix = arg->getSpelling(); + prefix.consume_back(arg->getOption().getName()); + auto option = optTable.getOption(opt); + return saver.save((prefix + option.getName()).str()); +} diff --git a/lld/include/lld/Common/Args.h b/lld/include/lld/Common/Args.h index 60f83fbbbf1a3..6e962807a15da 100644 --- a/lld/include/lld/Common/Args.h +++ b/lld/include/lld/Common/Args.h @@ -15,9 +15,13 @@ #include namespace llvm { +class StringSaver; namespace opt { +class Arg; class InputArgList; -} +class OptSpecifier; +class OptTable; +} // namespace opt } // namespace llvm namespace lld { @@ -40,6 +44,11 @@ std::vector getLines(MemoryBufferRef mb); StringRef getFilenameWithoutExe(StringRef path); +StringRef getOptionSpellingLikeArg(llvm::opt::OptTable &optTable, + llvm::opt::OptSpecifier opt, + llvm::opt::Arg *arg, + llvm::StringSaver &saver); + } // namespace args } // namespace lld diff --git a/lld/test/COFF/start-lib-warn.ll b/lld/test/COFF/start-lib-warn.ll new file mode 100644 index 0000000000000..05d40e4a1a049 --- /dev/null +++ b/lld/test/COFF/start-lib-warn.ll @@ -0,0 +1,55 @@ +; REQUIRES: x86 +; +; RUN: split-file %s %t.dir +; +; We need an input file to lld, so create one. +; RUN: llc -filetype=obj %t.dir/main.ll -o %t.obj + +; RUN: lld-link -start-lib %t.obj /subsystem:console /force 2>&1 \ +; RUN: | FileCheck --check-prefix=MISSING_END %s +; MISSING_END: -start-lib without -end-lib + +; RUN: not lld-link %t.obj -end-lib /subsystem:console /force 2>&1 \ +; RUN: | FileCheck --check-prefix=STRAY_END %s +; STRAY_END: stray -end-lib + +; RUN: not lld-link -start-lib -start-lib %t.obj /subsystem:console /force 2>&1 \ +; RUN: | FileCheck --check-prefix=NESTED_START %s +; NESTED_START: nested -start-lib + +; RUN: lld-link -start-lib %t.obj %S/Inputs/resource.res -end-lib /subsystem:console /force 2>&1 \ +; RUN: | FileCheck --check-prefix=WARN_RES %s +; WARN_RES: .res file provided between -start-lib/-end-lib will not be lazy + +; RUN: lld-link -start-lib %t.obj %S/Inputs/ret42.lib -end-lib /subsystem:console /force 2>&1 \ +; RUN: | FileCheck --check-prefix=WARN_LIB %s +; WARN_LIB: .lib/.a file provided between -start-lib/-end-lib has no effect + +; RUN: lld-link -start-lib %t.obj %S/Inputs/pdb-diff-cl.pdb -end-lib /subsystem:console /force 2>&1 \ +; RUN: | FileCheck --check-prefix=WARN_PDB %s +; WARN_PDB: .pdb file provided between -start-lib/-end-lib will not be lazy + +; RUN: llvm-mc -filetype=obj -triple=x86_64-windows-gnu %t.dir/lib.s -o %t.lib.o +; RUN: lld-link -noentry -dll -def:%t.dir/lib.def %t.lib.o -out:%t.lib.dll -implib:%t.implib.lib +; RUN: lld-link -lldmingw -start-lib %t.lib.dll -end-lib /force 2>&1 \ +; RUN: | FileCheck --check-prefix=WARN_EXE %s +; WARN_EXE: .dll file provided between -start-lib/-end-lib will not be lazy + +#--- main.ll +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @main() { + ret void +} + +#--- lib.s +.text +.global func1 +func1: + ret + +#--- lib.def +NAME lib.dll +EXPORTS + func1 \ No newline at end of file