Index: lld/COFF/Driver.h =================================================================== --- lld/COFF/Driver.h +++ lld/COFF/Driver.h @@ -21,6 +21,7 @@ #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/TarWriter.h" #include #include @@ -126,6 +127,13 @@ std::vector Resources; llvm::StringSet<> DirectivesExports; + + bool equivalentPath(StringRef A, StringRef B); + llvm::sys::fs::file_status getFileStatus(StringRef Path); + + // Pathnames to their stat() results. Since stat() is slow, + // we want to cache. + llvm::DenseMap StatCache; }; // Functions below this line are defined in DriverUtils.cpp. Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -128,6 +128,15 @@ bool WholeArchive) { StringRef Filename = MB->getBufferIdentifier(); + // A file shouldn't be parsed more than once even if it is + // represented in different paths. + for (StringRef Path : FilePaths) { + if (equivalentPath(Path, Filename)) { + warn(Filename + ": object specified more than once"); + return; + } + } + MemoryBufferRef MBRef = takeBuffer(std::move(MB)); FilePaths.push_back(Filename); @@ -775,6 +784,23 @@ TaskQueue.push_back(std::move(Task)); } +bool LinkerDriver::equivalentPath(StringRef A, StringRef B) { + return sys::fs::equivalent(getFileStatus(A), getFileStatus(B)); +} + +// A cached warpper for stat(). +sys::fs::file_status LinkerDriver::getFileStatus(StringRef Path) { + auto It = StatCache.find(Path); + if (It != StatCache.end()) + return It->second; + + // We ignore an error as we'll report it when we try to open a file. + sys::fs::file_status Ret; + sys::fs::status(Path, Ret); + StatCache[Path] = Ret; + return Ret; +} + bool LinkerDriver::run() { ScopedTimer T(InputFileTimer); @@ -1231,22 +1257,25 @@ if (errorCount()) return; - bool WholeArchiveFlag = Args.hasArg(OPT_wholearchive_flag); + // A predicate returning true if a given path is an argument for + // /wholearchive:, or /wholearchive is enabled globally. + // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj" + // needs to be handled as "/wholearchive:foo.obj foo.obj". + auto IsWholeArchive = [&](StringRef Path) { + if (Args.hasArg(OPT_wholearchive_flag)) + return true; + for (auto *Arg : Args.filtered(OPT_wholearchive_file)) + if (equivalentPath(Path, Arg->getValue())) + return true; + return false; + }; + // Create a list of input files. Files can be given as arguments // for /defaultlib option. - std::vector MBs; - for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) { - switch (Arg->getOption().getID()) { - case OPT_INPUT: - if (Optional Path = findFile(Arg->getValue())) - enqueuePath(*Path, WholeArchiveFlag); - break; - case OPT_wholearchive_file: - if (Optional Path = findFile(Arg->getValue())) - enqueuePath(*Path, true); - break; - } - } + for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) + if (Optional Path = findFile(Arg->getValue())) + enqueuePath(*Path, IsWholeArchive(Arg->getValue())); + for (auto *Arg : Args.filtered(OPT_defaultlib)) if (Optional Path = findLib(Arg->getValue())) enqueuePath(*Path, false); Index: lld/test/COFF/wholearchive.s =================================================================== --- lld/test/COFF/wholearchive.s +++ lld/test/COFF/wholearchive.s @@ -10,6 +10,14 @@ # RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj -wholearchive %t.archive.lib -implib:%t.lib # RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB +# RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj %t.archive.lib -wholearchive:%t.archive.lib -implib:%t.lib +# RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB + +# RUN: mkdir -p %t.dir +# RUN: cp %t.archive.lib %t.dir/foo.lib +# RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj %t.dir/./foo.lib -wholearchive:%t.dir/foo.lib -implib:%t.lib +# RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB + # CHECK-IMPLIB: Symbol: __imp_exportfn3 # CHECK-IMPLIB: Symbol: exportfn3