diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -91,11 +91,11 @@ 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, false, false, false); } MemoryBufferRef takeBuffer(std::unique_ptr mb); - void enqueuePath(StringRef path, bool wholeArchive, bool lazy); + void enqueuePath(StringRef path, bool wholeArchive, bool lazy, bool failOk); std::unique_ptr tar; // for /linkrepro @@ -191,6 +191,7 @@ std::list> taskQueue; std::vector filePaths; std::vector resources; + std::vector> pathsToRetry; llvm::DenseSet directivesExports; llvm::DenseSet excludedSymbols; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -121,6 +121,10 @@ return !s.empty() && s.drop_back().endswith("crtend"); } +static bool hasPathSep(StringRef filename) { + return filename.find_first_of("/\\") != StringRef::npos; +} + // ErrorOr is not default constructible, so it cannot be used as the type // parameter of a future. // FIXME: We could open the file in createFutureForFile and avoid needing to @@ -242,25 +246,30 @@ } } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy, + bool failOk) { auto future = std::make_shared>( createFutureForFile(std::string(path))); std::string pathStr = std::string(path); enqueueTask([=]() { auto mbOrErr = future->get(); if (mbOrErr.second) { - std::string msg = - "could not open '" + pathStr + "': " + mbOrErr.second.message(); - // Check if the filename is a typo for an option flag. OptTable thinks - // that all args that are not known options and that start with / are - // filenames, but e.g. `/nodefaultlibs` is more likely a typo for - // the option `/nodefaultlib` than a reference to a file in the root - // directory. - std::string nearest; - if (ctx.optTable.findNearest(pathStr, nearest) > 1) - error(msg); - else - error(msg + "; did you mean '" + nearest + "'"); + if (!hasPathSep(pathStr) && failOk) { + ctx.driver.pathsToRetry.push_back({pathStr, wholeArchive, lazy}); + } else { + std::string msg = + "could not open '" + pathStr + "': " + mbOrErr.second.message(); + // Check if the filename is a typo for an option flag. OptTable thinks + // that all args that are not known options and that start with / are + // filenames, but e.g. `/nodefaultlibs` is more likely a typo for + // the option `/nodefaultlib` than a reference to a file in the root + // directory. + std::string nearest; + if (ctx.optTable.findNearest(pathStr, nearest) > 1) + error(msg); + else + error(msg + "; did you mean '" + nearest + "'"); + } } else ctx.driver.addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); }); @@ -399,7 +408,7 @@ break; case OPT_defaultlib: if (std::optional path = findLib(arg->getValue())) - enqueuePath(*path, false, false); + enqueuePath(*path, false, false, false); break; case OPT_entry: ctx.config.entry = addUndefined(mangle(arg->getValue())); @@ -465,8 +474,7 @@ return filename; }; - bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos); - if (hasPathSep) + if (hasPathSep(filename)) return getFilename(filename); bool hasExt = filename.contains('.'); for (StringRef dir : searchPaths) { @@ -2004,11 +2012,11 @@ break; case OPT_wholearchive_file: if (std::optional path = findFile(arg->getValue())) - enqueuePath(*path, true, inLib); + enqueuePath(*path, true, inLib, true); break; case OPT_INPUT: if (std::optional path = findFile(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path), inLib); + enqueuePath(*path, isWholeArchive(*path), inLib, true); break; default: // Ignore other options. @@ -2018,8 +2026,6 @@ // Read all input files given via the command line. run(); - if (errorCount()) - return; // We should have inferred a machine type by now from the input files, but if // not we assume x64. @@ -2028,13 +2034,24 @@ config->machine = AMD64; addWinSysRootLibSearchPaths(); } + + // Retry again now that we've added winsysroot search paths. + for (auto p : pathsToRetry) { + auto [path, wholeArchive, lazy] = p; + if (std::optional file = findFile(path)) + enqueuePath(*file, wholeArchive, lazy, false); + } + run(); + if (errorCount()) + return; + config->wordsize = config->is64() ? 8 : 4; // Process files specified as /defaultlib. These must be processed after // addWinSysRootLibSearchPaths(), which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (std::optional path = findLib(arg->getValue())) - enqueuePath(*path, false, false); + enqueuePath(*path, false, false, false); run(); if (errorCount()) return; diff --git a/lld/test/COFF/winsysroot.test b/lld/test/COFF/winsysroot.test --- a/lld/test/COFF/winsysroot.test +++ b/lld/test/COFF/winsysroot.test @@ -12,6 +12,27 @@ # RUN: lld-link %p/Inputs/hello64.obj /winsysroot:%t.dir/sysroot \ # RUN: /defaultlib:std64 /entry:main +Check directly passed lib with /machine: +# RUN: lld-link %p/Inputs/hello64.obj /winsysroot:%t.dir/sysroot /machine:x64 \ +# RUN: std64.lib /entry:main + +# RUN: lld-link %t.obj /winsysroot:%t.dir/sysroot /machine:x86 \ +# RUN: std32.lib /entry:main + +Check directly passed lib without /machine: (should infer from obj arch) +# RUN: lld-link %p/Inputs/hello64.obj /winsysroot:%t.dir/sysroot \ +# RUN: std64.lib /entry:main + +# RUN: lld-link %t.obj /winsysroot:%t.dir/sysroot \ +# RUN: std32.lib /entry:main + +Check we don't choose the wrong arch +# RUN: not lld-link %t.obj /winsysroot:%t.dir/sysroot \ +# RUN: std64.lib /entry:main + +# RUN: not lld-link %p/Inputs/hello64.obj /winsysroot:%t.dir/sysroot \ +# RUN: std32.lib /entry:main + Check that when /winsysroot is specified, %LIB% is ignored. # RUN: env LIB=foo.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x86 not lld-link %t.obj /winsysroot:%t.dir/doesnotexist /defaultlib:std32 2>&1 | FileCheck -check-prefix=LIBIGNORED %s LIBIGNORED: could not open 'std32.lib'