Index: lld/COFF/Driver.h =================================================================== --- lld/COFF/Driver.h +++ lld/COFF/Driver.h @@ -108,6 +108,9 @@ bool findUnderscoreMangle(StringRef sym); + // Adds appropriate search paths from `sysroot`. + void addWinSysRootLibSearchPaths(StringRef sysroot); + // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Host.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Parallel.h" @@ -118,7 +119,7 @@ else if (config->driver) ext = ".sys"; - return (sys::path::stem(path) + ext).str(); + return (path::stem(path) + ext).str(); } // Returns true if S matches /crtend.?\.o$/. @@ -166,6 +167,159 @@ return sym; } +static std::string getHighestNumericTupleInDirectory(StringRef Directory) { + std::string Highest; + VersionTuple HighestTuple; + + std::error_code EC; + for (fs::directory_iterator DirIt(Directory, EC), DirEnd; + !EC && DirIt != DirEnd; DirIt.increment(EC)) { + + if (!fs::is_directory(DirIt->path())) + continue; + StringRef CandidateName = path::filename(DirIt->path()); + VersionTuple Tuple; + if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error. + continue; + if (Tuple > HighestTuple) { + HighestTuple = Tuple; + Highest = CandidateName.str(); + } + } + + return Highest; +} + +static const char *getWindowsSDKArch() { + switch (config->machine) { + case I386: + return "x86"; + case AMD64: + return "x64"; + case ARMNT: + return "arm"; + case ARM64: + return "arm64"; + default: + return ""; + } +} + +static const char *getLegacyVCArch() { + switch (config->machine) { + case I386: + // x86 is default in legacy VC toolchains. + // e.g. x86 libs are directly in /lib as opposed to /lib/x86. + return ""; + case AMD64: + return "amd64"; + case ARMNT: + return "arm"; + case ARM64: + return "arm64"; + default: + return ""; + } +} + +enum class SubDirectoryType { + Bin, + Include, + Lib, +}; + +std::string getSubDirectoryPath(SubDirectoryType Type, + StringRef VCToolChainPath, + StringRef SubdirParent = "") { + const char *SubdirName = getWindowsSDKArch(); + + SmallString<256> Path(VCToolChainPath); + if (!SubdirParent.empty()) + path::append(Path, SubdirParent); + + switch (Type) { + case SubDirectoryType::Bin: { + const bool HostIsX64 = Triple(getProcessTriple()).isArch64Bit(); + const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; + path::append(Path, "bin", HostName, SubdirName); + break; + } + case SubDirectoryType::Include: + path::append(Path, "include"); + break; + case SubDirectoryType::Lib: + path::append(Path, "lib", SubdirName); + break; + } + return std::string(Path.str()); +} + +// Check if the Include path of a specified version of Visual Studio contains +// specific header files. If not, they are probably shipped with Universal CRT. +static bool useUniversalCRT(StringRef VCToolChainPath) { + SmallString<128> TestPath( + getSubDirectoryPath(SubDirectoryType::Include, VCToolChainPath)); + path::append(TestPath, "stdlib.h"); + return !fs::exists(TestPath); +} + +static void getWindowsSDKDirViaCommandLine(StringRef sysroot, std::string &Path, + std::string &Version) { + SmallString<128> SDKPath(sysroot); + path::append(SDKPath, "Windows Kits"); + path::append(SDKPath, getHighestNumericTupleInDirectory(SDKPath)); + Path = std::string(SDKPath.str()); + + path::append(SDKPath, "Include"); + Version = getHighestNumericTupleInDirectory(SDKPath); +} + +static bool getUniversalCRTLibraryPath(StringRef sysroot, std::string &Path) { + Path.clear(); + + StringRef ArchName = getWindowsSDKArch(); + if (ArchName.empty()) + return false; + + std::string UCRTVersion; + getWindowsSDKDirViaCommandLine(sysroot, Path, UCRTVersion); + llvm::SmallString<128> LibPath(Path); + llvm::sys::path::append(LibPath, "Lib", UCRTVersion, "ucrt", ArchName); + Path = std::string(LibPath.str()); + return true; +} + +static bool getWindowsSDKLibraryPath(StringRef sysroot, std::string &Path) { + std::string sdkPath; + std::string windowsSDKLibVersion; + + Path.clear(); + getWindowsSDKDirViaCommandLine(sysroot, sdkPath, windowsSDKLibVersion); + + SmallString<128> libPath(sdkPath); + path::append(libPath, "Lib"); + if (!windowsSDKLibVersion.empty()) { + path::append(libPath, windowsSDKLibVersion, "um", getWindowsSDKArch()); + } else { + switch (config->machine) { + // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. + case I386: + break; + case AMD64: + path::append(libPath, "x64"); + break; + case ARMNT: + // It is not necessary to link against Windows SDK 7.x when targeting ARM. + return false; + default: + return false; + } + } + + Path = std::string(libPath.str()); + return true; +} + bool LinkerDriver::findUnderscoreMangle(StringRef sym) { Symbol *s = ctx.symtab.findMangle(mangle(sym)); return s && !isa(s); @@ -440,21 +594,21 @@ bool hasExt = filename.contains('.'); for (StringRef dir : searchPaths) { SmallString<128> path = dir; - sys::path::append(path, filename); - if (sys::fs::exists(path.str())) + path::append(path, filename); + if (fs::exists(path.str())) return saver.save(path.str()); if (!hasExt) { path.append(".obj"); - if (sys::fs::exists(path.str())) + if (fs::exists(path.str())) return saver.save(path.str()); } } return filename; } -static Optional getUniqueID(StringRef path) { - sys::fs::UniqueID ret; - if (sys::fs::getUniqueID(path, ret)) +static Optional getUniqueID(StringRef path) { + fs::UniqueID ret; + if (fs::getUniqueID(path, ret)) return None; return ret; } @@ -464,14 +618,14 @@ Optional LinkerDriver::findFile(StringRef filename) { StringRef path = doFindFile(filename); - if (Optional id = getUniqueID(path)) { + if (Optional id = getUniqueID(path)) { bool seen = !visitedFiles.insert(*id).second; if (seen) return None; } if (path.endswith_insensitive(".lib")) - visitedLibs.insert(std::string(sys::path::filename(path))); + visitedLibs.insert(std::string(path::filename(path))); return path; } @@ -482,7 +636,7 @@ return filename; SmallString<128> s = filename; - sys::path::replace_extension(s, ".a"); + path::replace_extension(s, ".a"); StringRef libName = saver.save("lib" + s.str()); return doFindFile(libName); } @@ -514,12 +668,36 @@ if (config->noDefaultLibs.count(path.lower())) return None; - if (Optional id = getUniqueID(path)) + if (Optional id = getUniqueID(path)) if (!visitedFiles.insert(*id).second) return None; return path; } +void LinkerDriver::addWinSysRootLibSearchPaths(StringRef sysroot) { + SmallString<128> ToolsPath(sysroot); + path::append(ToolsPath, "VC", "Tools", "MSVC"); + path::append(ToolsPath, getHighestNumericTupleInDirectory(ToolsPath)); + + SmallString<128> DIAPath(sysroot); + // The DIA SDK always uses the legacy vc arch, even in new MSVC versions. + path::append(DIAPath, "DIA SDK", "lib", getLegacyVCArch()); + searchPaths.push_back(DIAPath); + + searchPaths.push_back(getSubDirectoryPath(SubDirectoryType::Lib, ToolsPath)); + searchPaths.push_back( + getSubDirectoryPath(SubDirectoryType::Lib, ToolsPath, "atlmfc")); + + if (useUniversalCRT(ToolsPath)) { + std::string UniversalCRTLibPath; + if (getUniversalCRTLibraryPath(sysroot, UniversalCRTLibPath)) + searchPaths.push_back(UniversalCRTLibPath); + } + std::string WindowsSdkLibPath; + if (getWindowsSDKLibraryPath(sysroot, WindowsSdkLibPath)) + searchPaths.push_back(WindowsSdkLibPath); +} + // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { Optional envOpt = Process::GetEnv("LIB"); @@ -642,6 +820,7 @@ case OPT_INPUT: case OPT_defaultlib: case OPT_libpath: + case OPT_winsysroot: break; case OPT_call_graph_ordering_file: case OPT_deffile: @@ -667,7 +846,7 @@ case OPT_pdb: case OPT_pdbstripped: case OPT_out: - os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n"; + os << arg->getSpelling() << path::filename(arg->getValue()) << "\n"; break; default: os << toString(*arg) << "\n"; @@ -775,7 +954,7 @@ if (!config->implib.empty()) return std::string(config->implib); SmallString<128> out = StringRef(config->outputFile); - sys::path::replace_extension(out, ".lib"); + path::replace_extension(out, ".lib"); return std::string(out.str()); } @@ -790,14 +969,13 @@ SmallString<128> out; if (config->importName.empty()) { - out.assign(sys::path::filename(config->outputFile)); + out.assign(path::filename(config->outputFile)); if (asLib) - sys::path::replace_extension(out, ".dll"); + path::replace_extension(out, ".dll"); } else { out.assign(config->importName); - if (!sys::path::has_extension(out)) - sys::path::replace_extension(out, - (config->dll || asLib) ? ".dll" : ".exe"); + if (!path::has_extension(out)) + path::replace_extension(out, (config->dll || asLib) ? ".dll" : ".exe"); } return std::string(out.str()); @@ -839,7 +1017,7 @@ SmallString<128> tmpName; if (std::error_code ec = - sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName)) + fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName)) fatal("cannot create temporary file for import library " + path + ": " + ec.message()); @@ -853,9 +1031,9 @@ tmpName, /*IsText=*/false, /*RequiresNullTerminator=*/false)); if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) { oldBuf->reset(); - checkError(errorCodeToError(sys::fs::rename(tmpName, path))); + checkError(errorCodeToError(fs::rename(tmpName, path))); } else { - sys::fs::remove(tmpName); + fs::remove(tmpName); } } @@ -1103,10 +1281,9 @@ // vars. static void parsePDBAltPath(StringRef altPath) { SmallString<128> buf; - StringRef pdbBasename = - sys::path::filename(config->pdbPath, sys::path::Style::windows); + StringRef pdbBasename = path::filename(config->pdbPath, path::Style::windows); StringRef binaryExtension = - sys::path::extension(config->outputFile, sys::path::Style::windows); + path::extension(config->outputFile, path::Style::windows); if (!binaryExtension.empty()) binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'. @@ -1237,7 +1414,7 @@ if (auto *arg = args.getLastArg(OPT_linkrepro)) { SmallString<64> path = StringRef(arg->getValue()); - sys::path::append(path, "repro.tar"); + path::append(path, "repro.tar"); return std::string(path); } @@ -1328,7 +1505,7 @@ // Handle /linkrepro and /reproduce. if (Optional path = getReproduceFile(args)) { Expected> errOrWriter = - TarWriter::create(*path, sys::path::stem(*path)); + TarWriter::create(*path, path::stem(*path)); if (errOrWriter) { tar = std::move(*errOrWriter); @@ -1349,6 +1526,8 @@ searchPaths.push_back(""); for (auto *arg : args.filtered(OPT_libpath)) searchPaths.push_back(arg->getValue()); + if (auto *arg = args.getLastArg(OPT_winsysroot)) + addWinSysRootLibSearchPaths(arg->getValue()); if (!args.hasArg(OPT_lldignoreenv)) addLibSearchPaths(); @@ -1802,10 +1981,10 @@ if (errorCount()) return; - std::set wholeArchives; + std::set wholeArchives; for (auto *arg : args.filtered(OPT_wholearchive_file)) if (Optional path = doFindFile(arg->getValue())) - if (Optional id = getUniqueID(*path)) + if (Optional id = getUniqueID(*path)) wholeArchives.insert(*id); // A predicate returning true if a given path is an argument for @@ -1815,7 +1994,7 @@ auto isWholeArchive = [&](StringRef path) -> bool { if (args.hasArg(OPT_wholearchive_flag)) return true; - if (Optional id = getUniqueID(path)) + if (Optional id = getUniqueID(path)) return wholeArchives.count(*id); return false; }; @@ -1981,7 +2160,7 @@ // Put the PDB next to the image if no /pdb flag was passed. if (config->pdbPath.empty()) { config->pdbPath = config->outputFile; - sys::path::replace_extension(config->pdbPath, ".pdb"); + path::replace_extension(config->pdbPath, ".pdb"); } // The embedded PDB path should be the absolute path to the PDB if no @@ -1992,8 +2171,8 @@ // It's important to make the path absolute and remove dots. This path // will eventually be written into the PE header, and certain Microsoft // tools won't work correctly if these assumptions are not held. - sys::fs::make_absolute(config->pdbAltPath); - sys::path::remove_dots(config->pdbAltPath); + fs::make_absolute(config->pdbAltPath); + path::remove_dots(config->pdbAltPath); } else { // Don't do this earlier, so that Config->OutputFile is ready. parsePDBAltPath(config->pdbAltPath); Index: lld/COFF/Options.td =================================================================== --- lld/COFF/Options.td +++ lld/COFF/Options.td @@ -92,6 +92,8 @@ def version : P<"version", "Specify a version number in the PE header">; def wholearchive_file : P<"wholearchive", "Include all object files from this library">; +def winsysroot : P<"winsysroot", + "Adds several subdirectories to the library search paths">; def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, Alias;