diff options
Diffstat (limited to 'lib/Driver/WinLinkDriver.cpp')
-rw-r--r-- | lib/Driver/WinLinkDriver.cpp | 1371 |
1 files changed, 1371 insertions, 0 deletions
diff --git a/lib/Driver/WinLinkDriver.cpp b/lib/Driver/WinLinkDriver.cpp new file mode 100644 index 0000000000000..6ee7a5a004b5a --- /dev/null +++ b/lib/Driver/WinLinkDriver.cpp @@ -0,0 +1,1371 @@ +//===- 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 |