COFF: Add /nodefaultlib option.
authorRui Ueyama <ruiu@google.com>
Sun, 31 May 2015 19:17:14 +0000 (19:17 +0000)
committerRui Ueyama <ruiu@google.com>
Sun, 31 May 2015 19:17:14 +0000 (19:17 +0000)
llvm-svn: 238679

lld/COFF/Config.h
lld/COFF/Driver.cpp
lld/COFF/Driver.h
lld/test/COFF/nodefaultlib.test [new file with mode: 0644]

index 3ab81bd..afe5670 100644 (file)
@@ -20,6 +20,7 @@ namespace lld {
 namespace coff {
 
 using llvm::COFF::WindowsSubsystem;
+using llvm::StringRef;
 
 class Configuration {
 public:
@@ -28,6 +29,9 @@ public:
   WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
   std::string EntryName;
 
+  std::set<StringRef> NoDefaultLibs;
+  bool NoDefaultLibAll = false;
+
   uint64_t ImageBase = 0x140000000;
   uint64_t StackReserve = 1024 * 1024;
   uint64_t StackCommit = 4096;
index 8ea41e9..a5f6f52 100644 (file)
@@ -95,18 +95,15 @@ LinkerDriver::parseDirectives(StringRef S,
     return EC;
   std::unique_ptr<llvm::opt::InputArgList> Args = std::move(ArgsOrErr.get());
 
-  for (auto *Arg : Args->filtered(OPT_defaultlib)) {
-    StringRef Path = findLib(Arg->getValue());
-    if (!insertFile(Path))
-      continue;
-    Res->push_back(llvm::make_unique<ArchiveFile>(Path));
-  }
+  for (auto *Arg : Args->filtered(OPT_defaultlib))
+    if (Optional<StringRef> Path = findLib(Arg->getValue()))
+      Res->push_back(llvm::make_unique<ArchiveFile>(*Path));
   return std::error_code();
 }
 
 // Find file from search paths. You can omit ".obj", this function takes
 // care of that. Note that the returned path is not guaranteed to exist.
-StringRef LinkerDriver::findFile(StringRef Filename) {
+StringRef LinkerDriver::doFindFile(StringRef Filename) {
   bool hasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
   if (hasPathSep)
     return Filename;
@@ -125,17 +122,38 @@ StringRef LinkerDriver::findFile(StringRef Filename) {
   return Filename;
 }
 
-StringRef LinkerDriver::findLib(StringRef Filename) {
-  StringRef S = addExtOpt(Filename, ".lib");
-  return findFile(S);
+// Resolves a file path. This never returns the same path
+// (in that case, it returns None).
+Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
+  StringRef Path = doFindFile(Filename);
+  bool Seen = !VisitedFiles.insert(Path.lower()).second;
+  if (Seen)
+    return None;
+  return Path;
 }
 
-// Add Ext to Filename if Filename has no file extension.
-StringRef LinkerDriver::addExtOpt(StringRef Filename, StringRef Ext) {
+// Find library file from search path.
+StringRef LinkerDriver::doFindLib(StringRef Filename) {
+  // Add ".lib" to Filename if that has no file extension.
   bool hasExt = (Filename.find('.') != StringRef::npos);
-  if (hasExt)
-    return Filename;
-  return Alloc.save(Filename + Ext);
+  if (!hasExt)
+    Filename = Alloc.save(Filename + ".lib");
+  return doFindFile(Filename);
+}
+
+// Resolves a library path. /nodefaultlib options are taken into
+// consideration. This never returns the same path (in that case,
+// it returns None).
+Optional<StringRef> LinkerDriver::findLib(StringRef Filename) {
+  if (Config->NoDefaultLibAll)
+    return None;
+  StringRef Path = doFindLib(Filename);
+  if (Config->NoDefaultLibs.count(Path))
+    return None;
+  bool Seen = !VisitedFiles.insert(Path.lower()).second;
+  if (Seen)
+    return None;
+  return Path;
 }
 
 // Parses LIB environment which contains a list of search paths.
@@ -190,6 +208,14 @@ bool LinkerDriver::link(int Argc, const char *Argv[]) {
   }
   Config->MachineType = MTOrErr.get();
 
+  // Handle /nodefaultlib:<filename>
+  for (auto *Arg : Args->filtered(OPT_nodefaultlib))
+    Config->NoDefaultLibs.insert(doFindLib(Arg->getValue()));
+
+  // Handle /nodefaultlib
+  if (Args->hasArg(OPT_nodefaultlib_all))
+    Config->NoDefaultLibAll = true;
+
   // Handle /base
   if (auto *Arg = Args->getLastArg(OPT_base)) {
     if (auto EC = parseNumbers(Arg->getValue(), &Config->ImageBase)) {
@@ -235,13 +261,20 @@ bool LinkerDriver::link(int Argc, const char *Argv[]) {
     }
   }
 
+  // Create a list of input files. Files can be given as arguments
+  // for /defaultlib option.
+  std::vector<StringRef> Inputs;
+  for (auto *Arg : Args->filtered(OPT_INPUT))
+    if (Optional<StringRef> Path = findFile(Arg->getValue()))
+      Inputs.push_back(*Path);
+  for (auto *Arg : Args->filtered(OPT_defaultlib))
+    if (Optional<StringRef> Path = findLib(Arg->getValue()))
+      Inputs.push_back(*Path);
+
   // Parse all input files and put all symbols to the symbol table.
   // The symbol table will take care of name resolution.
   SymbolTable Symtab;
-  for (auto *Arg : Args->filtered(OPT_INPUT)) {
-    StringRef Path = findFile(Arg->getValue());
-    if (!insertFile(Path))
-      continue;
+  for (StringRef Path : Inputs) {
     if (auto EC = Symtab.addFile(createFile(Path))) {
       llvm::errs() << Path << ": " << EC.message() << "\n";
       return false;
@@ -289,9 +322,5 @@ bool LinkerDriver::link(int Argc, const char *Argv[]) {
   return true;
 }
 
-bool LinkerDriver::insertFile(StringRef Path) {
-  return VisitedFiles.insert(Path.lower()).second;
-}
-
 } // namespace coff
 } // namespace lld
index 7123c43..f64011c 100644 (file)
@@ -11,6 +11,7 @@
 #define LLD_COFF_DRIVER_H
 
 #include "Memory.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Option/Arg.h"
@@ -28,6 +29,7 @@ extern LinkerDriver *Driver;
 
 using llvm::COFF::MachineTypes;
 using llvm::COFF::WindowsSubsystem;
+using llvm::Optional;
 class InputFile;
 
 // Entry point of the COFF linker.
@@ -43,13 +45,13 @@ public:
   parseDirectives(StringRef S, std::vector<std::unique_ptr<InputFile>> *Res);
 
 private:
-  // Returns false if a given file has already been read.
-  bool insertFile(StringRef Path);
+  StringAllocator Alloc;
 
   // Searches a file from search paths.
-  StringRef findFile(StringRef Filename);
-  StringRef findLib(StringRef Filename);
-  StringRef addExtOpt(StringRef Filename, StringRef Ext);
+  Optional<StringRef> findFile(StringRef Filename);
+  Optional<StringRef> findLib(StringRef Filename);
+  StringRef doFindFile(StringRef Filename);
+  StringRef doFindLib(StringRef Filename);
 
   // Parses LIB environment which contains a list of search paths.
   // The returned list always contains "." as the first element.
@@ -57,7 +59,6 @@ private:
 
   std::vector<StringRef> SearchPaths;
   std::set<std::string> VisitedFiles;
-  StringAllocator Alloc;
 };
 
 ErrorOr<std::unique_ptr<llvm::opt::InputArgList>>
diff --git a/lld/test/COFF/nodefaultlib.test b/lld/test/COFF/nodefaultlib.test
new file mode 100644 (file)
index 0000000..2144feb
--- /dev/null
@@ -0,0 +1,30 @@
+# RUN: cp %p/Inputs/hello64.obj %T
+# RUN: cp %p/Inputs/std64.lib %T
+
+# RUN: not lld -flavor link2 /out:%t.exe /entry:main /subsystem:console \
+# RUN:   hello64.obj /defaultlib:std64.lib >& %t.log
+# RUN: FileCheck -check-prefix=CHECK1 %s < %t.log
+
+# RUN: not lld -flavor link2 /out:%t.exe /entry:main /subsystem:console \
+# RUN:   hello64 /defaultlib:std64.lib >& %t.log
+# RUN: FileCheck -check-prefix=CHECK2 %s < %t.log
+
+### On Windows, "env" command does not propagate subcommand exit status.
+### On Unix it does. "|| true" is a hack to deal with that.
+
+# RUN: env LIB=%T lld -flavor link2 /out:%t.exe /entry:main \
+# RUN:   /subsystem:console hello64.obj /defaultlib:std64.lib \
+# RUN:   /nodefaultlib:std64.lib >& %t.log || true
+# RUN: FileCheck -check-prefix=CHECK3 %s < %t.log
+
+# RUN: env LIB=%T lld -flavor link2 /out:%t.exe /entry:main \
+# RUN:   /subsystem:console hello64.obj /defaultlib:std64 \
+# RUN:   /nodefaultlib:std64.lib >& %t.log || true
+# RUN: FileCheck -check-prefix=CHECK3 %s < %t.log
+
+CHECK1: hello64.obj: {{[Nn]}}o such file or directory
+CHECK2: hello64: {{[Nn]}}o such file or directory
+CHECK3: undefined symbol: MessageBoxA
+
+# RUN: env LIB=%T lld -flavor link2 /out:%t.exe /entry:main \
+# RUN:   /subsystem:console hello64.obj /defaultlib:std64.lib