diff options
Diffstat (limited to 'lib/Driver/WinLinkDriver.cpp')
-rw-r--r-- | lib/Driver/WinLinkDriver.cpp | 1371 |
1 files changed, 0 insertions, 1371 deletions
diff --git a/lib/Driver/WinLinkDriver.cpp b/lib/Driver/WinLinkDriver.cpp deleted file mode 100644 index 6ee7a5a004b5a..0000000000000 --- a/lib/Driver/WinLinkDriver.cpp +++ /dev/null @@ -1,1371 +0,0 @@ -//===- lib/Driver/WinLinkDriver.cpp ---------------------------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// Concrete instance of the Driver for Windows link.exe. -/// -//===----------------------------------------------------------------------===// - -#include "lld/Driver/Driver.h" -#include "lld/Driver/WinLinkModuleDef.h" -#include "lld/ReaderWriter/PECOFFLinkingContext.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Object/COFF.h" -#include "llvm/Option/Arg.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/raw_ostream.h" -#include <algorithm> -#include <cctype> -#include <map> -#include <memory> -#include <sstream> -#include <tuple> - -namespace lld { - -// -// Option definitions -// - -// Create enum with OPT_xxx values for each option in WinLinkOptions.td -enum { - OPT_INVALID = 0, -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELP, META) \ - OPT_##ID, -#include "WinLinkOptions.inc" -#undef OPTION -}; - -// Create prefix string literals used in WinLinkOptions.td -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "WinLinkOptions.inc" -#undef PREFIX - -// Create table mapping all options defined in WinLinkOptions.td -static const llvm::opt::OptTable::Info infoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) \ - { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS }, -#include "WinLinkOptions.inc" -#undef OPTION -}; - -namespace { - -// Create OptTable class for parsing actual command line arguments -class WinLinkOptTable : public llvm::opt::OptTable { -public: - // link.exe's command line options are case insensitive, unlike - // other driver's options for Unix. - WinLinkOptTable() - : OptTable(infoTable, llvm::array_lengthof(infoTable), - /* ignoreCase */ true) {} -}; - -} // anonymous namespace - -// -// Functions to parse each command line option -// - -// Split the given string with spaces. -static std::vector<std::string> splitArgList(const std::string &str) { - std::stringstream stream(str); - std::istream_iterator<std::string> begin(stream); - std::istream_iterator<std::string> end; - return std::vector<std::string>(begin, end); -} - -// Split the given string with the path separator. -static std::vector<StringRef> splitPathList(StringRef str) { - std::vector<StringRef> ret; - while (!str.empty()) { - StringRef path; - std::tie(path, str) = str.split(';'); - ret.push_back(path); - } - return ret; -} - -// Parse an argument for /alternatename. The expected string is -// "<string>=<string>". -static bool parseAlternateName(StringRef arg, StringRef &weak, StringRef &def, - raw_ostream &diag) { - std::tie(weak, def) = arg.split('='); - if (weak.empty() || def.empty()) { - diag << "Error: malformed /alternatename option: " << arg << "\n"; - return false; - } - return true; -} - -// Parse an argument for /base, /stack or /heap. The expected string -// is "<integer>[,<integer>]". -static bool parseMemoryOption(StringRef arg, uint64_t &reserve, - uint64_t &commit) { - StringRef reserveStr, commitStr; - std::tie(reserveStr, commitStr) = arg.split(','); - if (reserveStr.getAsInteger(0, reserve)) - return false; - if (!commitStr.empty() && commitStr.getAsInteger(0, commit)) - return false; - return true; -} - -// Parse an argument for /version or /subsystem. The expected string is -// "<integer>[.<integer>]". -static bool parseVersion(StringRef arg, uint32_t &major, uint32_t &minor) { - StringRef majorVersion, minorVersion; - std::tie(majorVersion, minorVersion) = arg.split('.'); - if (minorVersion.empty()) - minorVersion = "0"; - if (majorVersion.getAsInteger(0, major)) - return false; - if (minorVersion.getAsInteger(0, minor)) - return false; - return true; -} - -// Returns subsystem type for the given string. -static llvm::COFF::WindowsSubsystem stringToWinSubsystem(StringRef str) { - return llvm::StringSwitch<llvm::COFF::WindowsSubsystem>(str.lower()) - .Case("windows", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI) - .Case("console", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI) - .Case("boot_application", - llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) - .Case("efi_application", llvm::COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION) - .Case("efi_boot_service_driver", - llvm::COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) - .Case("efi_rom", llvm::COFF::IMAGE_SUBSYSTEM_EFI_ROM) - .Case("efi_runtime_driver", - llvm::COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) - .Case("native", llvm::COFF::IMAGE_SUBSYSTEM_NATIVE) - .Case("posix", llvm::COFF::IMAGE_SUBSYSTEM_POSIX_CUI) - .Default(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN); -} - -// Parse /subsystem command line option. The form of /subsystem is -// "subsystem_name[,majorOSVersion[.minorOSVersion]]". -static bool parseSubsystem(StringRef arg, - llvm::COFF::WindowsSubsystem &subsystem, - llvm::Optional<uint32_t> &major, - llvm::Optional<uint32_t> &minor, raw_ostream &diag) { - StringRef subsystemStr, osVersion; - std::tie(subsystemStr, osVersion) = arg.split(','); - if (!osVersion.empty()) { - uint32_t v1, v2; - if (!parseVersion(osVersion, v1, v2)) - return false; - major = v1; - minor = v2; - } - subsystem = stringToWinSubsystem(subsystemStr); - if (subsystem == llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN) { - diag << "error: unknown subsystem name: " << subsystemStr << "\n"; - return false; - } - return true; -} - -static llvm::COFF::MachineTypes stringToMachineType(StringRef str) { - // FIXME: we have no way to differentiate between ARM and ARMNT currently. - // However, given that LLVM only supports ARM NT, default to that for now. - return llvm::StringSwitch<llvm::COFF::MachineTypes>(str.lower()) - .Case("arm", llvm::COFF::IMAGE_FILE_MACHINE_ARMNT) - .Case("x64", llvm::COFF::IMAGE_FILE_MACHINE_AMD64) - .Case("x86", llvm::COFF::IMAGE_FILE_MACHINE_I386) - .Default(llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN); -} - -// Parse /section:name,[[!]{DEKPRSW}] -// -// /section option is to set non-default bits in the Characteristics fields of -// the section header. D, E, K, P, R, S, and W represent discardable, -// execute, not_cachable, not_pageable, read, shared, and write bits, -// respectively. You can specify multiple flags in one /section option. -// -// If the flag starts with "!", the flags represent a mask that should be turned -// off regardless of the default value. You can even create a section which is -// not readable, writable nor executable with this -- although it's probably -// useless. -static bool parseSection(StringRef option, std::string §ion, - llvm::Optional<uint32_t> &flags, - llvm::Optional<uint32_t> &mask) { - StringRef flagString; - std::tie(section, flagString) = option.split(","); - - bool negative = false; - if (flagString.startswith("!")) { - negative = true; - flagString = flagString.substr(1); - } - if (flagString.empty()) - return false; - - uint32_t attribs = 0; - for (size_t i = 0, e = flagString.size(); i < e; ++i) { - switch (tolower(flagString[i])) { -#define CASE(c, flag) \ - case c: \ - attribs |= flag; \ - break - CASE('d', llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE); - CASE('e', llvm::COFF::IMAGE_SCN_MEM_EXECUTE); - CASE('k', llvm::COFF::IMAGE_SCN_MEM_NOT_CACHED); - CASE('p', llvm::COFF::IMAGE_SCN_MEM_NOT_PAGED); - CASE('r', llvm::COFF::IMAGE_SCN_MEM_READ); - CASE('s', llvm::COFF::IMAGE_SCN_MEM_SHARED); - CASE('w', llvm::COFF::IMAGE_SCN_MEM_WRITE); -#undef CASE - default: - return false; - } - } - - if (negative) { - mask = attribs; - } else { - flags = attribs; - } - return true; -} - -static bool readFile(PECOFFLinkingContext &ctx, StringRef path, - ArrayRef<uint8_t> &result) { - ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); - if (!buf) - return false; - StringRef Data = buf.get()->getBuffer(); - result = ctx.allocate(ArrayRef<uint8_t>( - reinterpret_cast<const uint8_t *>(Data.begin()), Data.size())); - return true; -} - -// Parse /manifest:EMBED[,ID=#]|NO. -static bool parseManifest(StringRef option, bool &enable, bool &embed, - int &id) { - if (option.equals_lower("no")) { - enable = false; - return true; - } - if (!option.startswith_lower("embed")) - return false; - - embed = true; - option = option.substr(strlen("embed")); - if (option.empty()) - return true; - if (!option.startswith_lower(",id=")) - return false; - option = option.substr(strlen(",id=")); - if (option.getAsInteger(0, id)) - return false; - return true; -} - -static bool isLibraryFile(StringRef path) { - return path.endswith_lower(".lib") || path.endswith_lower(".imp"); -} - -static StringRef getObjectPath(PECOFFLinkingContext &ctx, StringRef path) { - std::string result; - if (isLibraryFile(path)) { - result = ctx.searchLibraryFile(path); - } else if (llvm::sys::path::extension(path).empty()) { - result = path.str() + ".obj"; - } else { - result = path; - } - return ctx.allocate(result); -} - -static StringRef getLibraryPath(PECOFFLinkingContext &ctx, StringRef path) { - std::string result = isLibraryFile(path) - ? ctx.searchLibraryFile(path) - : ctx.searchLibraryFile(path.str() + ".lib"); - return ctx.allocate(result); -} - -// Returns true if the given file is a Windows resource file. -static bool isResoruceFile(StringRef path) { - llvm::sys::fs::file_magic fileType; - if (llvm::sys::fs::identify_magic(path, fileType)) { - // If we cannot read the file, assume it's not a resource file. - // The further stage will raise an error on this unreadable file. - return false; - } - return fileType == llvm::sys::fs::file_magic::windows_resource; -} - -// Merge Windows resource files and convert them to a single COFF file. -// The temporary file path is set to result. -static bool convertResourceFiles(PECOFFLinkingContext &ctx, - std::vector<std::string> inFiles, - std::string &result) { - // Create an output file path. - SmallString<128> outFile; - if (llvm::sys::fs::createTemporaryFile("resource", "obj", outFile)) - return false; - std::string outFileArg = ("/out:" + outFile).str(); - - // Construct CVTRES.EXE command line and execute it. - std::string program = "cvtres.exe"; - ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); - if (!programPathOrErr) { - llvm::errs() << "Unable to find " << program << " in PATH\n"; - return false; - } - const std::string &programPath = *programPathOrErr; - - std::vector<const char *> args; - args.push_back(programPath.c_str()); - args.push_back(ctx.is64Bit() ? "/machine:x64" : "/machine:x86"); - args.push_back("/readonly"); - args.push_back("/nologo"); - args.push_back(outFileArg.c_str()); - for (const std::string &path : inFiles) - args.push_back(path.c_str()); - args.push_back(nullptr); - - if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { - llvm::errs() << program << " failed\n"; - return false; - } - result = outFile.str(); - return true; -} - -// Parse /manifestuac:(level=<string>|uiAccess=<string>). -// -// The arguments will be embedded to the manifest XML file with no error check, -// so the values given via the command line must be valid as XML attributes. -// This may sound a bit odd, but that's how link.exe works, so we will follow. -static bool parseManifestUAC(StringRef option, - llvm::Optional<std::string> &level, - llvm::Optional<std::string> &uiAccess) { - for (;;) { - option = option.ltrim(); - if (option.empty()) - return true; - if (option.startswith_lower("level=")) { - option = option.substr(strlen("level=")); - StringRef value; - std::tie(value, option) = option.split(" "); - level = value.str(); - continue; - } - if (option.startswith_lower("uiaccess=")) { - option = option.substr(strlen("uiaccess=")); - StringRef value; - std::tie(value, option) = option.split(" "); - uiAccess = value.str(); - continue; - } - return false; - } -} - -// Returns the machine type (e.g. x86) of the given input file. -// If the file is not COFF, returns false. -static bool getMachineType(StringRef path, llvm::COFF::MachineTypes &result) { - llvm::sys::fs::file_magic fileType; - if (llvm::sys::fs::identify_magic(path, fileType)) - return false; - if (fileType != llvm::sys::fs::file_magic::coff_object) - return false; - ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); - if (!buf) - return false; - std::error_code ec; - llvm::object::COFFObjectFile obj(buf.get()->getMemBufferRef(), ec); - if (ec) - return false; - result = static_cast<llvm::COFF::MachineTypes>(obj.getMachine()); - return true; -} - -// Parse /export:entryname[=internalname][,@ordinal[,NONAME]][,DATA][,PRIVATE]. -// -// MSDN doesn't say anything about /export:foo=bar style option or PRIVATE -// attribtute, but link.exe actually accepts them. -static bool parseExport(StringRef option, - PECOFFLinkingContext::ExportDesc &ret) { - StringRef name; - StringRef rest; - std::tie(name, rest) = option.split(","); - if (name.empty()) - return false; - if (name.find('=') == StringRef::npos) { - ret.name = name; - } else { - std::tie(ret.externalName, ret.name) = name.split("="); - if (ret.name.empty()) - return false; - } - - for (;;) { - if (rest.empty()) - return true; - StringRef arg; - std::tie(arg, rest) = rest.split(","); - if (arg.equals_lower("noname")) { - if (ret.ordinal < 0) - return false; - ret.noname = true; - continue; - } - if (arg.equals_lower("data")) { - ret.isData = true; - continue; - } - if (arg.equals_lower("private")) { - ret.isPrivate = true; - continue; - } - if (arg.startswith("@")) { - int ordinal; - if (arg.substr(1).getAsInteger(0, ordinal)) - return false; - if (ordinal <= 0 || 65535 < ordinal) - return false; - ret.ordinal = ordinal; - continue; - } - return false; - } -} - -// Read module-definition file. -static bool parseDef(StringRef option, llvm::BumpPtrAllocator &alloc, - std::vector<moduledef::Directive *> &result) { - ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(option); - if (!buf) - return false; - moduledef::Lexer lexer(std::move(buf.get())); - moduledef::Parser parser(lexer, alloc); - return parser.parse(result); -} - -static StringRef replaceExtension(PECOFFLinkingContext &ctx, StringRef path, - StringRef extension) { - SmallString<128> val = path; - llvm::sys::path::replace_extension(val, extension); - return ctx.allocate(val.str()); -} - -// Create a manifest file contents. -static std::string createManifestXml(PECOFFLinkingContext &ctx) { - std::string ret; - llvm::raw_string_ostream out(ret); - // Emit the XML. Note that we do *not* verify that the XML attributes are - // syntactically correct. This is intentional for link.exe compatibility. - out << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" - "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" - " manifestVersion=\"1.0\">\n"; - if (ctx.getManifestUAC()) { - out << " <trustInfo>\n" - " <security>\n" - " <requestedPrivileges>\n" - " <requestedExecutionLevel level=" << ctx.getManifestLevel() - << " uiAccess=" << ctx.getManifestUiAccess() - << "/>\n" - " </requestedPrivileges>\n" - " </security>\n" - " </trustInfo>\n"; - const std::string &dependency = ctx.getManifestDependency(); - if (!dependency.empty()) { - out << " <dependency>\n" - " <dependentAssembly>\n" - " <assemblyIdentity " << dependency - << " />\n" - " </dependentAssembly>\n" - " </dependency>\n"; - } - } - out << "</assembly>\n"; - out.flush(); - return ret; -} - -// Convert one doublequote to two doublequotes, so that we can embed the string -// into a resource script file. -static void quoteAndPrintXml(raw_ostream &out, StringRef str) { - for (;;) { - if (str.empty()) - return; - StringRef line; - std::tie(line, str) = str.split("\n"); - if (line.empty()) - continue; - out << '\"'; - const char *p = line.data(); - for (int i = 0, size = line.size(); i < size; ++i) { - switch (p[i]) { - case '\"': - out << '\"'; - // fallthrough - default: - out << p[i]; - } - } - out << "\"\n"; - } -} - -// Create a resource file (.res file) containing the manifest XML. This is done -// in two steps: -// -// 1. Create a resource script file containing the XML as a literal string. -// 2. Run RC.EXE command to compile the script file to a resource file. -// -// The temporary file created in step 1 will be deleted on exit from this -// function. The file created in step 2 will have the same lifetime as the -// PECOFFLinkingContext. -static bool createManifestResourceFile(PECOFFLinkingContext &ctx, - raw_ostream &diag, - std::string &resFile) { - // Create a temporary file for the resource script file. - SmallString<128> rcFileSmallString; - if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) { - diag << "Cannot create a temporary file\n"; - return false; - } - StringRef rcFile(rcFileSmallString.str()); - llvm::FileRemover rcFileRemover((Twine(rcFile))); - - // Open the temporary file for writing. - std::error_code ec; - llvm::raw_fd_ostream out(rcFileSmallString, ec, llvm::sys::fs::F_Text); - if (ec) { - diag << "Failed to open " << ctx.getManifestOutputPath() << ": " - << ec.message() << "\n"; - return false; - } - - // Write resource script to the RC file. - out << "#define LANG_ENGLISH 9\n" - << "#define SUBLANG_DEFAULT 1\n" - << "#define APP_MANIFEST " << ctx.getManifestId() << "\n" - << "#define RT_MANIFEST 24\n" - << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n" - << "APP_MANIFEST RT_MANIFEST {\n"; - quoteAndPrintXml(out, createManifestXml(ctx)); - out << "}\n"; - out.close(); - - // Create output resource file. - SmallString<128> resFileSmallString; - if (llvm::sys::fs::createTemporaryFile("tmp", "res", resFileSmallString)) { - diag << "Cannot create a temporary file"; - return false; - } - resFile = resFileSmallString.str(); - - // Register the resource file path so that the file will be deleted when the - // context's destructor is called. - ctx.registerTemporaryFile(resFile); - - // Run RC.EXE /fo tmp.res tmp.rc - std::string program = "rc.exe"; - ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); - if (!programPathOrErr) { - diag << "Unable to find " << program << " in PATH\n"; - return false; - } - const std::string &programPath = *programPathOrErr; - std::vector<const char *> args; - args.push_back(programPath.c_str()); - args.push_back("/fo"); - args.push_back(resFile.c_str()); - args.push_back("/nologo"); - args.push_back(rcFileSmallString.c_str()); - args.push_back(nullptr); - - if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { - diag << program << " failed\n"; - return false; - } - return true; -} - - -// Create the a side-by-side manifest file. -// -// The manifest file will convey some information to the linker, such as whether -// the binary needs to run as Administrator or not. Instead of being placed in -// the PE/COFF header, it's in XML format for some reason -- I guess it's -// probably because it's invented in the early dot-com era. -// -// The side-by-side manifest file is a separate XML file having ".manifest" -// extension. It will be created in the same directory as the resulting -// executable. -static bool createSideBySideManifestFile(PECOFFLinkingContext &ctx, - raw_ostream &diag) { - std::string path = ctx.getManifestOutputPath(); - if (path.empty()) { - // Default name of the manifest file is "foo.exe.manifest" where "foo.exe" is - // the output path. - path = ctx.outputPath(); - path.append(".manifest"); - } - - std::error_code ec; - llvm::raw_fd_ostream out(path, ec, llvm::sys::fs::F_Text); - if (ec) { - diag << ec.message() << "\n"; - return false; - } - out << createManifestXml(ctx); - return true; -} - -// Handle /failifmismatch option. -static bool -handleFailIfMismatchOption(StringRef option, - std::map<StringRef, StringRef> &mustMatch, - raw_ostream &diag) { - StringRef key, value; - std::tie(key, value) = option.split('='); - if (key.empty() || value.empty()) { - diag << "error: malformed /failifmismatch option: " << option << "\n"; - return true; - } - auto it = mustMatch.find(key); - if (it != mustMatch.end() && it->second != value) { - diag << "error: mismatch detected: '" << it->second << "' and '" << value - << "' for key '" << key << "'\n"; - return true; - } - mustMatch[key] = value; - return false; -} - -// -// Environment variable -// - -// Process "LINK" environment variable. If defined, the value of the variable -// should be processed as command line arguments. -static std::vector<const char *> processLinkEnv(PECOFFLinkingContext &ctx, - int argc, const char **argv) { - std::vector<const char *> ret; - // The first argument is the name of the command. This should stay at the head - // of the argument list. - assert(argc > 0); - ret.push_back(argv[0]); - - // Add arguments specified by the LINK environment variable. - llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LINK"); - if (env.hasValue()) - for (std::string &arg : splitArgList(*env)) - ret.push_back(ctx.allocate(arg).data()); - - // Add the rest of arguments passed via the command line. - for (int i = 1; i < argc; ++i) - ret.push_back(argv[i]); - ret.push_back(nullptr); - return ret; -} - -// Process "LIB" environment variable. The variable contains a list of search -// paths separated by semicolons. -static void processLibEnv(PECOFFLinkingContext &ctx) { - llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LIB"); - if (env.hasValue()) - for (StringRef path : splitPathList(*env)) - ctx.appendInputSearchPath(ctx.allocate(path)); -} - -namespace { -class DriverStringSaver : public llvm::cl::StringSaver { -public: - DriverStringSaver(PECOFFLinkingContext &ctx) : _ctx(ctx) {} - - const char *SaveString(const char *s) override { - return _ctx.allocate(StringRef(s)).data(); - } - -private: - PECOFFLinkingContext &_ctx; -}; -} - -// Tokenize command line options in a given file and add them to result. -static bool readResponseFile(StringRef path, PECOFFLinkingContext &ctx, - std::vector<const char *> &result) { - ArrayRef<uint8_t> contents; - if (!readFile(ctx, path, contents)) - return false; - StringRef contentsStr(reinterpret_cast<const char *>(contents.data()), - contents.size()); - DriverStringSaver saver(ctx); - SmallVector<const char *, 0> args; - llvm::cl::TokenizeWindowsCommandLine(contentsStr, saver, args); - for (const char *s : args) - result.push_back(s); - return true; -} - -// Expand arguments starting with "@". It's an error if a specified file does -// not exist. Returns true on success. -static bool expandResponseFiles(int &argc, const char **&argv, - PECOFFLinkingContext &ctx, raw_ostream &diag, - bool &expanded) { - std::vector<const char *> newArgv; - for (int i = 0; i < argc; ++i) { - if (argv[i][0] != '@') { - newArgv.push_back(argv[i]); - continue; - } - StringRef filename = StringRef(argv[i] + 1); - if (!readResponseFile(filename, ctx, newArgv)) { - diag << "error: cannot read response file: " << filename << "\n"; - return false; - } - expanded = true; - } - if (!expanded) - return true; - argc = newArgv.size(); - newArgv.push_back(nullptr); - argv = &ctx.allocateCopy(newArgv)[0]; - return true; -} - -// Parses the given command line options and returns the result. Returns NULL if -// there's an error in the options. -static std::unique_ptr<llvm::opt::InputArgList> -parseArgs(int argc, const char **argv, PECOFFLinkingContext &ctx, - raw_ostream &diag, bool isReadingDirectiveSection) { - // Expand arguments starting with "@". - bool expanded = false; - if (!expandResponseFiles(argc, argv, ctx, diag, expanded)) - return nullptr; - - // Parse command line options using WinLinkOptions.td - std::unique_ptr<llvm::opt::InputArgList> parsedArgs; - WinLinkOptTable table; - unsigned missingIndex; - unsigned missingCount; - parsedArgs.reset(table.ParseArgs(&argv[1], &argv[argc], - missingIndex, missingCount)); - if (missingCount) { - diag << "error: missing arg value for '" - << parsedArgs->getArgString(missingIndex) << "' expected " - << missingCount << " argument(s).\n"; - return nullptr; - } - - // Show warning for unknown arguments. In .drectve section, unknown options - // starting with "-?" are silently ignored. This is a COFF's feature to embed a - // new linker option to an object file while keeping backward compatibility. - for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) { - StringRef arg = unknownArg->getSpelling(); - if (isReadingDirectiveSection && arg.startswith("-?")) - continue; - diag << "warning: ignoring unknown argument: " << arg << "\n"; - } - - // Copy mllvm - for (auto arg : parsedArgs->filtered(OPT_mllvm)) - ctx.appendLLVMOption(arg->getValue()); - - // If we have expaneded response files and /verbose is given, print out the - // final command line. - if (!isReadingDirectiveSection && expanded && - parsedArgs->getLastArg(OPT_verbose)) { - diag << "Command line:"; - for (int i = 0; i < argc; ++i) - diag << " " << argv[i]; - diag << "\n\n"; - } - - return parsedArgs; -} - -// Returns true if the given file node has already been added to the input -// graph. -static bool hasLibrary(PECOFFLinkingContext &ctx, File *file) { - StringRef path = file->path(); - for (std::unique_ptr<Node> &p : ctx.getNodes()) - if (auto *f = dyn_cast<FileNode>(p.get())) - if (f->getFile()->path() == path) - return true; - return false; -} - -// If the first command line argument is "/lib", link.exe acts as if it's -// "lib.exe" command. This is for backward compatibility. -// http://msdn.microsoft.com/en-us/library/h34w59b3.aspx -static bool maybeRunLibCommand(int argc, const char **argv, raw_ostream &diag) { - if (argc <= 1) - return false; - if (!StringRef(argv[1]).equals_lower("/lib")) - return false; - ErrorOr<std::string> pathOrErr = llvm::sys::findProgramByName("lib.exe"); - if (!pathOrErr) { - diag << "Unable to find lib.exe in PATH\n"; - return true; - } - const std::string &path = *pathOrErr; - - // Run lib.exe - std::vector<const char *> vec; - vec.push_back(path.c_str()); - for (int i = 2; i < argc; ++i) - vec.push_back(argv[i]); - vec.push_back(nullptr); - - if (llvm::sys::ExecuteAndWait(path.c_str(), &vec[0]) != 0) - diag << "lib.exe failed\n"; - return true; -} - -/// \brief Parse the input file to lld::File. -void addFiles(PECOFFLinkingContext &ctx, StringRef path, raw_ostream &diag, - std::vector<std::unique_ptr<File>> &files) { - for (std::unique_ptr<File> &file : loadFile(ctx, path, false)) { - if (ctx.logInputFiles()) - diag << file->path() << "\n"; - files.push_back(std::move(file)); - } -} - -// -// Main driver -// - -bool WinLinkDriver::linkPECOFF(int argc, const char **argv, raw_ostream &diag) { - if (maybeRunLibCommand(argc, argv, diag)) - return true; - - PECOFFLinkingContext ctx; - ctx.setParseDirectives(parseDirectives); - ctx.registry().addSupportCOFFObjects(ctx); - ctx.registry().addSupportCOFFImportLibraries(ctx); - ctx.registry().addSupportArchives(ctx.logInputFiles()); - ctx.registry().addSupportNativeObjects(); - ctx.registry().addSupportYamlFiles(); - - std::vector<const char *> newargv = processLinkEnv(ctx, argc, argv); - processLibEnv(ctx); - if (!parse(newargv.size() - 1, &newargv[0], ctx, diag)) - return false; - - // Create the file if needed. - if (ctx.getCreateManifest() && !ctx.getEmbedManifest()) - if (!createSideBySideManifestFile(ctx, diag)) - return false; - - return link(ctx, diag); -} - -bool WinLinkDriver::parse(int argc, const char *argv[], - PECOFFLinkingContext &ctx, raw_ostream &diag, - bool isReadingDirectiveSection) { - // Parse may be called from multiple threads simultaneously to parse .drectve - // sections. This function is not thread-safe because it mutates the context - // object. So acquire the lock. - std::lock_guard<std::recursive_mutex> lock(ctx.getMutex()); - - std::map<StringRef, StringRef> failIfMismatchMap; - // Parse the options. - std::unique_ptr<llvm::opt::InputArgList> parsedArgs = - parseArgs(argc, argv, ctx, diag, isReadingDirectiveSection); - if (!parsedArgs) - return false; - - // The list of input files. - std::vector<std::unique_ptr<File>> files; - std::vector<std::unique_ptr<File>> libraries; - - // Handle /help - if (parsedArgs->hasArg(OPT_help)) { - WinLinkOptTable table; - table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); - return false; - } - - // Handle /machine before parsing all the other options, as the target machine - // type affects how to handle other options. For example, x86 needs the - // leading underscore to mangle symbols, while x64 doesn't need it. - if (llvm::opt::Arg *inputArg = parsedArgs->getLastArg(OPT_machine)) { - StringRef arg = inputArg->getValue(); - llvm::COFF::MachineTypes type = stringToMachineType(arg); - if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) { - diag << "error: unknown machine type: " << arg << "\n"; - return false; - } - ctx.setMachineType(type); - } else { - // If /machine option is missing, we need to take a look at - // the magic byte of the first object file to infer machine type. - std::vector<StringRef> filePaths; - for (auto arg : *parsedArgs) - if (arg->getOption().getID() == OPT_INPUT) - filePaths.push_back(arg->getValue()); - if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_DASH_DASH)) - filePaths.insert(filePaths.end(), arg->getValues().begin(), - arg->getValues().end()); - for (StringRef path : filePaths) { - llvm::COFF::MachineTypes type; - if (!getMachineType(path, type)) - continue; - if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) - continue; - ctx.setMachineType(type); - break; - } - } - - // Handle /nodefaultlib:<lib>. The same option without argument is handled in - // the following for loop. - for (auto *arg : parsedArgs->filtered(OPT_nodefaultlib)) - ctx.addNoDefaultLib(arg->getValue()); - - // Handle /defaultlib. Argument of the option is added to the input file list - // unless it's blacklisted by /nodefaultlib. - std::vector<StringRef> defaultLibs; - for (auto *arg : parsedArgs->filtered(OPT_defaultlib)) - defaultLibs.push_back(arg->getValue()); - - // -alternatename:<alias>=<symbol> - for (auto *arg : parsedArgs->filtered(OPT_alternatename)) { - StringRef weak, def; - if (!parseAlternateName(arg->getValue(), weak, def, diag)) - return false; - ctx.addAlternateName(weak, def); - } - - // Parse /base command line option. The argument for the parameter is in - // the form of "<address>[:<size>]". - if (auto *arg = parsedArgs->getLastArg(OPT_base)) { - uint64_t addr, size; - // Size should be set to SizeOfImage field in the COFF header, and if - // it's smaller than the actual size, the linker should warn about that. - // Currently we just ignore the value of size parameter. - if (!parseMemoryOption(arg->getValue(), addr, size)) - return false; - ctx.setBaseAddress(addr); - } - - // Parse /dll command line option - if (parsedArgs->hasArg(OPT_dll)) { - ctx.setIsDll(true); - // Default base address of a DLL is 0x10000000. - if (!parsedArgs->hasArg(OPT_base)) - ctx.setBaseAddress(0x10000000); - } - - // Parse /stack command line option - if (auto *arg = parsedArgs->getLastArg(OPT_stack)) { - uint64_t reserve; - uint64_t commit = ctx.getStackCommit(); - if (!parseMemoryOption(arg->getValue(), reserve, commit)) - return false; - ctx.setStackReserve(reserve); - ctx.setStackCommit(commit); - } - - // Parse /heap command line option - if (auto *arg = parsedArgs->getLastArg(OPT_heap)) { - uint64_t reserve; - uint64_t commit = ctx.getHeapCommit(); - if (!parseMemoryOption(arg->getValue(), reserve, commit)) - return false; - ctx.setHeapReserve(reserve); - ctx.setHeapCommit(commit); - } - - if (auto *arg = parsedArgs->getLastArg(OPT_align)) { - uint32_t align; - StringRef val = arg->getValue(); - if (val.getAsInteger(10, align)) { - diag << "error: invalid value for /align: " << val << "\n"; - return false; - } - ctx.setSectionDefaultAlignment(align); - } - - if (auto *arg = parsedArgs->getLastArg(OPT_version)) { - uint32_t major, minor; - if (!parseVersion(arg->getValue(), major, minor)) - return false; - ctx.setImageVersion(PECOFFLinkingContext::Version(major, minor)); - } - - // Parse /merge:<from>=<to>. - for (auto *arg : parsedArgs->filtered(OPT_merge)) { - StringRef from, to; - std::tie(from, to) = StringRef(arg->getValue()).split('='); - if (from.empty() || to.empty()) { - diag << "error: malformed /merge option: " << arg->getValue() << "\n"; - return false; - } - if (!ctx.addSectionRenaming(diag, from, to)) - return false; - } - - // Parse /subsystem:<subsystem>[,<majorOSVersion>[.<minorOSVersion>]]. - if (auto *arg = parsedArgs->getLastArg(OPT_subsystem)) { - llvm::COFF::WindowsSubsystem subsystem; - llvm::Optional<uint32_t> major, minor; - if (!parseSubsystem(arg->getValue(), subsystem, major, minor, diag)) - return false; - ctx.setSubsystem(subsystem); - if (major.hasValue()) - ctx.setMinOSVersion(PECOFFLinkingContext::Version(*major, *minor)); - } - - // Parse /section:name,[[!]{DEKPRSW}] - for (auto *arg : parsedArgs->filtered(OPT_section)) { - std::string section; - llvm::Optional<uint32_t> flags, mask; - if (!parseSection(arg->getValue(), section, flags, mask)) { - diag << "Unknown argument for /section: " << arg->getValue() << "\n"; - return false; - } - if (flags.hasValue()) - ctx.setSectionSetMask(section, *flags); - if (mask.hasValue()) - ctx.setSectionClearMask(section, *mask); - } - - // Parse /manifest:EMBED[,ID=#]|NO. - if (auto *arg = parsedArgs->getLastArg(OPT_manifest_colon)) { - bool enable = true; - bool embed = false; - int id = 1; - if (!parseManifest(arg->getValue(), enable, embed, id)) { - diag << "Unknown argument for /manifest: " << arg->getValue() << "\n"; - return false; - } - ctx.setCreateManifest(enable); - ctx.setEmbedManifest(embed); - ctx.setManifestId(id); - } - - // Parse /manifestuac. - if (auto *arg = parsedArgs->getLastArg(OPT_manifestuac)) { - if (StringRef(arg->getValue()).equals_lower("no")) { - ctx.setManifestUAC(false); - } else { - llvm::Optional<std::string> privilegeLevel; - llvm::Optional<std::string> uiAccess; - if (!parseManifestUAC(arg->getValue(), privilegeLevel, uiAccess)) { - diag << "Unknown argument for /manifestuac: " << arg->getValue() - << "\n"; - return false; - } - if (privilegeLevel.hasValue()) - ctx.setManifestLevel(privilegeLevel.getValue()); - if (uiAccess.hasValue()) - ctx.setManifestUiAccess(uiAccess.getValue()); - } - } - - if (auto *arg = parsedArgs->getLastArg(OPT_manifestfile)) - ctx.setManifestOutputPath(ctx.allocate(arg->getValue())); - - // /manifestdependency:<string> option. Note that the argument will be - // embedded to the manifest XML file with no error check, for link.exe - // compatibility. We do not gurantete that the resulting XML file is - // valid. - if (auto *arg = parsedArgs->getLastArg(OPT_manifestdependency)) - ctx.setManifestDependency(ctx.allocate(arg->getValue())); - - for (auto *arg : parsedArgs->filtered(OPT_failifmismatch)) - if (handleFailIfMismatchOption(arg->getValue(), failIfMismatchMap, diag)) - return false; - - if (auto *arg = parsedArgs->getLastArg(OPT_entry)) - ctx.setEntrySymbolName(ctx.allocate(arg->getValue())); - - for (auto *arg : parsedArgs->filtered(OPT_export)) { - PECOFFLinkingContext::ExportDesc desc; - if (!parseExport(arg->getValue(), desc)) { - diag << "Error: malformed /export option: " << arg->getValue() << "\n"; - return false; - } - - // Mangle the symbol name only if it is reading user-supplied command line - // arguments. Because the symbol name in the .drectve section is already - // mangled by the compiler, we shouldn't add a leading underscore in that - // case. It's odd that the command line option has different semantics in - // the .drectve section, but this behavior is needed for compatibility - // with MSVC's link.exe. - if (!isReadingDirectiveSection) - desc.name = ctx.decorateSymbol(desc.name); - ctx.addDllExport(desc); - } - - for (auto *arg : parsedArgs->filtered(OPT_deffile)) { - llvm::BumpPtrAllocator alloc; - std::vector<moduledef::Directive *> dirs; - if (!parseDef(arg->getValue(), alloc, dirs)) { - diag << "Error: invalid module-definition file\n"; - return false; - } - for (moduledef::Directive *dir : dirs) { - if (auto *exp = dyn_cast<moduledef::Exports>(dir)) { - for (PECOFFLinkingContext::ExportDesc desc : exp->getExports()) { - desc.name = ctx.decorateSymbol(desc.name); - ctx.addDllExport(desc); - } - } else if (auto *hs = dyn_cast<moduledef::Heapsize>(dir)) { - ctx.setHeapReserve(hs->getReserve()); - ctx.setHeapCommit(hs->getCommit()); - } else if (auto *lib = dyn_cast<moduledef::Library>(dir)) { - ctx.setIsDll(true); - ctx.setOutputPath(ctx.allocate(lib->getName())); - if (lib->getBaseAddress() && !ctx.getBaseAddress()) - ctx.setBaseAddress(lib->getBaseAddress()); - } else if (auto *name = dyn_cast<moduledef::Name>(dir)) { - if (!name->getOutputPath().empty() && ctx.outputPath().empty()) - ctx.setOutputPath(ctx.allocate(name->getOutputPath())); - if (name->getBaseAddress() && ctx.getBaseAddress()) - ctx.setBaseAddress(name->getBaseAddress()); - } else if (auto *ver = dyn_cast<moduledef::Version>(dir)) { - ctx.setImageVersion(PECOFFLinkingContext::Version( - ver->getMajorVersion(), ver->getMinorVersion())); - } else { - llvm::dbgs() << static_cast<int>(dir->getKind()) << "\n"; - llvm_unreachable("Unknown module-definition directive.\n"); - } - } - } - - for (auto *arg : parsedArgs->filtered(OPT_libpath)) - ctx.appendInputSearchPath(ctx.allocate(arg->getValue())); - - for (auto *arg : parsedArgs->filtered(OPT_opt)) { - std::string val = StringRef(arg->getValue()).lower(); - if (val == "noref") { - ctx.setDeadStripping(false); - } else if (val != "ref" && val != "icf" && val != "noicf" && - val != "lbr" && val != "nolbr" && - !StringRef(val).startswith("icf=")) { - diag << "unknown option for /opt: " << val << "\n"; - return false; - } - } - - // LLD is not yet capable of creating a PDB file, so /debug does not have - // any effect. - // TODO: This should disable dead stripping. Currently we can't do that - // because removal of associative sections depends on dead stripping. - if (parsedArgs->hasArg(OPT_debug)) - ctx.setDebug(true); - - if (parsedArgs->hasArg(OPT_verbose)) - ctx.setLogInputFiles(true); - - // /force and /force:unresolved mean the same thing. We do not currently - // support /force:multiple. - if (parsedArgs->hasArg(OPT_force) || - parsedArgs->hasArg(OPT_force_unresolved)) { - ctx.setAllowRemainingUndefines(true); - } - - if (parsedArgs->hasArg(OPT_fixed)) { - // /fixed is not compatible with /dynamicbase. Check for it. - if (parsedArgs->hasArg(OPT_dynamicbase)) { - diag << "/dynamicbase must not be specified with /fixed\n"; - return false; - } - ctx.setBaseRelocationEnabled(false); - ctx.setDynamicBaseEnabled(false); - } - - // /swaprun:{cd,net} options set IMAGE_FILE_{REMOVABLE,NET}_RUN_FROM_SWAP - // bits in the COFF header, respectively. If one of the bits is on, the - // Windows loader will copy the entire file to swap area then execute it, - // so that the user can eject a CD or disconnect from the network. - if (parsedArgs->hasArg(OPT_swaprun_cd)) - ctx.setSwapRunFromCD(true); - - if (parsedArgs->hasArg(OPT_swaprun_net)) - ctx.setSwapRunFromNet(true); - - if (parsedArgs->hasArg(OPT_profile)) { - // /profile implies /opt:ref, /opt:noicf, /incremental:no and /fixed:no. - ctx.setDeadStripping(true); - ctx.setBaseRelocationEnabled(true); - ctx.setDynamicBaseEnabled(true); - } - - for (auto *arg : parsedArgs->filtered(OPT_implib)) - ctx.setOutputImportLibraryPath(arg->getValue()); - - for (auto *arg : parsedArgs->filtered(OPT_delayload)) { - ctx.addInitialUndefinedSymbol(ctx.getDelayLoadHelperName()); - ctx.addDelayLoadDLL(arg->getValue()); - } - - if (auto *arg = parsedArgs->getLastArg(OPT_stub)) { - ArrayRef<uint8_t> contents; - if (!readFile(ctx, arg->getValue(), contents)) { - diag << "Failed to read DOS stub file " << arg->getValue() << "\n"; - return false; - } - ctx.setDosStub(contents); - } - - for (auto *arg : parsedArgs->filtered(OPT_incl)) - ctx.addInitialUndefinedSymbol(ctx.allocate(arg->getValue())); - - if (parsedArgs->hasArg(OPT_noentry)) - ctx.setHasEntry(false); - - if (parsedArgs->hasArg(OPT_nodefaultlib_all)) - ctx.setNoDefaultLibAll(true); - - if (auto *arg = parsedArgs->getLastArg(OPT_out)) - ctx.setOutputPath(ctx.allocate(arg->getValue())); - - if (auto *arg = parsedArgs->getLastArg(OPT_pdb)) - ctx.setPDBFilePath(arg->getValue()); - - if (auto *arg = parsedArgs->getLastArg(OPT_lldmoduledeffile)) - ctx.setModuleDefinitionFile(arg->getValue()); - - std::vector<StringRef> inputFiles; - for (auto *arg : parsedArgs->filtered(OPT_INPUT)) - inputFiles.push_back(ctx.allocate(arg->getValue())); - -#define BOOLEAN_FLAG(name, setter) \ - if (auto *arg = parsedArgs->getLastArg(OPT_##name, OPT_##name##_no)) \ - ctx.setter(arg->getOption().matches(OPT_##name)); - - BOOLEAN_FLAG(nxcompat, setNxCompat); - BOOLEAN_FLAG(largeaddressaware, setLargeAddressAware); - BOOLEAN_FLAG(allowbind, setAllowBind); - BOOLEAN_FLAG(allowisolation, setAllowIsolation); - BOOLEAN_FLAG(dynamicbase, setDynamicBaseEnabled); - BOOLEAN_FLAG(tsaware, setTerminalServerAware); - BOOLEAN_FLAG(highentropyva, setHighEntropyVA); - BOOLEAN_FLAG(safeseh, setSafeSEH); -#undef BOOLEAN_FLAG - - // Arguments after "--" are interpreted as filenames even if they - // start with a hypen or a slash. This is not compatible with link.exe - // but useful for us to test lld on Unix. - if (llvm::opt::Arg *dashdash = parsedArgs->getLastArg(OPT_DASH_DASH)) - for (const StringRef value : dashdash->getValues()) - inputFiles.push_back(value); - - // Compile Windows resource files to compiled resource file. - if (ctx.getCreateManifest() && ctx.getEmbedManifest() && - !isReadingDirectiveSection) { - std::string resFile; - if (!createManifestResourceFile(ctx, diag, resFile)) - return false; - inputFiles.push_back(ctx.allocate(resFile)); - } - - // A Windows Resource file is not an object file. It contains data, - // such as an icon image, and is not in COFF file format. If resource - // files are given, the linker merge them into one COFF file using - // CVTRES.EXE and then link the resulting file. - { - auto it = std::partition(inputFiles.begin(), inputFiles.end(), - isResoruceFile); - if (it != inputFiles.begin()) { - std::vector<std::string> resFiles(inputFiles.begin(), it); - std::string resObj; - if (!convertResourceFiles(ctx, resFiles, resObj)) { - diag << "Failed to convert resource files\n"; - return false; - } - inputFiles = std::vector<StringRef>(it, inputFiles.end()); - inputFiles.push_back(ctx.allocate(resObj)); - ctx.registerTemporaryFile(resObj); - } - } - - // Prepare objects to add them to the list of input files. - for (StringRef path : inputFiles) { - path = ctx.allocate(path); - if (isLibraryFile(path)) { - addFiles(ctx, getLibraryPath(ctx, path), diag, libraries); - } else { - addFiles(ctx, getObjectPath(ctx, path), diag, files); - } - } - - // If dead-stripping is enabled, we need to add the entry symbol and - // symbols given by /include to the dead strip root set, so that it - // won't be removed from the output. - if (ctx.deadStrip()) - for (const StringRef symbolName : ctx.initialUndefinedSymbols()) - ctx.addDeadStripRoot(symbolName); - - // Add the libraries specified by /defaultlib unless they are already added - // nor blacklisted by /nodefaultlib. - if (!ctx.getNoDefaultLibAll()) - for (const StringRef path : defaultLibs) - if (!ctx.hasNoDefaultLib(path)) - addFiles(ctx, getLibraryPath(ctx, path.lower()), diag, libraries); - - if (files.empty() && !isReadingDirectiveSection) { - diag << "No input files\n"; - return false; - } - - // If /out option was not specified, the default output file name is - // constructed by replacing an extension of the first input file - // with ".exe". - if (ctx.outputPath().empty()) { - StringRef path = files[0]->path(); - ctx.setOutputPath(replaceExtension(ctx, path, ".exe")); - } - - // Add the input files to the linking context. - for (std::unique_ptr<File> &file : files) { - if (isReadingDirectiveSection) { - File *f = file.get(); - ctx.getTaskGroup().spawn([f] { f->parse(); }); - } - ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); - } - - // Add the library group to the linking context. - if (!isReadingDirectiveSection) { - // Add a group-end marker. - ctx.getNodes().push_back(llvm::make_unique<GroupEnd>(0)); - } - - // Add the library files to the library group. - for (std::unique_ptr<File> &file : libraries) { - if (!hasLibrary(ctx, file.get())) { - if (isReadingDirectiveSection) { - File *f = file.get(); - ctx.getTaskGroup().spawn([f] { f->parse(); }); - } - ctx.addLibraryFile(llvm::make_unique<FileNode>(std::move(file))); - } - } - - // Validate the combination of options used. - return ctx.validate(diag); -} - -} // namespace lld |