diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -15,6 +15,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CachePruning.h" +#include "llvm/Support/VirtualFileSystem.h" #include #include #include @@ -238,6 +239,9 @@ // Used for /print-symbol-order: StringRef printSymbolOrder; + // Used for /vfsoverlay: + std::unique_ptr vfs; + uint64_t align = 4096; uint64_t imageBase = -1; uint64_t fileAlign = 512; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -436,17 +436,26 @@ // 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::doFindFile(StringRef filename) { + auto getFilename = [](StringRef filename) -> StringRef { + if (config->vfs) + if (auto statOrErr = config->vfs->status(filename)) + return saver().save(statOrErr->getName()); + return filename; + }; + bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos); if (hasPathSep) - return filename; + return getFilename(filename); bool hasExt = filename.contains('.'); for (StringRef dir : searchPaths) { SmallString<128> path = dir; sys::path::append(path, filename); + path = SmallString<128>{getFilename(path.str())}; if (sys::fs::exists(path.str())) return saver().save(path.str()); if (!hasExt) { path.append(".obj"); + path = SmallString<128>{getFilename(path.str())}; if (sys::fs::exists(path.str())) return saver().save(path.str()); } @@ -1349,6 +1358,28 @@ return None; } +static std::unique_ptr +getVFS(const opt::InputArgList &args) { + using namespace llvm::vfs; + + const opt::Arg *arg = args.getLastArg(OPT_vfsoverlay); + if (!arg) + return nullptr; + + auto bufOrErr = llvm::MemoryBuffer::getFile(arg->getValue()); + if (!bufOrErr) { + checkError(errorCodeToError(bufOrErr.getError())); + return nullptr; + } + + if (auto ret = vfs::getVFSFromYAML(std::move(*bufOrErr), /*DiagHandler*/ nullptr, + arg->getValue())) + return ret; + + error("Invalid vfs overlay"); + return nullptr; +} + void LinkerDriver::linkerMain(ArrayRef argsArr) { ScopedTimer rootTimer(ctx.rootTimer); @@ -1390,6 +1421,8 @@ errorHandler().errorLimit = n; } + config->vfs = getVFS(args); + // Handle /help if (args.hasArg(OPT_help)) { printHelp(argsArr[0]); diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -278,6 +278,8 @@ "/call-graph-profile-sort into the specified file">; def wrap : P_priv<"wrap">; +def vfsoverlay : P<"vfsoverlay", "Path to a vfsoverlay yaml file to optionally look for /defaultlib's in">; + // Flags for debugging def lldmap : F<"lldmap">; def lldmap_file : P_priv<"lldmap">; diff --git a/lld/test/COFF/vfsoverlay.test b/lld/test/COFF/vfsoverlay.test new file mode 100644 --- /dev/null +++ b/lld/test/COFF/vfsoverlay.test @@ -0,0 +1,33 @@ +# RUN: rm -rf %t +# RUN: split-file %s %t +# RUN: cp %p/Inputs/std64.lib %t/std64.lib +# RUN: sed -e "s|REPLACE|%/t/std64.lib|g" %t/overlay.yaml.in > %t/overlay.yaml + +# RUN: lld-link %S/Inputs/hello64.obj /libpath:/noexist /out:%t.exe /entry:main /defaultlib:notstd64 /vfsoverlay:%t/overlay.yaml + +# RUN: not lld-link %S/Inputs/hello64.obj /libpath:/noexist /out:%t.exe /entry:main /defaultlib:notstd64 /vfsoverlay:noexist 2>&1 \ +# RUN: | FileCheck %s +# CHECK: error: No such file or directory + +# RUN: sed -e "s|{|bad|g" %t/overlay.yaml > %t/badoverlay.yaml +# RUN: not lld-link %S/Inputs/hello64.obj /libpath:/noexist /out:%t.exe /entry:main /defaultlib:notstd64 /vfsoverlay:%t/badoverlay.yaml 2>&1 \ +# RUN: | FileCheck %s --check-prefix=BAD-OVERLAY +# BAD-OVERLAY: error: Invalid vfs overlay + +#--- overlay.yaml.in +{ + 'version': 0, + 'roots' : [ + { + 'name': '/noexist', + 'type': 'directory', + 'contents': [ + { + 'name': 'notstd64.lib', + 'type': 'file', + 'external-contents': 'REPLACE' + } + ] + } + ] +}