summaryrefslogtreecommitdiff
path: root/lib/Driver
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Driver')
-rw-r--r--lib/Driver/CMakeLists.txt43
-rw-r--r--lib/Driver/CoreDriver.cpp172
-rw-r--r--lib/Driver/CoreOptions.td15
-rw-r--r--lib/Driver/DarwinLdDriver.cpp832
-rw-r--r--lib/Driver/DarwinLdOptions.td187
-rw-r--r--lib/Driver/Driver.cpp130
-rw-r--r--lib/Driver/GnuLdDriver.cpp760
-rw-r--r--lib/Driver/GnuLdOptions.td323
-rw-r--r--lib/Driver/Makefile38
-rw-r--r--lib/Driver/TODO.rst101
-rw-r--r--lib/Driver/UniversalDriver.cpp218
-rw-r--r--lib/Driver/UniversalDriverOptions.td19
-rw-r--r--lib/Driver/WinLinkDriver.cpp1371
-rw-r--r--lib/Driver/WinLinkModuleDef.cpp295
-rw-r--r--lib/Driver/WinLinkOptions.td120
15 files changed, 4624 insertions, 0 deletions
diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt
new file mode 100644
index 0000000000000..5a410e7eed7e9
--- /dev/null
+++ b/lib/Driver/CMakeLists.txt
@@ -0,0 +1,43 @@
+set(LLVM_TARGET_DEFINITIONS UniversalDriverOptions.td)
+tablegen(LLVM UniversalDriverOptions.inc -gen-opt-parser-defs)
+set(LLVM_TARGET_DEFINITIONS GnuLdOptions.td)
+tablegen(LLVM GnuLdOptions.inc -gen-opt-parser-defs)
+set(LLVM_TARGET_DEFINITIONS CoreOptions.td)
+tablegen(LLVM CoreOptions.inc -gen-opt-parser-defs)
+set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td)
+tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs)
+set(LLVM_TARGET_DEFINITIONS WinLinkOptions.td)
+tablegen(LLVM WinLinkOptions.inc -gen-opt-parser-defs)
+add_public_tablegen_target(DriverOptionsTableGen)
+
+add_llvm_library(lldDriver
+ CoreDriver.cpp
+ DarwinLdDriver.cpp
+ Driver.cpp
+ GnuLdDriver.cpp
+ UniversalDriver.cpp
+ WinLinkDriver.cpp
+ WinLinkModuleDef.cpp
+ LINK_LIBS
+ lldConfig
+ lldMachO
+ lldPECOFF
+ lldELF
+ lldAArch64ELFTarget
+ lldARMELFTarget
+ lldHexagonELFTarget
+ lldMipsELFTarget
+ lldX86ELFTarget
+ lldExampleSubTarget
+ lldX86_64ELFTarget
+ lldCore
+ lldNative
+ lldReaderWriter
+ lldYAML
+ LLVMObject
+ LLVMOption
+ LLVMSupport
+ )
+
+add_dependencies(lldDriver DriverOptionsTableGen)
+
diff --git a/lib/Driver/CoreDriver.cpp b/lib/Driver/CoreDriver.cpp
new file mode 100644
index 0000000000000..b8adee55746f4
--- /dev/null
+++ b/lib/Driver/CoreDriver.cpp
@@ -0,0 +1,172 @@
+//===- lib/Driver/CoreDriver.cpp ------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/Reader.h"
+#include "lld/Driver/Driver.h"
+#include "lld/ReaderWriter/CoreLinkingContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lld;
+
+namespace {
+
+// Create enum with OPT_xxx values for each option in CoreOptions.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELP, META) \
+ OPT_##ID,
+#include "CoreOptions.inc"
+#undef OPTION
+};
+
+// Create prefix string literals used in CoreOptions.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "CoreOptions.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in CoreOptions.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 "CoreOptions.inc"
+#undef OPTION
+};
+
+// Create OptTable class for parsing actual command line arguments
+class CoreOptTable : public llvm::opt::OptTable {
+public:
+ CoreOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
+};
+
+} // namespace anonymous
+
+
+namespace lld {
+
+static const Registry::KindStrings coreKindStrings[] = {
+ { CoreLinkingContext::TEST_RELOC_CALL32, "call32" },
+ { CoreLinkingContext::TEST_RELOC_PCREL32, "pcrel32" },
+ { CoreLinkingContext::TEST_RELOC_GOT_LOAD32, "gotLoad32" },
+ { CoreLinkingContext::TEST_RELOC_GOT_USE32, "gotUse32" },
+ { CoreLinkingContext::TEST_RELOC_LEA32_WAS_GOT, "lea32wasGot" },
+ LLD_KIND_STRING_END
+};
+
+bool CoreDriver::link(int argc, const char *argv[], raw_ostream &diagnostics) {
+ CoreLinkingContext ctx;
+
+ // Register possible input file parsers.
+ ctx.registry().addSupportNativeObjects();
+ ctx.registry().addSupportYamlFiles();
+ ctx.registry().addKindTable(Reference::KindNamespace::testing,
+ Reference::KindArch::all, coreKindStrings);
+
+ if (!parse(argc, argv, ctx))
+ return false;
+ return Driver::link(ctx);
+}
+
+bool CoreDriver::parse(int argc, const char *argv[], CoreLinkingContext &ctx,
+ raw_ostream &diagnostics) {
+ // Parse command line options using CoreOptions.td
+ std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
+ CoreOptTable table;
+ unsigned missingIndex;
+ unsigned missingCount;
+ parsedArgs.reset(
+ table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
+ if (missingCount) {
+ diagnostics << "error: missing arg value for '"
+ << parsedArgs->getArgString(missingIndex) << "' expected "
+ << missingCount << " argument(s).\n";
+ return false;
+ }
+
+ // Set default options
+ ctx.setOutputPath("-");
+ ctx.setDeadStripping(false);
+ ctx.setGlobalsAreDeadStripRoots(false);
+ ctx.setPrintRemainingUndefines(false);
+ ctx.setAllowRemainingUndefines(true);
+ ctx.setSearchArchivesToOverrideTentativeDefinitions(false);
+
+ // Process all the arguments and create input files.
+ for (auto inputArg : *parsedArgs) {
+ switch (inputArg->getOption().getID()) {
+ case OPT_mllvm:
+ ctx.appendLLVMOption(inputArg->getValue());
+ break;
+
+ case OPT_entry:
+ ctx.setEntrySymbolName(inputArg->getValue());
+ break;
+
+ case OPT_output:
+ ctx.setOutputPath(inputArg->getValue());
+ break;
+
+ case OPT_dead_strip:
+ ctx.setDeadStripping(true);
+ break;
+
+ case OPT_keep_globals:
+ ctx.setGlobalsAreDeadStripRoots(true);
+ break;
+
+ case OPT_undefines_are_errors:
+ ctx.setPrintRemainingUndefines(true);
+ ctx.setAllowRemainingUndefines(false);
+ break;
+
+ case OPT_commons_search_archives:
+ ctx.setSearchArchivesToOverrideTentativeDefinitions(true);
+ break;
+
+ case OPT_add_pass:
+ ctx.addPassNamed(inputArg->getValue());
+ break;
+
+ case OPT_INPUT: {
+ std::vector<std::unique_ptr<File>> files
+ = loadFile(ctx, inputArg->getValue(), false);
+ for (std::unique_ptr<File> &file : files)
+ ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (ctx.getNodes().empty()) {
+ diagnostics << "No input files\n";
+ return false;
+ }
+
+ // Validate the combination of options used.
+ return ctx.validate(diagnostics);
+}
+
+} // namespace lld
diff --git a/lib/Driver/CoreOptions.td b/lib/Driver/CoreOptions.td
new file mode 100644
index 0000000000000..df7cb41737d23
--- /dev/null
+++ b/lib/Driver/CoreOptions.td
@@ -0,0 +1,15 @@
+include "llvm/Option/OptParser.td"
+
+def output : Separate<["-"], "o">;
+def entry : Separate<["-"], "e">;
+
+def dead_strip : Flag<["--"], "dead-strip">;
+def undefines_are_errors : Flag<["--"], "undefines-are-errors">;
+def keep_globals : Flag<["--"], "keep-globals">;
+def commons_search_archives : Flag<["--"], "commons-search-archives">;
+
+def add_pass : Separate<["--"], "add-pass">;
+
+def target : Separate<["-"], "target">, HelpText<"Target triple to link for">;
+def mllvm : Separate<["-"], "mllvm">, HelpText<"Options to pass to LLVM">;
+
diff --git a/lib/Driver/DarwinLdDriver.cpp b/lib/Driver/DarwinLdDriver.cpp
new file mode 100644
index 0000000000000..2c64aeee38a58
--- /dev/null
+++ b/lib/Driver/DarwinLdDriver.cpp
@@ -0,0 +1,832 @@
+//===- lib/Driver/DarwinLdDriver.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 darwin's ld.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/File.h"
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/SharedLibraryFile.h"
+#include "lld/Driver/Driver.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MachO.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+
+using namespace lld;
+
+namespace {
+
+// Create enum with OPT_xxx values for each option in DarwinLdOptions.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELP, META) \
+ OPT_##ID,
+#include "DarwinLdOptions.inc"
+#undef OPTION
+};
+
+// Create prefix string literals used in DarwinLdOptions.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "DarwinLdOptions.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in DarwinLdOptions.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 "DarwinLdOptions.inc"
+#undef OPTION
+};
+
+// Create OptTable class for parsing actual command line arguments
+class DarwinLdOptTable : public llvm::opt::OptTable {
+public:
+ DarwinLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
+};
+
+std::vector<std::unique_ptr<File>>
+loadFile(MachOLinkingContext &ctx, StringRef path,
+ raw_ostream &diag, bool wholeArchive, bool upwardDylib) {
+ if (ctx.logInputFiles())
+ diag << path << "\n";
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path);
+ if (std::error_code ec = mbOrErr.getError())
+ return makeErrorFile(path, ec);
+ std::vector<std::unique_ptr<File>> files;
+ if (std::error_code ec = ctx.registry().loadFile(std::move(mbOrErr.get()), files))
+ return makeErrorFile(path, ec);
+ for (std::unique_ptr<File> &pf : files) {
+ // If file is a dylib, inform LinkingContext about it.
+ if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(pf.get())) {
+ if (std::error_code ec = shl->parse())
+ return makeErrorFile(path, ec);
+ ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile*>(shl),
+ upwardDylib);
+ }
+ }
+ if (wholeArchive)
+ return parseMemberFiles(files);
+ return files;
+}
+
+} // anonymous namespace
+
+// Test may be running on Windows. Canonicalize the path
+// separator to '/' to get consistent outputs for tests.
+static std::string canonicalizePath(StringRef path) {
+ char sep = llvm::sys::path::get_separator().front();
+ if (sep != '/') {
+ std::string fixedPath = path;
+ std::replace(fixedPath.begin(), fixedPath.end(), sep, '/');
+ return fixedPath;
+ } else {
+ return path;
+ }
+}
+
+static void addFile(StringRef path, MachOLinkingContext &ctx,
+ bool loadWholeArchive,
+ bool upwardDylib, raw_ostream &diag) {
+ std::vector<std::unique_ptr<File>> files =
+ loadFile(ctx, path, diag, loadWholeArchive, upwardDylib);
+ for (std::unique_ptr<File> &file : files)
+ ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
+}
+
+// Export lists are one symbol per line. Blank lines are ignored.
+// Trailing comments start with #.
+static std::error_code parseExportsList(StringRef exportFilePath,
+ MachOLinkingContext &ctx,
+ raw_ostream &diagnostics) {
+ // Map in export list file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
+ MemoryBuffer::getFileOrSTDIN(exportFilePath);
+ if (std::error_code ec = mb.getError())
+ return ec;
+ ctx.addInputFileDependency(exportFilePath);
+ StringRef buffer = mb->get()->getBuffer();
+ while (!buffer.empty()) {
+ // Split off each line in the file.
+ std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
+ StringRef line = lineAndRest.first;
+ // Ignore trailing # comments.
+ std::pair<StringRef, StringRef> symAndComment = line.split('#');
+ StringRef sym = symAndComment.first.trim();
+ if (!sym.empty())
+ ctx.addExportSymbol(sym);
+ buffer = lineAndRest.second;
+ }
+ return std::error_code();
+}
+
+
+
+/// Order files are one symbol per line. Blank lines are ignored.
+/// Trailing comments start with #. Symbol names can be prefixed with an
+/// architecture name and/or .o leaf name. Examples:
+/// _foo
+/// bar.o:_bar
+/// libfrob.a(bar.o):_bar
+/// x86_64:_foo64
+static std::error_code parseOrderFile(StringRef orderFilePath,
+ MachOLinkingContext &ctx,
+ raw_ostream &diagnostics) {
+ // Map in order file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
+ MemoryBuffer::getFileOrSTDIN(orderFilePath);
+ if (std::error_code ec = mb.getError())
+ return ec;
+ ctx.addInputFileDependency(orderFilePath);
+ StringRef buffer = mb->get()->getBuffer();
+ while (!buffer.empty()) {
+ // Split off each line in the file.
+ std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
+ StringRef line = lineAndRest.first;
+ buffer = lineAndRest.second;
+ // Ignore trailing # comments.
+ std::pair<StringRef, StringRef> symAndComment = line.split('#');
+ if (symAndComment.first.empty())
+ continue;
+ StringRef sym = symAndComment.first.trim();
+ if (sym.empty())
+ continue;
+ // Check for prefix.
+ StringRef prefix;
+ std::pair<StringRef, StringRef> prefixAndSym = sym.split(':');
+ if (!prefixAndSym.second.empty()) {
+ sym = prefixAndSym.second;
+ prefix = prefixAndSym.first;
+ if (!prefix.endswith(".o") && !prefix.endswith(".o)")) {
+ // If arch name prefix does not match arch being linked, ignore symbol.
+ if (!ctx.archName().equals(prefix))
+ continue;
+ prefix = "";
+ }
+ } else
+ sym = prefixAndSym.first;
+ if (!sym.empty()) {
+ ctx.appendOrderedSymbol(sym, prefix);
+ //llvm::errs() << sym << ", prefix=" << prefix << "\n";
+ }
+ }
+ return std::error_code();
+}
+
+//
+// There are two variants of the -filelist option:
+//
+// -filelist <path>
+// In this variant, the path is to a text file which contains one file path
+// per line. There are no comments or trimming of whitespace.
+//
+// -fileList <path>,<dir>
+// In this variant, the path is to a text file which contains a partial path
+// per line. The <dir> prefix is prepended to each partial path.
+//
+static std::error_code loadFileList(StringRef fileListPath,
+ MachOLinkingContext &ctx, bool forceLoad,
+ raw_ostream &diagnostics) {
+ // If there is a comma, split off <dir>.
+ std::pair<StringRef, StringRef> opt = fileListPath.split(',');
+ StringRef filePath = opt.first;
+ StringRef dirName = opt.second;
+ ctx.addInputFileDependency(filePath);
+ // Map in file list file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
+ MemoryBuffer::getFileOrSTDIN(filePath);
+ if (std::error_code ec = mb.getError())
+ return ec;
+ StringRef buffer = mb->get()->getBuffer();
+ while (!buffer.empty()) {
+ // Split off each line in the file.
+ std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
+ StringRef line = lineAndRest.first;
+ StringRef path;
+ if (!dirName.empty()) {
+ // If there is a <dir> then prepend dir to each line.
+ SmallString<256> fullPath;
+ fullPath.assign(dirName);
+ llvm::sys::path::append(fullPath, Twine(line));
+ path = ctx.copy(fullPath.str());
+ } else {
+ // No <dir> use whole line as input file path.
+ path = ctx.copy(line);
+ }
+ if (!ctx.pathExists(path)) {
+ return make_dynamic_error_code(Twine("File not found '")
+ + path
+ + "'");
+ }
+ if (ctx.testingFileUsage()) {
+ diagnostics << "Found filelist entry " << canonicalizePath(path) << '\n';
+ }
+ addFile(path, ctx, forceLoad, false, diagnostics);
+ buffer = lineAndRest.second;
+ }
+ return std::error_code();
+}
+
+/// Parse number assuming it is base 16, but allow 0x prefix.
+static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) {
+ if (numStr.startswith_lower("0x"))
+ numStr = numStr.drop_front(2);
+ return numStr.getAsInteger(16, baseAddress);
+}
+
+namespace lld {
+
+bool DarwinLdDriver::linkMachO(int argc, const char *argv[],
+ raw_ostream &diagnostics) {
+ MachOLinkingContext ctx;
+ if (!parse(argc, argv, ctx, diagnostics))
+ return false;
+ if (ctx.doNothing())
+ return true;
+ return link(ctx, diagnostics);
+}
+
+bool DarwinLdDriver::parse(int argc, const char *argv[],
+ MachOLinkingContext &ctx, raw_ostream &diagnostics) {
+ // Parse command line options using DarwinLdOptions.td
+ std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
+ DarwinLdOptTable table;
+ unsigned missingIndex;
+ unsigned missingCount;
+ bool globalWholeArchive = false;
+ parsedArgs.reset(
+ table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
+ if (missingCount) {
+ diagnostics << "error: missing arg value for '"
+ << parsedArgs->getArgString(missingIndex) << "' expected "
+ << missingCount << " argument(s).\n";
+ return false;
+ }
+
+ for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) {
+ diagnostics << "warning: ignoring unknown argument: "
+ << unknownArg->getAsString(*parsedArgs) << "\n";
+ }
+
+ // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static )
+ llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE;
+ if ( llvm::opt::Arg *kind = parsedArgs->getLastArg(OPT_dylib, OPT_relocatable,
+ OPT_bundle, OPT_static, OPT_preload)) {
+ switch (kind->getOption().getID()) {
+ case OPT_dylib:
+ fileType = llvm::MachO::MH_DYLIB;
+ break;
+ case OPT_relocatable:
+ fileType = llvm::MachO::MH_OBJECT;
+ break;
+ case OPT_bundle:
+ fileType = llvm::MachO::MH_BUNDLE;
+ break;
+ case OPT_static:
+ fileType = llvm::MachO::MH_EXECUTE;
+ break;
+ case OPT_preload:
+ fileType = llvm::MachO::MH_PRELOAD;
+ break;
+ }
+ }
+
+ // Handle -arch xxx
+ MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown;
+ if (llvm::opt::Arg *archStr = parsedArgs->getLastArg(OPT_arch)) {
+ arch = MachOLinkingContext::archFromName(archStr->getValue());
+ if (arch == MachOLinkingContext::arch_unknown) {
+ diagnostics << "error: unknown arch named '" << archStr->getValue()
+ << "'\n";
+ return false;
+ }
+ }
+ // If no -arch specified, scan input files to find first non-fat .o file.
+ if (arch == MachOLinkingContext::arch_unknown) {
+ for (auto &inFile: parsedArgs->filtered(OPT_INPUT)) {
+ // This is expensive because it opens and maps the file. But that is
+ // ok because no -arch is rare.
+ if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch))
+ break;
+ }
+ if (arch == MachOLinkingContext::arch_unknown
+ && !parsedArgs->getLastArg(OPT_test_file_usage)) {
+ // If no -arch and no options at all, print usage message.
+ if (parsedArgs->size() == 0)
+ table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false);
+ else
+ diagnostics << "error: -arch not specified and could not be inferred\n";
+ return false;
+ }
+ }
+
+ // Handle -macosx_version_min or -ios_version_min
+ MachOLinkingContext::OS os = MachOLinkingContext::OS::macOSX;
+ uint32_t minOSVersion = 0;
+ if (llvm::opt::Arg *minOS =
+ parsedArgs->getLastArg(OPT_macosx_version_min, OPT_ios_version_min,
+ OPT_ios_simulator_version_min)) {
+ switch (minOS->getOption().getID()) {
+ case OPT_macosx_version_min:
+ os = MachOLinkingContext::OS::macOSX;
+ if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
+ minOSVersion)) {
+ diagnostics << "error: malformed macosx_version_min value\n";
+ return false;
+ }
+ break;
+ case OPT_ios_version_min:
+ os = MachOLinkingContext::OS::iOS;
+ if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
+ minOSVersion)) {
+ diagnostics << "error: malformed ios_version_min value\n";
+ return false;
+ }
+ break;
+ case OPT_ios_simulator_version_min:
+ os = MachOLinkingContext::OS::iOS_simulator;
+ if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
+ minOSVersion)) {
+ diagnostics << "error: malformed ios_simulator_version_min value\n";
+ return false;
+ }
+ break;
+ }
+ } else {
+ // No min-os version on command line, check environment variables
+ }
+
+ // Now that there's enough information parsed in, let the linking context
+ // set up default values.
+ ctx.configure(fileType, arch, os, minOSVersion);
+
+ // Handle -e xxx
+ if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry))
+ ctx.setEntrySymbolName(entry->getValue());
+
+ // Handle -o xxx
+ if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_output))
+ ctx.setOutputPath(outpath->getValue());
+ else
+ ctx.setOutputPath("a.out");
+
+ // Handle -image_base XXX and -seg1addr XXXX
+ if (llvm::opt::Arg *imageBase = parsedArgs->getLastArg(OPT_image_base)) {
+ uint64_t baseAddress;
+ if (parseNumberBase16(imageBase->getValue(), baseAddress)) {
+ diagnostics << "error: image_base expects a hex number\n";
+ return false;
+ } else if (baseAddress < ctx.pageZeroSize()) {
+ diagnostics << "error: image_base overlaps with __PAGEZERO\n";
+ return false;
+ } else if (baseAddress % ctx.pageSize()) {
+ diagnostics << "error: image_base must be a multiple of page size ("
+ << llvm::format("0x%" PRIx64, ctx.pageSize()) << ")\n";
+ return false;
+ }
+
+ ctx.setBaseAddress(baseAddress);
+ }
+
+ // Handle -dead_strip
+ if (parsedArgs->getLastArg(OPT_dead_strip))
+ ctx.setDeadStripping(true);
+
+ // Handle -all_load
+ if (parsedArgs->getLastArg(OPT_all_load))
+ globalWholeArchive = true;
+
+ // Handle -install_name
+ if (llvm::opt::Arg *installName = parsedArgs->getLastArg(OPT_install_name))
+ ctx.setInstallName(installName->getValue());
+ else
+ ctx.setInstallName(ctx.outputPath());
+
+ // Handle -mark_dead_strippable_dylib
+ if (parsedArgs->getLastArg(OPT_mark_dead_strippable_dylib))
+ ctx.setDeadStrippableDylib(true);
+
+ // Handle -compatibility_version and -current_version
+ if (llvm::opt::Arg *vers =
+ parsedArgs->getLastArg(OPT_compatibility_version)) {
+ if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
+ diagnostics
+ << "error: -compatibility_version can only be used with -dylib\n";
+ return false;
+ }
+ uint32_t parsedVers;
+ if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
+ diagnostics << "error: -compatibility_version value is malformed\n";
+ return false;
+ }
+ ctx.setCompatibilityVersion(parsedVers);
+ }
+
+ if (llvm::opt::Arg *vers = parsedArgs->getLastArg(OPT_current_version)) {
+ if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
+ diagnostics << "-current_version can only be used with -dylib\n";
+ return false;
+ }
+ uint32_t parsedVers;
+ if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
+ diagnostics << "error: -current_version value is malformed\n";
+ return false;
+ }
+ ctx.setCurrentVersion(parsedVers);
+ }
+
+ // Handle -bundle_loader
+ if (llvm::opt::Arg *loader = parsedArgs->getLastArg(OPT_bundle_loader))
+ ctx.setBundleLoader(loader->getValue());
+
+ // Handle -sectalign segname sectname align
+ for (auto &alignArg : parsedArgs->filtered(OPT_sectalign)) {
+ const char* segName = alignArg->getValue(0);
+ const char* sectName = alignArg->getValue(1);
+ const char* alignStr = alignArg->getValue(2);
+ if ((alignStr[0] == '0') && (alignStr[1] == 'x'))
+ alignStr += 2;
+ unsigned long long alignValue;
+ if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) {
+ diagnostics << "error: -sectalign alignment value '"
+ << alignStr << "' not a valid number\n";
+ return false;
+ }
+ uint8_t align2 = llvm::countTrailingZeros(alignValue);
+ if ( (unsigned long)(1 << align2) != alignValue ) {
+ diagnostics << "warning: alignment for '-sectalign "
+ << segName << " " << sectName
+ << llvm::format(" 0x%llX", alignValue)
+ << "' is not a power of two, using "
+ << llvm::format("0x%08X", (1 << align2)) << "\n";
+ }
+ ctx.addSectionAlignment(segName, sectName, align2);
+ }
+
+ // Handle -mllvm
+ for (auto &llvmArg : parsedArgs->filtered(OPT_mllvm)) {
+ ctx.appendLLVMOption(llvmArg->getValue());
+ }
+
+ // Handle -print_atoms
+ if (parsedArgs->getLastArg(OPT_print_atoms))
+ ctx.setPrintAtoms();
+
+ // Handle -t (trace) option.
+ if (parsedArgs->getLastArg(OPT_t))
+ ctx.setLogInputFiles(true);
+
+ // Handle -demangle option.
+ if (parsedArgs->getLastArg(OPT_demangle))
+ ctx.setDemangleSymbols(true);
+
+ // Handle -keep_private_externs
+ if (parsedArgs->getLastArg(OPT_keep_private_externs)) {
+ ctx.setKeepPrivateExterns(true);
+ if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
+ diagnostics << "warning: -keep_private_externs only used in -r mode\n";
+ }
+
+ // Handle -dependency_info <path> used by Xcode.
+ if (llvm::opt::Arg *depInfo = parsedArgs->getLastArg(OPT_dependency_info)) {
+ if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) {
+ diagnostics << "warning: " << ec.message()
+ << ", processing '-dependency_info "
+ << depInfo->getValue()
+ << "'\n";
+ }
+ }
+
+ // In -test_file_usage mode, we'll be given an explicit list of paths that
+ // exist. We'll also be expected to print out information about how we located
+ // libraries and so on that the user specified, but not to actually do any
+ // linking.
+ if (parsedArgs->getLastArg(OPT_test_file_usage)) {
+ ctx.setTestingFileUsage();
+
+ // With paths existing by fiat, linking is not going to end well.
+ ctx.setDoNothing(true);
+
+ // Only bother looking for an existence override if we're going to use it.
+ for (auto existingPath : parsedArgs->filtered(OPT_path_exists)) {
+ ctx.addExistingPathForDebug(existingPath->getValue());
+ }
+ }
+
+ // Register possible input file parsers.
+ if (!ctx.doNothing()) {
+ ctx.registry().addSupportMachOObjects(ctx);
+ ctx.registry().addSupportArchives(ctx.logInputFiles());
+ ctx.registry().addSupportNativeObjects();
+ ctx.registry().addSupportYamlFiles();
+ }
+
+ // Now construct the set of library search directories, following ld64's
+ // baroque set of accumulated hacks. Mostly, the algorithm constructs
+ // { syslibroots } x { libpaths }
+ //
+ // Unfortunately, there are numerous exceptions:
+ // 1. Only absolute paths get modified by syslibroot options.
+ // 2. If there is just 1 -syslibroot, system paths not found in it are
+ // skipped.
+ // 3. If the last -syslibroot is "/", all of them are ignored entirely.
+ // 4. If { syslibroots } x path == {}, the original path is kept.
+ std::vector<StringRef> sysLibRoots;
+ for (auto syslibRoot : parsedArgs->filtered(OPT_syslibroot)) {
+ sysLibRoots.push_back(syslibRoot->getValue());
+ }
+ if (!sysLibRoots.empty()) {
+ // Ignore all if last -syslibroot is "/".
+ if (sysLibRoots.back() != "/")
+ ctx.setSysLibRoots(sysLibRoots);
+ }
+
+ // Paths specified with -L come first, and are not considered system paths for
+ // the case where there is precisely 1 -syslibroot.
+ for (auto libPath : parsedArgs->filtered(OPT_L)) {
+ ctx.addModifiedSearchDir(libPath->getValue());
+ }
+
+ // Process -F directories (where to look for frameworks).
+ for (auto fwPath : parsedArgs->filtered(OPT_F)) {
+ ctx.addFrameworkSearchDir(fwPath->getValue());
+ }
+
+ // -Z suppresses the standard search paths.
+ if (!parsedArgs->hasArg(OPT_Z)) {
+ ctx.addModifiedSearchDir("/usr/lib", true);
+ ctx.addModifiedSearchDir("/usr/local/lib", true);
+ ctx.addFrameworkSearchDir("/Library/Frameworks", true);
+ ctx.addFrameworkSearchDir("/System/Library/Frameworks", true);
+ }
+
+ // Now that we've constructed the final set of search paths, print out those
+ // search paths in verbose mode.
+ if (parsedArgs->getLastArg(OPT_v)) {
+ diagnostics << "Library search paths:\n";
+ for (auto path : ctx.searchDirs()) {
+ diagnostics << " " << path << '\n';
+ }
+ diagnostics << "Framework search paths:\n";
+ for (auto path : ctx.frameworkDirs()) {
+ diagnostics << " " << path << '\n';
+ }
+ }
+
+ // Handle -exported_symbols_list <file>
+ for (auto expFile : parsedArgs->filtered(OPT_exported_symbols_list)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
+ diagnostics << "error: -exported_symbols_list cannot be combined "
+ << "with -unexported_symbol[s_list]\n";
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
+ if (std::error_code ec = parseExportsList(expFile->getValue(), ctx,
+ diagnostics)) {
+ diagnostics << "error: " << ec.message()
+ << ", processing '-exported_symbols_list "
+ << expFile->getValue()
+ << "'\n";
+ return false;
+ }
+ }
+
+ // Handle -exported_symbol <symbol>
+ for (auto symbol : parsedArgs->filtered(OPT_exported_symbol)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
+ diagnostics << "error: -exported_symbol cannot be combined "
+ << "with -unexported_symbol[s_list]\n";
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
+ ctx.addExportSymbol(symbol->getValue());
+ }
+
+ // Handle -unexported_symbols_list <file>
+ for (auto expFile : parsedArgs->filtered(OPT_unexported_symbols_list)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
+ diagnostics << "error: -unexported_symbols_list cannot be combined "
+ << "with -exported_symbol[s_list]\n";
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
+ if (std::error_code ec = parseExportsList(expFile->getValue(), ctx,
+ diagnostics)) {
+ diagnostics << "error: " << ec.message()
+ << ", processing '-unexported_symbols_list "
+ << expFile->getValue()
+ << "'\n";
+ return false;
+ }
+ }
+
+ // Handle -unexported_symbol <symbol>
+ for (auto symbol : parsedArgs->filtered(OPT_unexported_symbol)) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
+ diagnostics << "error: -unexported_symbol cannot be combined "
+ << "with -exported_symbol[s_list]\n";
+ return false;
+ }
+ ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
+ ctx.addExportSymbol(symbol->getValue());
+ }
+
+ // Handle obosolete -multi_module and -single_module
+ if (llvm::opt::Arg *mod = parsedArgs->getLastArg(OPT_multi_module,
+ OPT_single_module)) {
+ if (mod->getOption().getID() == OPT_multi_module) {
+ diagnostics << "warning: -multi_module is obsolete and being ignored\n";
+ }
+ else {
+ if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
+ diagnostics << "warning: -single_module being ignored. "
+ "It is only for use when producing a dylib\n";
+ }
+ }
+ }
+
+ // Handle -pie or -no_pie
+ if (llvm::opt::Arg *pie = parsedArgs->getLastArg(OPT_pie, OPT_no_pie)) {
+ switch (ctx.outputMachOType()) {
+ case llvm::MachO::MH_EXECUTE:
+ switch (ctx.os()) {
+ case MachOLinkingContext::OS::macOSX:
+ if ((minOSVersion < 0x000A0500) &&
+ (pie->getOption().getID() == OPT_pie)) {
+ diagnostics << "-pie can only be used when targeting "
+ "Mac OS X 10.5 or later\n";
+ return false;
+ }
+ break;
+ case MachOLinkingContext::OS::iOS:
+ if ((minOSVersion < 0x00040200) &&
+ (pie->getOption().getID() == OPT_pie)) {
+ diagnostics << "-pie can only be used when targeting "
+ "iOS 4.2 or later\n";
+ return false;
+ }
+ break;
+ case MachOLinkingContext::OS::iOS_simulator:
+ if (pie->getOption().getID() == OPT_no_pie)
+ diagnostics << "iOS simulator programs must be built PIE\n";
+ return false;
+ break;
+ case MachOLinkingContext::OS::unknown:
+ break;
+ }
+ ctx.setPIE(pie->getOption().getID() == OPT_pie);
+ break;
+ case llvm::MachO::MH_PRELOAD:
+ break;
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ diagnostics << "warning: " << pie->getSpelling() << " being ignored. "
+ << "It is only used when linking main executables\n";
+ break;
+ default:
+ diagnostics << pie->getSpelling()
+ << " can only used when linking main executables\n";
+ return false;
+ break;
+ }
+ }
+
+ // Handle debug info handling options: -S
+ if (parsedArgs->hasArg(OPT_S))
+ ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap);
+
+ // Handle -order_file <file>
+ for (auto orderFile : parsedArgs->filtered(OPT_order_file)) {
+ if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx,
+ diagnostics)) {
+ diagnostics << "error: " << ec.message()
+ << ", processing '-order_file "
+ << orderFile->getValue()
+ << "'\n";
+ return false;
+ }
+ }
+
+ // Handle -rpath <path>
+ if (parsedArgs->hasArg(OPT_rpath)) {
+ switch (ctx.outputMachOType()) {
+ case llvm::MachO::MH_EXECUTE:
+ case llvm::MachO::MH_DYLIB:
+ case llvm::MachO::MH_BUNDLE:
+ if (!ctx.minOS("10.5", "2.0")) {
+ if (ctx.os() == MachOLinkingContext::OS::macOSX) {
+ diagnostics << "error: -rpath can only be used when targeting "
+ "OS X 10.5 or later\n";
+ } else {
+ diagnostics << "error: -rpath can only be used when targeting "
+ "iOS 2.0 or later\n";
+ }
+ return false;
+ }
+ break;
+ default:
+ diagnostics << "error: -rpath can only be used when creating "
+ "a dynamic final linked image\n";
+ return false;
+ }
+
+ for (auto rPath : parsedArgs->filtered(OPT_rpath)) {
+ ctx.addRpath(rPath->getValue());
+ }
+ }
+
+ // Handle input files
+ for (auto &arg : *parsedArgs) {
+ bool upward;
+ ErrorOr<StringRef> resolvedPath = StringRef();
+ switch (arg->getOption().getID()) {
+ default:
+ continue;
+ case OPT_INPUT:
+ addFile(arg->getValue(), ctx, globalWholeArchive, false, diagnostics);
+ break;
+ case OPT_upward_library:
+ addFile(arg->getValue(), ctx, false, true, diagnostics);
+ break;
+ case OPT_force_load:
+ addFile(arg->getValue(), ctx, true, false, diagnostics);
+ break;
+ case OPT_l:
+ case OPT_upward_l:
+ upward = (arg->getOption().getID() == OPT_upward_l);
+ resolvedPath = ctx.searchLibrary(arg->getValue());
+ if (!resolvedPath) {
+ diagnostics << "Unable to find library for " << arg->getSpelling()
+ << arg->getValue() << "\n";
+ return false;
+ } else if (ctx.testingFileUsage()) {
+ diagnostics << "Found " << (upward ? "upward " : " ") << "library "
+ << canonicalizePath(resolvedPath.get()) << '\n';
+ }
+ addFile(resolvedPath.get(), ctx, globalWholeArchive, upward, diagnostics);
+ break;
+ case OPT_framework:
+ case OPT_upward_framework:
+ upward = (arg->getOption().getID() == OPT_upward_framework);
+ resolvedPath = ctx.findPathForFramework(arg->getValue());
+ if (!resolvedPath) {
+ diagnostics << "Unable to find framework for "
+ << arg->getSpelling() << " " << arg->getValue() << "\n";
+ return false;
+ } else if (ctx.testingFileUsage()) {
+ diagnostics << "Found " << (upward ? "upward " : " ") << "framework "
+ << canonicalizePath(resolvedPath.get()) << '\n';
+ }
+ addFile(resolvedPath.get(), ctx, globalWholeArchive, upward, diagnostics);
+ break;
+ case OPT_filelist:
+ if (std::error_code ec = loadFileList(arg->getValue(),
+ ctx, globalWholeArchive,
+ diagnostics)) {
+ diagnostics << "error: " << ec.message()
+ << ", processing '-filelist " << arg->getValue()
+ << "'\n";
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (ctx.getNodes().empty()) {
+ diagnostics << "No input files\n";
+ return false;
+ }
+
+ // Validate the combination of options used.
+ return ctx.validate(diagnostics);
+}
+
+
+} // namespace lld
diff --git a/lib/Driver/DarwinLdOptions.td b/lib/Driver/DarwinLdOptions.td
new file mode 100644
index 0000000000000..81dcc0a1d9251
--- /dev/null
+++ b/lib/Driver/DarwinLdOptions.td
@@ -0,0 +1,187 @@
+include "llvm/Option/OptParser.td"
+
+
+// output kinds
+def grp_kind : OptionGroup<"outs">, HelpText<"OUTPUT KIND">;
+def relocatable : Flag<["-"], "r">,
+ HelpText<"Create relocatable object file">, Group<grp_kind>;
+def static : Flag<["-"], "static">,
+ HelpText<"Create static executable">, Group<grp_kind>;
+def dynamic : Flag<["-"], "dynamic">,
+ HelpText<"Create dynamic executable (default)">,Group<grp_kind>;
+def dylib : Flag<["-"], "dylib">,
+ HelpText<"Create dynamic library">, Group<grp_kind>;
+def bundle : Flag<["-"], "bundle">,
+ HelpText<"Create dynamic bundle">, Group<grp_kind>;
+def execute : Flag<["-"], "execute">,
+ HelpText<"Create main executable (default)">, Group<grp_kind>;
+def preload : Flag<["-"], "preload">,
+ HelpText<"Create binary for use with embedded systems">, Group<grp_kind>;
+
+// optimizations
+def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">;
+def dead_strip : Flag<["-"], "dead_strip">,
+ HelpText<"Remove unreference code and data">, Group<grp_opts>;
+def macosx_version_min : Separate<["-"], "macosx_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Minimum Mac OS X version">, Group<grp_opts>;
+def ios_version_min : Separate<["-"], "ios_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Minimum iOS version">, Group<grp_opts>;
+def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">,
+ Alias<ios_version_min>;
+def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Minimum iOS simulator version">, Group<grp_opts>;
+def mllvm : Separate<["-"], "mllvm">,
+ MetaVarName<"<option>">,
+ HelpText<"Options to pass to LLVM during LTO">, Group<grp_opts>;
+def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
+ MetaVarName<"<file-path>">,
+ HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
+def exported_symbol : Separate<["-"], "exported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
+def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
+ MetaVarName<"<file-path>">,
+ HelpText<"Lists symbols that should not be exported">, Group<grp_opts>;
+def unexported_symbol : Separate<["-"], "unexported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"A symbol which should not be exported">, Group<grp_opts>;
+def keep_private_externs : Flag<["-"], "keep_private_externs">,
+ HelpText<"Private extern (hidden) symbols should not be transformed "
+ "into local symbols">, Group<grp_opts>;
+def order_file : Separate<["-"], "order_file">,
+ MetaVarName<"<file-path>">,
+ HelpText<"re-order and move specified symbols to start of their section">,
+ Group<grp_opts>;
+
+// main executable options
+def grp_main : OptionGroup<"opts">, HelpText<"MAIN EXECUTABLE OPTIONS">;
+def entry : Separate<["-"], "e">,
+ MetaVarName<"<entry-name>">,
+ HelpText<"entry symbol name">,Group<grp_main>;
+def pie : Flag<["-"], "pie">,
+ HelpText<"Create Position Independent Executable (for ASLR)">,
+ Group<grp_main>;
+def no_pie : Flag<["-"], "no_pie">,
+ HelpText<"Do not create Position Independent Executable">,
+ Group<grp_main>;
+
+// dylib executable options
+def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">;
+def install_name : Separate<["-"], "install_name">,
+ MetaVarName<"<path>">,
+ HelpText<"The dylib's install name">, Group<grp_dylib>;
+def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
+ HelpText<"Marks the dylib as having no side effects during initialization">,
+ Group<grp_dylib>;
+def compatibility_version : Separate<["-"], "compatibility_version">,
+ MetaVarName<"<version>">,
+ HelpText<"The dylib's compatibility version">, Group<grp_dylib>;
+def current_version : Separate<["-"], "current_version">,
+ MetaVarName<"<version>">,
+ HelpText<"The dylib's current version">, Group<grp_dylib>;
+
+// dylib executable options - compatibility aliases
+def dylib_install_name : Separate<["-"], "dylib_install_name">,
+ Alias<install_name>;
+def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">,
+ MetaVarName<"<version>">, Alias<compatibility_version>;
+def dylib_current_version : Separate<["-"], "dylib_current_version">,
+ MetaVarName<"<version>">, Alias<current_version>;
+
+// bundle executable options
+def grp_bundle : OptionGroup<"opts">, HelpText<"BUNDLE EXECUTABLE OPTIONS">;
+def bundle_loader : Separate<["-"], "bundle_loader">,
+ MetaVarName<"<path>">,
+ HelpText<"The executable that will be loading this Mach-O bundle">,
+ Group<grp_bundle>;
+
+// library options
+def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">;
+def L : JoinedOrSeparate<["-"], "L">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add directory to library search path">, Group<grp_libs>;
+def F : JoinedOrSeparate<["-"], "F">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add directory to framework search path">, Group<grp_libs>;
+def Z : Flag<["-"], "Z">,
+ HelpText<"Do not search standard directories for libraries or frameworks">;
+def all_load : Flag<["-"], "all_load">,
+ HelpText<"Forces all members of all static libraries to be loaded">,
+ Group<grp_libs>;
+def force_load : Separate<["-"], "force_load">,
+ MetaVarName<"<library-path>">,
+ HelpText<"Forces all members of specified static libraries to be loaded">,
+ Group<grp_libs>;
+def syslibroot : Separate<["-"], "syslibroot">, MetaVarName<"<dir>">,
+ HelpText<"Add path to SDK to all absolute library search paths">,
+ Group<grp_libs>;
+
+// Input options
+def l : Joined<["-"], "l">,
+ MetaVarName<"<libname>">,
+ HelpText<"Base name of library searched for in -L directories">;
+def upward_l : Joined<["-"], "upward-l">,
+ MetaVarName<"<libname>">,
+ HelpText<"Base name of upward library searched for in -L directories">;
+def framework : Separate<["-"], "framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Base name of framework searched for in -F directories">;
+def upward_framework : Separate<["-"], "upward_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Base name of upward framework searched for in -F directories">;
+def upward_library : Separate<["-"], "upward_library">,
+ MetaVarName<"<path>">,
+ HelpText<"path to upward dylib to link with">;
+def filelist : Separate<["-"], "filelist">,
+ MetaVarName<"<path>">,
+ HelpText<"file containing paths to input files">;
+
+
+// test case options
+def print_atoms : Flag<["-"], "print_atoms">,
+ HelpText<"Emit output as yaml atoms">;
+def test_file_usage : Flag<["-"], "test_file_usage">,
+ HelpText<"Only files specified by -file_exists are considered to exist. "
+ "Print which files would be used">;
+def path_exists : Separate<["-"], "path_exists">,
+ MetaVarName<"<path>">,
+ HelpText<"Used with -test_file_usage to declare a path">;
+
+
+// general options
+def output : Separate<["-"], "o">,
+ MetaVarName<"<path>">,
+ HelpText<"Output file path">;
+def arch : Separate<["-"], "arch">,
+ MetaVarName<"<arch-name>">,
+ HelpText<"Architecture to link">;
+def sectalign : MultiArg<["-"], "sectalign", 3>,
+ MetaVarName<"<segname> <sectname> <alignment>">,
+ HelpText<"alignment for segment/section">;
+def image_base : Separate<["-"], "image_base">;
+def seg1addr : Separate<["-"], "seg1addr">, Alias<image_base>;
+def demangle : Flag<["-"], "demangle">,
+ HelpText<"Demangles symbol names in errors and warnings">;
+def dependency_info : Separate<["-"], "dependency_info">,
+ MetaVarName<"<file>">,
+ HelpText<"Write binary list of files used during link">;
+def S : Flag<["-"], "S">,
+ HelpText<"Remove debug information (STABS or DWARF) from the output file">;
+def rpath : Separate<["-"], "rpath">,
+ MetaVarName<"<path>">,
+ HelpText<"Add path to the runpath search path list for image being created">;
+
+def t : Flag<["-"], "t">,
+ HelpText<"Print the names of the input files as ld processes them">;
+def v : Flag<["-"], "v">,
+ HelpText<"Print linker information">;
+
+// Obsolete options
+def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE OPTIONS">;
+def single_module : Flag<["-"], "single_module">,
+ HelpText<"Default for dylibs">, Group<grp_obsolete>;
+def multi_module : Flag<["-"], "multi_module">,
+ HelpText<"Unsupported way to build dylibs">, Group<grp_obsolete>;
diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp
new file mode 100644
index 0000000000000..d32bfa6e47beb
--- /dev/null
+++ b/lib/Driver/Driver.cpp
@@ -0,0 +1,130 @@
+//===- lib/Driver/Driver.cpp - Linker Driver Emulator ---------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Instrumentation.h"
+#include "lld/Core/LLVM.h"
+#include "lld/Core/Parallel.h"
+#include "lld/Core/PassManager.h"
+#include "lld/Core/Reader.h"
+#include "lld/Core/Resolver.h"
+#include "lld/Core/Writer.h"
+#include "lld/Driver/Driver.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/raw_ostream.h"
+#include <mutex>
+
+namespace lld {
+
+FileVector makeErrorFile(StringRef path, std::error_code ec) {
+ std::vector<std::unique_ptr<File>> result;
+ result.push_back(llvm::make_unique<ErrorFile>(path, ec));
+ return result;
+}
+
+FileVector parseMemberFiles(FileVector &files) {
+ std::vector<std::unique_ptr<File>> members;
+ for (std::unique_ptr<File> &file : files) {
+ if (auto *archive = dyn_cast<ArchiveLibraryFile>(file.get())) {
+ if (std::error_code ec = archive->parseAllMembers(members))
+ return makeErrorFile(file->path(), ec);
+ } else {
+ members.push_back(std::move(file));
+ }
+ }
+ return members;
+}
+
+FileVector loadFile(LinkingContext &ctx, StringRef path, bool wholeArchive) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb
+ = MemoryBuffer::getFileOrSTDIN(path);
+ if (std::error_code ec = mb.getError())
+ return makeErrorFile(path, ec);
+ std::vector<std::unique_ptr<File>> files;
+ if (std::error_code ec = ctx.registry().loadFile(std::move(mb.get()), files))
+ return makeErrorFile(path, ec);
+ if (wholeArchive)
+ return parseMemberFiles(files);
+ return files;
+}
+
+/// This is where the link is actually performed.
+bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) {
+ // Honor -mllvm
+ if (!context.llvmOptions().empty()) {
+ unsigned numArgs = context.llvmOptions().size();
+ const char **args = new const char *[numArgs + 2];
+ args[0] = "lld (LLVM option parsing)";
+ for (unsigned i = 0; i != numArgs; ++i)
+ args[i + 1] = context.llvmOptions()[i];
+ args[numArgs + 1] = 0;
+ llvm::cl::ParseCommandLineOptions(numArgs + 1, args);
+ }
+ if (context.getNodes().empty())
+ return false;
+
+ for (std::unique_ptr<Node> &ie : context.getNodes())
+ if (FileNode *node = dyn_cast<FileNode>(ie.get()))
+ context.getTaskGroup().spawn([node] { node->getFile()->parse(); });
+
+ std::vector<std::unique_ptr<File>> internalFiles;
+ context.createInternalFiles(internalFiles);
+ for (auto i = internalFiles.rbegin(), e = internalFiles.rend(); i != e; ++i) {
+ auto &members = context.getNodes();
+ members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
+ }
+
+ // Give target a chance to add files.
+ std::vector<std::unique_ptr<File>> implicitFiles;
+ context.createImplicitFiles(implicitFiles);
+ for (auto i = implicitFiles.rbegin(), e = implicitFiles.rend(); i != e; ++i) {
+ auto &members = context.getNodes();
+ members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
+ }
+
+ // Give target a chance to postprocess input files.
+ // Mach-O uses this chance to move all object files before library files.
+ // ELF adds specific undefined symbols resolver.
+ context.finalizeInputFiles();
+
+ // Do core linking.
+ ScopedTask resolveTask(getDefaultDomain(), "Resolve");
+ Resolver resolver(context);
+ if (!resolver.resolve())
+ return false;
+ std::unique_ptr<MutableFile> merged = resolver.resultFile();
+ resolveTask.end();
+
+ // Run passes on linked atoms.
+ ScopedTask passTask(getDefaultDomain(), "Passes");
+ PassManager pm;
+ context.addPasses(pm);
+ pm.runOnFile(merged);
+ passTask.end();
+
+ // Give linked atoms to Writer to generate output file.
+ ScopedTask writeTask(getDefaultDomain(), "Write");
+ if (std::error_code ec = context.writeFile(*merged)) {
+ diagnostics << "Failed to write file '" << context.outputPath()
+ << "': " << ec.message() << "\n";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
diff --git a/lib/Driver/GnuLdDriver.cpp b/lib/Driver/GnuLdDriver.cpp
new file mode 100644
index 0000000000000..b9af04d4b6152
--- /dev/null
+++ b/lib/Driver/GnuLdDriver.cpp
@@ -0,0 +1,760 @@
+//===- lib/Driver/GnuLdDriver.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 GNU's ld.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lld/Driver/Driver.h"
+#include "lld/ReaderWriter/ELFLinkingContext.h"
+#include "lld/ReaderWriter/ELFTargets.h"
+#include "lld/ReaderWriter/LinkerScript.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstring>
+#include <tuple>
+
+using namespace lld;
+
+using llvm::BumpPtrAllocator;
+
+namespace {
+
+// Create enum with OPT_xxx values for each option in GnuLdOptions.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELP, META) \
+ OPT_##ID,
+#include "GnuLdOptions.inc"
+#undef OPTION
+};
+
+// Create prefix string literals used in GnuLdOptions.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "GnuLdOptions.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in GnuLdOptions.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 "GnuLdOptions.inc"
+#undef OPTION
+};
+
+
+// Create OptTable class for parsing actual command line arguments
+class GnuLdOptTable : public llvm::opt::OptTable {
+public:
+ GnuLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
+};
+
+class DriverStringSaver : public llvm::cl::StringSaver {
+public:
+ DriverStringSaver(BumpPtrAllocator &alloc) : _alloc(alloc) {}
+
+ const char *SaveString(const char *s) override {
+ char *p = _alloc.Allocate<char>(strlen(s) + 1);
+ strcpy(p, s);
+ return p;
+ }
+
+private:
+ BumpPtrAllocator &_alloc;
+};
+
+} // anonymous namespace
+
+// If a command line option starts with "@", the driver reads its suffix as a
+// file, parse its contents as a list of command line options, and insert them
+// at the original @file position. If file cannot be read, @file is not expanded
+// and left unmodified. @file can appear in a response file, so it's a recursive
+// process.
+static std::tuple<int, const char **>
+maybeExpandResponseFiles(int argc, const char **argv, BumpPtrAllocator &alloc) {
+ // Expand response files.
+ SmallVector<const char *, 256> smallvec;
+ for (int i = 0; i < argc; ++i)
+ smallvec.push_back(argv[i]);
+ DriverStringSaver saver(alloc);
+ llvm::cl::ExpandResponseFiles(saver, llvm::cl::TokenizeGNUCommandLine, smallvec);
+
+ // Pack the results to a C-array and return it.
+ argc = smallvec.size();
+ const char **copy = alloc.Allocate<const char *>(argc + 1);
+ std::copy(smallvec.begin(), smallvec.end(), copy);
+ copy[argc] = nullptr;
+ return std::make_tuple(argc, copy);
+}
+
+static std::error_code
+getFileMagic(StringRef path, llvm::sys::fs::file_magic &magic) {
+ std::error_code ec = llvm::sys::fs::identify_magic(path, magic);
+ if (ec)
+ return ec;
+ switch (magic) {
+ case llvm::sys::fs::file_magic::archive:
+ case llvm::sys::fs::file_magic::elf_relocatable:
+ case llvm::sys::fs::file_magic::elf_shared_object:
+ case llvm::sys::fs::file_magic::unknown:
+ return std::error_code();
+ default:
+ return make_dynamic_error_code(StringRef("unknown type of object file"));
+ }
+}
+
+// Parses an argument of --defsym=<sym>=<number>
+static bool parseDefsymAsAbsolute(StringRef opt, StringRef &sym,
+ uint64_t &addr) {
+ size_t equalPos = opt.find('=');
+ if (equalPos == 0 || equalPos == StringRef::npos)
+ return false;
+ sym = opt.substr(0, equalPos);
+ if (opt.substr(equalPos + 1).getAsInteger(0, addr))
+ return false;
+ return true;
+}
+
+// Parses an argument of --defsym=<sym>=<sym>
+static bool parseDefsymAsAlias(StringRef opt, StringRef &sym,
+ StringRef &target) {
+ size_t equalPos = opt.find('=');
+ if (equalPos == 0 || equalPos == StringRef::npos)
+ return false;
+ sym = opt.substr(0, equalPos);
+ target = opt.substr(equalPos + 1);
+ return !target.empty();
+}
+
+// Parses -z max-page-size=<value>
+static bool parseMaxPageSize(StringRef opt, uint64_t &val) {
+ size_t equalPos = opt.find('=');
+ if (equalPos == 0 || equalPos == StringRef::npos)
+ return false;
+ StringRef value = opt.substr(equalPos + 1);
+ val = 0;
+ if (value.getAsInteger(0, val) || !val)
+ return false;
+ return true;
+}
+
+bool GnuLdDriver::linkELF(int argc, const char *argv[], raw_ostream &diag) {
+ BumpPtrAllocator alloc;
+ std::tie(argc, argv) = maybeExpandResponseFiles(argc, argv, alloc);
+ std::unique_ptr<ELFLinkingContext> options;
+ if (!parse(argc, argv, options, diag))
+ return false;
+ if (!options)
+ return true;
+ bool linked = link(*options, diag);
+
+ // Handle --stats.
+ if (options->collectStats()) {
+ llvm::TimeRecord t = llvm::TimeRecord::getCurrentTime(true);
+ diag << "total time in link " << t.getProcessTime() << "\n";
+ diag << "data size " << t.getMemUsed() << "\n";
+ }
+ return linked;
+}
+
+static llvm::Optional<llvm::Triple::ArchType>
+getArchType(const llvm::Triple &triple, StringRef value) {
+ switch (triple.getArch()) {
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ if (value == "elf_i386")
+ return llvm::Triple::x86;
+ if (value == "elf_x86_64")
+ return llvm::Triple::x86_64;
+ return llvm::None;
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64el:
+ if (value == "elf32ltsmip")
+ return llvm::Triple::mipsel;
+ if (value == "elf64ltsmip")
+ return llvm::Triple::mips64el;
+ return llvm::None;
+ case llvm::Triple::aarch64:
+ if (value == "aarch64linux")
+ return llvm::Triple::aarch64;
+ return llvm::None;
+ case llvm::Triple::arm:
+ if (value == "armelf_linux_eabi")
+ return llvm::Triple::arm;
+ return llvm::None;
+ default:
+ return llvm::None;
+ }
+}
+
+static bool isLinkerScript(StringRef path, raw_ostream &diag) {
+ llvm::sys::fs::file_magic magic = llvm::sys::fs::file_magic::unknown;
+ std::error_code ec = getFileMagic(path, magic);
+ if (ec) {
+ diag << "unknown input file format for file " << path << "\n";
+ return false;
+ }
+ return magic == llvm::sys::fs::file_magic::unknown;
+}
+
+static ErrorOr<StringRef>
+findFile(ELFLinkingContext &ctx, StringRef path, bool dashL) {
+ // If the path was referred to by using a -l argument, let's search
+ // for the file in the search path.
+ if (dashL) {
+ ErrorOr<StringRef> pathOrErr = ctx.searchLibrary(path);
+ if (std::error_code ec = pathOrErr.getError())
+ return make_dynamic_error_code(
+ Twine("Unable to find library -l") + path + ": " + ec.message());
+ path = pathOrErr.get();
+ }
+ if (!llvm::sys::fs::exists(path))
+ return make_dynamic_error_code(
+ Twine("lld: cannot find file ") + path);
+ return path;
+}
+
+static bool isPathUnderSysroot(StringRef sysroot, StringRef path) {
+ if (sysroot.empty())
+ return false;
+ while (!path.empty() && !llvm::sys::fs::equivalent(sysroot, path))
+ path = llvm::sys::path::parent_path(path);
+ return !path.empty();
+}
+
+static std::error_code
+addFilesFromLinkerScript(ELFLinkingContext &ctx, StringRef scriptPath,
+ const std::vector<script::Path> &inputPaths,
+ raw_ostream &diag) {
+ bool sysroot = (!ctx.getSysroot().empty()
+ && isPathUnderSysroot(ctx.getSysroot(), scriptPath));
+ for (const script::Path &path : inputPaths) {
+ ErrorOr<StringRef> pathOrErr = path._isDashlPrefix
+ ? ctx.searchLibrary(path._path) : ctx.searchFile(path._path, sysroot);
+ if (std::error_code ec = pathOrErr.getError()) {
+ auto file = llvm::make_unique<ErrorFile>(path._path, ec);
+ ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
+ continue;
+ }
+
+ std::vector<std::unique_ptr<File>> files
+ = loadFile(ctx, pathOrErr.get(), false);
+ for (std::unique_ptr<File> &file : files) {
+ if (ctx.logInputFiles())
+ diag << file->path() << "\n";
+ ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
+ }
+ }
+ return std::error_code();
+}
+
+std::error_code GnuLdDriver::evalLinkerScript(ELFLinkingContext &ctx,
+ std::unique_ptr<MemoryBuffer> mb,
+ raw_ostream &diag,
+ bool nostdlib) {
+ // Read the script file from disk and parse.
+ StringRef path = mb->getBufferIdentifier();
+ auto parser = llvm::make_unique<script::Parser>(std::move(mb));
+ if (std::error_code ec = parser->parse())
+ return ec;
+ script::LinkerScript *script = parser->get();
+ if (!script)
+ return LinkerScriptReaderError::parse_error;
+ // Evaluate script commands.
+ // Currently we only recognize this subset of linker script commands.
+ for (const script::Command *c : script->_commands) {
+ if (auto *input = dyn_cast<script::Input>(c))
+ if (std::error_code ec = addFilesFromLinkerScript(
+ ctx, path, input->getPaths(), diag))
+ return ec;
+ if (auto *group = dyn_cast<script::Group>(c)) {
+ int origSize = ctx.getNodes().size();
+ if (std::error_code ec = addFilesFromLinkerScript(
+ ctx, path, group->getPaths(), diag))
+ return ec;
+ size_t groupSize = ctx.getNodes().size() - origSize;
+ ctx.getNodes().push_back(llvm::make_unique<GroupEnd>(groupSize));
+ }
+ if (auto *searchDir = dyn_cast<script::SearchDir>(c))
+ if (!nostdlib)
+ ctx.addSearchPath(searchDir->getSearchPath());
+ if (auto *entry = dyn_cast<script::Entry>(c))
+ ctx.setEntrySymbolName(entry->getEntryName());
+ if (auto *output = dyn_cast<script::Output>(c))
+ ctx.setOutputPath(output->getOutputFileName());
+ if (auto *externs = dyn_cast<script::Extern>(c)) {
+ for (auto symbol : *externs) {
+ ctx.addInitialUndefinedSymbol(symbol);
+ }
+ }
+ }
+ // Transfer ownership of the script to the linking context
+ ctx.linkerScriptSema().addLinkerScript(std::move(parser));
+ return std::error_code();
+}
+
+bool GnuLdDriver::applyEmulation(llvm::Triple &triple,
+ llvm::opt::InputArgList &args,
+ raw_ostream &diag) {
+ llvm::opt::Arg *arg = args.getLastArg(OPT_m);
+ if (!arg)
+ return true;
+ llvm::Optional<llvm::Triple::ArchType> arch =
+ getArchType(triple, arg->getValue());
+ if (!arch) {
+ diag << "error: unsupported emulation '" << arg->getValue() << "'.\n";
+ return false;
+ }
+ triple.setArch(*arch);
+ return true;
+}
+
+void GnuLdDriver::addPlatformSearchDirs(ELFLinkingContext &ctx,
+ llvm::Triple &triple,
+ llvm::Triple &baseTriple) {
+ if (triple.getOS() == llvm::Triple::NetBSD &&
+ triple.getArch() == llvm::Triple::x86 &&
+ baseTriple.getArch() == llvm::Triple::x86_64) {
+ ctx.addSearchPath("=/usr/lib/i386");
+ return;
+ }
+ ctx.addSearchPath("=/usr/lib");
+}
+
+std::unique_ptr<ELFLinkingContext>
+GnuLdDriver::createELFLinkingContext(llvm::Triple triple) {
+ std::unique_ptr<ELFLinkingContext> p;
+ // FIXME: #include "llvm/Config/Targets.def"
+#define LLVM_TARGET(targetName) \
+ if ((p = elf::targetName##LinkingContext::create(triple))) return p;
+ LLVM_TARGET(AArch64)
+ LLVM_TARGET(ARM)
+ LLVM_TARGET(Hexagon)
+ LLVM_TARGET(Mips)
+ LLVM_TARGET(X86)
+ LLVM_TARGET(Example)
+ LLVM_TARGET(X86_64)
+#undef LLVM_TARGET
+ return nullptr;
+}
+
+static llvm::Optional<bool>
+getBool(const llvm::opt::InputArgList &parsedArgs,
+ unsigned yesFlag, unsigned noFlag) {
+ if (auto *arg = parsedArgs.getLastArg(yesFlag, noFlag))
+ return arg->getOption().getID() == yesFlag;
+ return llvm::None;
+}
+
+bool GnuLdDriver::parse(int argc, const char *argv[],
+ std::unique_ptr<ELFLinkingContext> &context,
+ raw_ostream &diag) {
+ // Parse command line options using GnuLdOptions.td
+ std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
+ GnuLdOptTable 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 false;
+ }
+
+ // Handle --help
+ if (parsedArgs->hasArg(OPT_help)) {
+ table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false);
+ return true;
+ }
+
+ // Use -target or use default target triple to instantiate LinkingContext
+ llvm::Triple baseTriple;
+ if (auto *arg = parsedArgs->getLastArg(OPT_target)) {
+ baseTriple = llvm::Triple(arg->getValue());
+ } else {
+ baseTriple = getDefaultTarget(argv[0]);
+ }
+ llvm::Triple triple(baseTriple);
+
+ if (!applyEmulation(triple, *parsedArgs, diag))
+ return false;
+
+ std::unique_ptr<ELFLinkingContext> ctx(createELFLinkingContext(triple));
+
+ if (!ctx) {
+ diag << "unknown target triple\n";
+ return false;
+ }
+
+ // Copy mllvm
+ for (auto *arg : parsedArgs->filtered(OPT_mllvm))
+ ctx->appendLLVMOption(arg->getValue());
+
+ // Ignore unknown arguments.
+ for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN))
+ diag << "warning: ignoring unknown argument: "
+ << unknownArg->getValue() << "\n";
+
+ // Set sys root path.
+ if (auto *arg = parsedArgs->getLastArg(OPT_sysroot))
+ ctx->setSysroot(arg->getValue());
+
+ // Handle --demangle option(For compatibility)
+ if (parsedArgs->hasArg(OPT_demangle))
+ ctx->setDemangleSymbols(true);
+
+ // Handle --no-demangle option.
+ if (parsedArgs->hasArg(OPT_no_demangle))
+ ctx->setDemangleSymbols(false);
+
+ // Figure out output kind (-r, -static, -shared)
+ if (parsedArgs->hasArg(OPT_relocatable)) {
+ ctx->setOutputELFType(llvm::ELF::ET_REL);
+ ctx->setPrintRemainingUndefines(false);
+ ctx->setAllowRemainingUndefines(true);
+ }
+
+ if (parsedArgs->hasArg(OPT_static)) {
+ ctx->setOutputELFType(llvm::ELF::ET_EXEC);
+ ctx->setIsStaticExecutable(true);
+ }
+
+ if (parsedArgs->hasArg(OPT_shared)) {
+ ctx->setOutputELFType(llvm::ELF::ET_DYN);
+ ctx->setAllowShlibUndefines(true);
+ ctx->setUseShlibUndefines(false);
+ ctx->setPrintRemainingUndefines(false);
+ ctx->setAllowRemainingUndefines(true);
+ }
+
+ // Handle --stats.
+ if (parsedArgs->hasArg(OPT_stats)) {
+ ctx->setCollectStats(true);
+ }
+
+ // Figure out if the output type is nmagic/omagic
+ if (auto *arg = parsedArgs->getLastArg(
+ OPT_nmagic, OPT_omagic, OPT_no_omagic)) {
+ switch (arg->getOption().getID()) {
+ case OPT_nmagic:
+ ctx->setOutputMagic(ELFLinkingContext::OutputMagic::NMAGIC);
+ ctx->setIsStaticExecutable(true);
+ break;
+ case OPT_omagic:
+ ctx->setOutputMagic(ELFLinkingContext::OutputMagic::OMAGIC);
+ ctx->setIsStaticExecutable(true);
+ break;
+ case OPT_no_omagic:
+ ctx->setOutputMagic(ELFLinkingContext::OutputMagic::DEFAULT);
+ ctx->setNoAllowDynamicLibraries();
+ break;
+ }
+ }
+
+ if (parsedArgs->hasArg(OPT_strip_all))
+ ctx->setStripSymbols(true);
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_soname))
+ ctx->setSharedObjectName(arg->getValue());
+
+ if (parsedArgs->hasArg(OPT_rosegment))
+ ctx->setCreateSeparateROSegment();
+
+ if (parsedArgs->hasArg(OPT_no_align_segments))
+ ctx->setAlignSegments(false);
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_image_base)) {
+ uint64_t baseAddress = 0;
+ StringRef inputValue = arg->getValue();
+ if (inputValue.getAsInteger(0, baseAddress) || !baseAddress) {
+ diag << "invalid value for image base " << inputValue << "\n";
+ return false;
+ }
+ ctx->setBaseAddress(baseAddress);
+ }
+
+ if (parsedArgs->hasArg(OPT_merge_strings))
+ ctx->setMergeCommonStrings(true);
+
+ if (parsedArgs->hasArg(OPT_t))
+ ctx->setLogInputFiles(true);
+
+ if (parsedArgs->hasArg(OPT_use_shlib_undefs))
+ ctx->setUseShlibUndefines(true);
+
+ if (auto val = getBool(*parsedArgs, OPT_allow_shlib_undefs,
+ OPT_no_allow_shlib_undefs))
+ ctx->setAllowShlibUndefines(*val);
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_e))
+ ctx->setEntrySymbolName(arg->getValue());
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_output))
+ ctx->setOutputPath(arg->getValue());
+
+ if (parsedArgs->hasArg(OPT_noinhibit_exec))
+ ctx->setAllowRemainingUndefines(true);
+
+ if (auto val = getBool(*parsedArgs, OPT_export_dynamic,
+ OPT_no_export_dynamic))
+ ctx->setExportDynamic(*val);
+
+ if (parsedArgs->hasArg(OPT_allow_multiple_definition))
+ ctx->setAllowDuplicates(true);
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_dynamic_linker))
+ ctx->setInterpreter(arg->getValue());
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_init))
+ ctx->setInitFunction(arg->getValue());
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_fini))
+ ctx->setFiniFunction(arg->getValue());
+
+ if (auto *arg = parsedArgs->getLastArg(OPT_output_filetype))
+ ctx->setOutputFileType(arg->getValue());
+
+ for (auto *arg : parsedArgs->filtered(OPT_L))
+ ctx->addSearchPath(arg->getValue());
+
+ // Add the default search directory specific to the target.
+ if (!parsedArgs->hasArg(OPT_nostdlib))
+ addPlatformSearchDirs(*ctx, triple, baseTriple);
+
+ for (auto *arg : parsedArgs->filtered(OPT_u))
+ ctx->addInitialUndefinedSymbol(arg->getValue());
+
+ for (auto *arg : parsedArgs->filtered(OPT_defsym)) {
+ StringRef sym, target;
+ uint64_t addr;
+ if (parseDefsymAsAbsolute(arg->getValue(), sym, addr)) {
+ ctx->addInitialAbsoluteSymbol(sym, addr);
+ } else if (parseDefsymAsAlias(arg->getValue(), sym, target)) {
+ ctx->addAlias(sym, target);
+ } else {
+ diag << "invalid --defsym: " << arg->getValue() << "\n";
+ return false;
+ }
+ }
+
+ for (auto *arg : parsedArgs->filtered(OPT_z)) {
+ StringRef opt = arg->getValue();
+ if (opt == "muldefs") {
+ ctx->setAllowDuplicates(true);
+ } else if (opt.startswith("max-page-size")) {
+ // Parse -z max-page-size option.
+ // The default page size is considered the minimum page size the user
+ // can set, check the user input if its atleast the minimum page size
+ // and does not exceed the maximum page size allowed for the target.
+ uint64_t maxPageSize = 0;
+
+ // Error if the page size user set is less than the maximum page size
+ // and greather than the default page size and the user page size is a
+ // modulo of the default page size.
+ if ((!parseMaxPageSize(opt, maxPageSize)) ||
+ (maxPageSize < ctx->getPageSize()) ||
+ (maxPageSize % ctx->getPageSize())) {
+ diag << "invalid option: " << opt << "\n";
+ return false;
+ }
+ ctx->setMaxPageSize(maxPageSize);
+ } else {
+ diag << "warning: ignoring unknown argument for -z: " << opt << "\n";
+ }
+ }
+
+ for (auto *arg : parsedArgs->filtered(OPT_rpath)) {
+ SmallVector<StringRef, 2> rpaths;
+ StringRef(arg->getValue()).split(rpaths, ":");
+ for (auto path : rpaths)
+ ctx->addRpath(path);
+ }
+
+ for (auto *arg : parsedArgs->filtered(OPT_rpath_link)) {
+ SmallVector<StringRef, 2> rpaths;
+ StringRef(arg->getValue()).split(rpaths, ":");
+ for (auto path : rpaths)
+ ctx->addRpathLink(path);
+ }
+
+ // Support --wrap option.
+ for (auto *arg : parsedArgs->filtered(OPT_wrap))
+ ctx->addWrapForSymbol(arg->getValue());
+
+ // Register possible input file parsers.
+ ctx->registry().addSupportELFObjects(*ctx);
+ ctx->registry().addSupportArchives(ctx->logInputFiles());
+ ctx->registry().addSupportYamlFiles();
+ ctx->registry().addSupportNativeObjects();
+ if (ctx->allowLinkWithDynamicLibraries())
+ ctx->registry().addSupportELFDynamicSharedObjects(*ctx);
+
+ std::stack<int> groupStack;
+ int numfiles = 0;
+ bool asNeeded = false;
+ bool wholeArchive = false;
+
+ // Process files
+ for (auto arg : *parsedArgs) {
+ switch (arg->getOption().getID()) {
+ case OPT_no_whole_archive:
+ wholeArchive = false;
+ break;
+
+ case OPT_whole_archive:
+ wholeArchive = true;
+ break;
+
+ case OPT_as_needed:
+ asNeeded = true;
+ break;
+
+ case OPT_no_as_needed:
+ asNeeded = false;
+ break;
+
+ case OPT_start_group:
+ groupStack.push(numfiles);
+ break;
+
+ case OPT_end_group: {
+ if (groupStack.empty()) {
+ diag << "stray --end-group\n";
+ return false;
+ }
+ int startGroupPos = groupStack.top();
+ ctx->getNodes().push_back(
+ llvm::make_unique<GroupEnd>(numfiles - startGroupPos));
+ groupStack.pop();
+ break;
+ }
+
+ case OPT_INPUT:
+ case OPT_l:
+ case OPT_T: {
+ bool dashL = (arg->getOption().getID() == OPT_l);
+ StringRef path = arg->getValue();
+
+ ErrorOr<StringRef> pathOrErr = findFile(*ctx, path, dashL);
+ if (std::error_code ec = pathOrErr.getError()) {
+ auto file = llvm::make_unique<ErrorFile>(path, ec);
+ auto node = llvm::make_unique<FileNode>(std::move(file));
+ node->setAsNeeded(asNeeded);
+ ctx->getNodes().push_back(std::move(node));
+ break;
+ }
+ StringRef realpath = pathOrErr.get();
+
+ bool isScript =
+ (!path.endswith(".objtxt") && isLinkerScript(realpath, diag));
+ if (isScript) {
+ if (ctx->logInputFiles())
+ diag << path << "\n";
+ ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
+ MemoryBuffer::getFileOrSTDIN(realpath);
+ if (std::error_code ec = mb.getError()) {
+ diag << "Cannot open " << path << ": " << ec.message() << "\n";
+ return false;
+ }
+ bool nostdlib = parsedArgs->hasArg(OPT_nostdlib);
+ std::error_code ec =
+ evalLinkerScript(*ctx, std::move(mb.get()), diag, nostdlib);
+ if (ec) {
+ diag << path << ": Error parsing linker script: "
+ << ec.message() << "\n";
+ return false;
+ }
+ break;
+ }
+ std::vector<std::unique_ptr<File>> files
+ = loadFile(*ctx, realpath, wholeArchive);
+ for (std::unique_ptr<File> &file : files) {
+ if (ctx->logInputFiles())
+ diag << file->path() << "\n";
+ auto node = llvm::make_unique<FileNode>(std::move(file));
+ node->setAsNeeded(asNeeded);
+ ctx->getNodes().push_back(std::move(node));
+ }
+ numfiles += files.size();
+ break;
+ }
+ }
+ }
+
+ if (ctx->getNodes().empty()) {
+ diag << "No input files\n";
+ return false;
+ }
+
+ // Set default output file name if the output file was not specified.
+ if (ctx->outputPath().empty()) {
+ switch (ctx->outputFileType()) {
+ case LinkingContext::OutputFileType::YAML:
+ ctx->setOutputPath("-");
+ break;
+ case LinkingContext::OutputFileType::Native:
+ ctx->setOutputPath("a.native");
+ break;
+ default:
+ ctx->setOutputPath("a.out");
+ break;
+ }
+ }
+
+ // Validate the combination of options used.
+ if (!ctx->validate(diag))
+ return false;
+
+ // Perform linker script semantic actions
+ ctx->linkerScriptSema().perform();
+
+ context.swap(ctx);
+ return true;
+}
+
+/// Get the default target triple based on either the program name
+/// (e.g. "x86-ibm-linux-lld") or the primary target llvm was configured for.
+llvm::Triple GnuLdDriver::getDefaultTarget(const char *progName) {
+ SmallVector<StringRef, 4> components;
+ llvm::SplitString(llvm::sys::path::stem(progName), components, "-");
+ // If has enough parts to be start with a triple.
+ if (components.size() >= 4) {
+ llvm::Triple triple(components[0], components[1], components[2],
+ components[3]);
+ // If first component looks like an arch.
+ if (triple.getArch() != llvm::Triple::UnknownArch)
+ return triple;
+ }
+
+ // Fallback to use whatever default triple llvm was configured for.
+ return llvm::Triple(llvm::sys::getDefaultTargetTriple());
+}
diff --git a/lib/Driver/GnuLdOptions.td b/lib/Driver/GnuLdOptions.td
new file mode 100644
index 0000000000000..9d06f29354395
--- /dev/null
+++ b/lib/Driver/GnuLdOptions.td
@@ -0,0 +1,323 @@
+include "llvm/Option/OptParser.td"
+
+//===----------------------------------------------------------------------===//
+/// Utility Functions
+//===----------------------------------------------------------------------===//
+// Single and multiple dash options combined
+multiclass smDash<string opt1, string opt2, string help> {
+ // Option
+ def "" : Separate<["-"], opt1>, HelpText<help>;
+ def opt1_eq : Joined<["-"], opt1#"=">,
+ Alias<!cast<Option>(opt1)>;
+ // Compatibility aliases
+ def opt2_dashdash : Separate<["--"], opt2>,
+ Alias<!cast<Option>(opt1)>;
+ def opt2_dashdash_eq : Joined<["--"], opt2#"=">,
+ Alias<!cast<Option>(opt1)>;
+}
+
+// Support -<option>,-<option>=
+multiclass dashEq<string opt1, string opt2, string help> {
+ // Option
+ def "" : Separate<["-"], opt1>, HelpText<help>;
+ // Compatibility aliases
+ def opt2_eq : Joined<["-"], opt2#"=">,
+ Alias<!cast<Option>(opt1)>;
+}
+
+//===----------------------------------------------------------------------===//
+/// LLVM and Target options
+//===----------------------------------------------------------------------===//
+def grp_llvmtarget : OptionGroup<"opts">,
+ HelpText<"LLVM and Target Options">;
+def mllvm : Separate<["-"], "mllvm">,
+ HelpText<"Options to pass to LLVM">, Group<grp_llvmtarget>;
+def target : Separate<["-"], "target">, MetaVarName<"<triple>">,
+ HelpText<"Target triple to link for">,
+ Group<grp_llvmtarget>;
+
+//===----------------------------------------------------------------------===//
+/// Output Kinds
+//===----------------------------------------------------------------------===//
+def grp_kind : OptionGroup<"outs">,
+ HelpText<"OUTPUT KIND">;
+def relocatable : Flag<["-"], "r">,
+ HelpText<"Create relocatable object file">, Group<grp_kind>;
+def static : Flag<["-"], "static">,
+ HelpText<"Create static executable">, Group<grp_kind>;
+def dynamic : Flag<["-"], "dynamic">,
+ HelpText<"Create dynamic executable (default)">,Group<grp_kind>;
+def shared : Flag<["-"], "shared">,
+ HelpText<"Create dynamic library">, Group<grp_kind>;
+
+// output kinds - compatibility aliases
+def Bstatic : Flag<["-"], "Bstatic">, Alias<static>;
+def Bshareable : Flag<["-"], "Bshareable">, Alias<shared>;
+
+//===----------------------------------------------------------------------===//
+/// General Options
+//===----------------------------------------------------------------------===//
+def grp_general : OptionGroup<"opts">,
+ HelpText<"GENERAL OPTIONS">;
+def output : Separate<["-"], "o">, MetaVarName<"<path>">,
+ HelpText<"Path to file to write output">,
+ Group<grp_general>;
+def m : Separate<["-"], "m">, MetaVarName<"<emulation>">,
+ HelpText<"Select target emulation">,
+ Group<grp_general>;
+def build_id : Flag<["--"], "build-id">,
+ HelpText<"Request creation of \".note.gnu.build-id\" ELF note section">,
+ Group<grp_general>;
+def sysroot : Joined<["--"], "sysroot=">,
+ HelpText<"Set the system root">,
+ Group<grp_general>;
+
+
+//===----------------------------------------------------------------------===//
+/// Executable Options
+//===----------------------------------------------------------------------===//
+def grp_main : OptionGroup<"opts">,
+ HelpText<"EXECUTABLE OPTIONS">;
+def L : Joined<["-"], "L">, MetaVarName<"<dir>">,
+ HelpText<"Directory to search for libraries">,
+ Group<grp_main>;
+def l : Joined<["-"], "l">, MetaVarName<"<libName>">,
+ HelpText<"Root name of library to use">,
+ Group<grp_main>;
+def noinhibit_exec : Flag<["--"], "noinhibit-exec">,
+ HelpText<"Retain the executable output file whenever"
+ " it is still usable">,
+ Group<grp_main>;
+defm e : smDash<"e", "entry",
+ "Name of entry point symbol">,
+ Group<grp_main>;
+defm init: dashEq<"init", "init",
+ "Specify an initializer function">,
+ Group<grp_main>;
+defm fini: dashEq<"fini", "fini",
+ "Specify a finalizer function">,
+ Group<grp_main>;
+def whole_archive: Flag<["--"], "whole-archive">,
+ HelpText<"Force load of all members in a static library">,
+ Group<grp_main>;
+def no_whole_archive: Flag<["--"], "no-whole-archive">,
+ HelpText<"Restores the default behavior of loading archive members">,
+ Group<grp_main>;
+def nostdlib : Flag<["-"], "nostdlib">,
+ HelpText<"Disable default search path for libraries">,
+ Group<grp_main>;
+def image_base : Separate<["--"], "image-base">,
+ HelpText<"Set the base address">,
+ Group<grp_main>;
+
+//===----------------------------------------------------------------------===//
+/// Static Executable Options
+//===----------------------------------------------------------------------===//
+def grp_staticexec : OptionGroup<"opts">,
+ HelpText<"STATIC EXECUTABLE OPTIONS">;
+def nmagic : Flag<["--"], "nmagic">,
+ HelpText<"Turn off page alignment of sections,"
+ " and disable linking against shared libraries">,
+ Group<grp_staticexec>;
+def omagic : Flag<["--"], "omagic">,
+ HelpText<"Set the text and data sections to be readable and writable."
+ " Also, do not page-align the data segment, and"
+ " disable linking against shared libraries.">,
+ Group<grp_staticexec>;
+def no_omagic : Flag<["--"], "no-omagic">,
+ HelpText<"This option negates most of the effects of the -N option."
+ "Disable linking with shared libraries">,
+ Group<grp_staticexec>;
+// Compatible Aliases
+def nmagic_alias : Flag<["-"], "n">,
+ Alias<nmagic>;
+def omagic_alias : Flag<["-"], "N">,
+ Alias<omagic>;
+
+//===----------------------------------------------------------------------===//
+/// Dynamic Library/Executable Options
+//===----------------------------------------------------------------------===//
+def grp_dynlibexec : OptionGroup<"opts">,
+ HelpText<"DYNAMIC LIBRARY/EXECUTABLE OPTIONS">;
+def dynamic_linker : Joined<["--"], "dynamic-linker=">,
+ HelpText<"Set the path to the dynamic linker">, Group<grp_dynlibexec>;
+// Executable options - compatibility aliases
+def dynamic_linker_alias : Separate<["-"], "dynamic-linker">,
+ Alias<dynamic_linker>;
+defm rpath : dashEq<"rpath", "rpath",
+ "Add a directory to the runtime library search path">,
+ Group<grp_dynlibexec>;
+def rpath_link : Separate<["-"], "rpath-link">,
+ HelpText<"Specifies the first set of directories to search">,
+ Group<grp_dynlibexec>;
+def export_dynamic : Flag<["-", "--"], "export-dynamic">,
+ HelpText<"Add all symbols to the dynamic symbol table"
+ " when creating executables">,
+ Group<grp_main>;
+def alias_export_dynamic: Flag<["-"], "E">,
+ Alias<export_dynamic>;
+def no_export_dynamic : Flag<["--"], "no-export-dynamic">,
+ Group<grp_main>;
+
+//===----------------------------------------------------------------------===//
+/// Dynamic Library Options
+//===----------------------------------------------------------------------===//
+def grp_dynlib : OptionGroup<"opts">,
+ HelpText<"DYNAMIC LIBRARY OPTIONS">;
+def soname : Joined<["-", "--"], "soname=">,
+ HelpText<"Set the internal DT_SONAME field to the specified name">,
+ Group<grp_dynlib>;
+def soname_separate : Separate<["-", "--"], "soname">, Alias<soname>;
+def soname_h : Separate<["-"], "h">, Alias<soname>;
+
+//===----------------------------------------------------------------------===//
+/// Resolver Options
+//===----------------------------------------------------------------------===//
+def grp_resolveropt : OptionGroup<"opts">,
+ HelpText<"SYMBOL RESOLUTION OPTIONS">;
+defm u : smDash<"u", "undefined",
+ "Force symbol to be entered in the output file"
+ " as an undefined symbol">,
+ Group<grp_resolveropt>;
+def start_group : Flag<["--"], "start-group">,
+ HelpText<"Start a group">,
+ Group<grp_resolveropt>;
+def alias_start_group: Flag<["-"], "(">,
+ Alias<start_group>;
+def end_group : Flag<["--"], "end-group">,
+ HelpText<"End a group">,
+ Group<grp_resolveropt>;
+def alias_end_group: Flag<["-"], ")">,
+ Alias<end_group>;
+def as_needed : Flag<["--"], "as-needed">,
+ HelpText<"This option affects ELF DT_NEEDED tags for "
+ "dynamic libraries mentioned on the command line">,
+ Group<grp_resolveropt>;
+def no_as_needed : Flag<["--"], "no-as-needed">,
+ HelpText<"This option restores the default behavior"
+ " of adding DT_NEEDED entries">,
+ Group<grp_resolveropt>;
+def no_allow_shlib_undefs : Flag<["--"], "no-allow-shlib-undefined">,
+ HelpText<"Do not allow undefined symbols from dynamic"
+ " library when creating executables">,
+ Group<grp_resolveropt>;
+def allow_shlib_undefs : Flag<["-", "--"], "allow-shlib-undefined">,
+ HelpText<"Allow undefined symbols from dynamic"
+ " library when creating executables">,
+ Group<grp_resolveropt>;
+def use_shlib_undefs: Flag<["--"], "use-shlib-undefines">,
+ HelpText<"Resolve undefined symbols from dynamic libraries">,
+ Group<grp_resolveropt>;
+def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">,
+ HelpText<"Allow multiple definitions">,
+ Group<grp_resolveropt>;
+def defsym : Joined<["--"], "defsym=">,
+ HelpText<"Create a defined symbol">,
+ Group<grp_resolveropt>;
+
+//===----------------------------------------------------------------------===//
+/// Custom Options
+//===----------------------------------------------------------------------===//
+def grp_customopts : OptionGroup<"opts">,
+ HelpText<"CUSTOM OPTIONS">;
+def rosegment: Flag<["--"], "rosegment">,
+ HelpText<"Put read-only non-executable sections in their own segment">,
+ Group<grp_customopts>;
+def z : Separate<["-"], "z">,
+ HelpText<"Linker Option extensions">,
+ Group<grp_customopts>;
+def no_align_segments: Flag<["--"], "no-align-segments">,
+ HelpText<"Don't align ELF segments(virtualaddress/fileoffset) to page boundaries">,
+ Group<grp_customopts>;
+
+//===----------------------------------------------------------------------===//
+/// Symbol options
+//===----------------------------------------------------------------------===//
+def grp_symbolopts : OptionGroup<"opts">,
+ HelpText<"SYMBOL OPTIONS">;
+def demangle : Flag<["--"], "demangle">,
+ HelpText<"Demangle C++ symbols">,
+ Group<grp_symbolopts>;
+def no_demangle : Flag<["--"], "no-demangle">,
+ HelpText<"Dont demangle C++ symbols">,
+ Group<grp_symbolopts>;
+def strip_all : Flag<["--"], "strip-all">,
+ HelpText<"Omit all symbol informations from output">,
+ Group<grp_symbolopts>;
+def alias_strip_all : Flag<["-"], "s">,
+ Alias<strip_all>;
+defm wrap : smDash<"wrap", "wrap",
+ "Use a wrapper function for symbol. Any "
+ " undefined reference to symbol will be resolved to "
+ "\"__wrap_symbol\". Any undefined reference to \"__real_symbol\""
+ " will be resolved to symbol.">,
+ MetaVarName<"<symbol>">,
+ Group<grp_symbolopts>;
+
+//===----------------------------------------------------------------------===//
+/// Script Options
+//===----------------------------------------------------------------------===//
+def grp_scriptopts : OptionGroup<"opts">,
+ HelpText<"SCRIPT OPTIONS">;
+defm T : smDash<"T", "script",
+ "Use the given linker script in place of the default script.">,
+ Group<grp_scriptopts>;
+
+//===----------------------------------------------------------------------===//
+/// Optimization Options
+//===----------------------------------------------------------------------===//
+def grp_opts : OptionGroup<"opts">,
+ HelpText<"OPTIMIZATIONS">;
+def hash_style : Joined <["--"], "hash-style=">,
+ HelpText<"Set the type of linker's hash table(s)">,
+ Group<grp_opts>;
+def merge_strings : Flag<["--"], "merge-strings">,
+ HelpText<"Merge common strings across mergeable sections">,
+ Group<grp_opts>;
+def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">,
+ HelpText<"Request creation of .eh_frame_hdr section and ELF "
+ " PT_GNU_EH_FRAME segment header">,
+ Group<grp_opts>;
+
+//===----------------------------------------------------------------------===//
+/// Tracing Options
+//===----------------------------------------------------------------------===//
+def grp_tracingopts : OptionGroup<"opts">,
+ HelpText<"TRACING OPTIONS">;
+def t : Flag<["-"], "t">,
+ HelpText<"Print the names of the input files as ld processes them">,
+ Group<grp_tracingopts>;
+def stats : Flag<["--"], "stats">,
+ HelpText<"Print time and memory usage stats">, Group<grp_tracingopts>;
+
+//===----------------------------------------------------------------------===//
+/// Extensions
+//===----------------------------------------------------------------------===//
+def grp_extns : OptionGroup<"opts">,
+ HelpText<"Extensions">;
+def output_filetype: Separate<["--"], "output-filetype">,
+ HelpText<"Specify what type of output file that lld creates, YAML/Native">,
+ Group<grp_extns>;
+def alias_output_filetype: Joined<["--"], "output-filetype=">,
+ Alias<output_filetype>;
+
+//===----------------------------------------------------------------------===//
+/// Ignored options
+//===----------------------------------------------------------------------===//
+def grp_ignored: OptionGroup<"ignored">,
+ HelpText<"GNU Options ignored for Compatibility ">;
+def dashg : Flag<["-"], "g">,
+ HelpText<"Ignored.">,
+ Group<grp_ignored>;
+def Qy : Flag<["-"], "Qy">,
+ HelpText<"Ignored for SVR4 Compatibility">,
+ Group<grp_ignored>;
+def qmagic : Flag<["-"], "qmagic">,
+ HelpText<"Ignored for Linux Compatibility">,
+ Group<grp_ignored>;
+
+//===----------------------------------------------------------------------===//
+/// Help
+//===----------------------------------------------------------------------===//
+def help : Flag<["--"], "help">,
+ HelpText<"Display this help message">;
diff --git a/lib/Driver/Makefile b/lib/Driver/Makefile
new file mode 100644
index 0000000000000..19024cfab0f16
--- /dev/null
+++ b/lib/Driver/Makefile
@@ -0,0 +1,38 @@
+##===- lld/lib/Driver/Makefile ---------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LLD_LEVEL := ../..
+LIBRARYNAME := lldDriver
+
+BUILT_SOURCES = CoreOptions.inc UniversalDriverOptions.inc DarwinLdOptions.inc \
+ GnuLdOptions.inc WinLinkOptions.inc
+
+TABLEGEN_INC_FILES_COMMON = 1
+
+include $(LLD_LEVEL)/Makefile
+
+$(ObjDir)/CoreOptions.inc.tmp : CoreOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
+ $(Echo) "Building LLD CoreOptions Option tables with tblgen"
+ $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
+
+$(ObjDir)/UniversalDriverOptions.inc.tmp : UniversalDriverOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
+ $(Echo) "Building LLD Universal Driver Options tables with tblgen"
+ $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
+
+$(ObjDir)/DarwinLdOptions.inc.tmp : DarwinLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
+ $(Echo) "Building LLD Darwin ld Option tables with tblgen"
+ $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
+
+$(ObjDir)/GnuLdOptions.inc.tmp : GnuLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
+ $(Echo) "Building LLD Gnu ld Option tables with tblgen"
+ $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
+
+$(ObjDir)/WinLinkOptions.inc.tmp : WinLinkOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
+ $(Echo) "Building LLD WinLinkOptions Option tables with tblgen"
+ $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
diff --git a/lib/Driver/TODO.rst b/lib/Driver/TODO.rst
new file mode 100644
index 0000000000000..e03d829c232dd
--- /dev/null
+++ b/lib/Driver/TODO.rst
@@ -0,0 +1,101 @@
+GNU ld Driver
+~~~~~~~~~~~~~
+
+Missing Options
+###############
+
+* --audit
+* -A,--architecture
+* -b,--format
+* -d,-dc,-dp
+* -P,--depaudit
+* --exclude-libs
+* --exclude-modules-for-implib
+* -E,--export-dynamic,--no-export-dynamic
+* -EB (We probably shouldn't support this)
+* -EL (We probably shouldn't support this)
+* -f,--auxiliary
+* -F,--filter
+* -G,--gpsize
+* -h
+* -i
+* --library
+* -M
+* --print-map
+* -output
+* -O
+* -q,--emit-relocs
+* --force-dynamic
+* --relocatable
+* -R,--just-symbols
+* -s,--strip-all
+* -S,--strip-debug
+* --trace
+* -dT,--default-script
+* -Ur
+* --unique
+* -v,--version,-V
+* -x,--discard-all
+* -X,--discard-locals
+* -y,--trace-symbol
+* -z (keywords need to be implemented)
+* --accept-unknown-input-arch,--no-accept-unknown-input-arch
+* -Bdynamic,-dy,-call_shared
+* -Bgroup
+* -dn,-non_shared
+* -Bsymbolic
+* -Bsymbolic-functions
+* --dynamic-list
+* --dynamic-list-data
+* --dynamic-list-cpp-new
+* --dynamic-list-cpp-typeinfo
+* --check-sections,--no-check-sections
+* --copy-dt-needed-entries,--no-copy-dt-needed-entires
+* --cref
+* --no-define-common
+* --defsym (only absolute value supported now)
+* --demangle,--no-demangle
+* -I
+* --fatal-warnings,--no-fatal-warnings
+* --force-exe-suffix
+* --gc-sections,--no-gc-sections
+* --print-gc-sections,--no-print-gc-sections
+* --print-output-format
+* --target-help
+* -Map
+* --no-keep-memory
+* --no-undefined,-z defs
+* --allow-shlib-undefined,--no-alow-shlib-undefined
+* --no-undefined-version
+* --default-symver
+* --default-imported-symver
+* --no-warn-mismatch
+* --no-warn-search-mismatch
+* --oformat
+* -pie,--pic-executable
+* --relax,--no-relax
+* --retain-symbols-file
+* --sort-common
+* --sort-section={name,alignment}
+* --split-by-file
+* --split-by-reloc
+* --stats
+* --section-start
+* -T{bss,data,text,{text,rodata,data}-segment}
+* --unresolved-symbols
+* -dll-verbose,--verbose
+* --version-script
+* --warn-common
+* --warn-constructors
+* --warn-multiple-gp
+* --warn-once
+* --warn-section-align
+* --warn-shared-textrel
+* --warn-alternate-em
+* --warn-unresolved-symbols
+* --error-unresolved-symbols
+* --wrap
+* --no-ld-generated-unwind-info
+* --hash-size
+* --reduce-memory-overheads
+* --build-id
diff --git a/lib/Driver/UniversalDriver.cpp b/lib/Driver/UniversalDriver.cpp
new file mode 100644
index 0000000000000..7d42ad7b4bfc0
--- /dev/null
+++ b/lib/Driver/UniversalDriver.cpp
@@ -0,0 +1,218 @@
+//===- lib/Driver/UniversalDriver.cpp -------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// Driver for "universal" lld tool which can mimic any linker command line
+/// parsing once it figures out which command line flavor to use.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lld/Driver/Driver.h"
+#include "lld/Config/Version.h"
+#include "lld/Core/LLVM.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lld;
+
+namespace {
+
+// Create enum with OPT_xxx values for each option in GnuLdOptions.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELP, META) \
+ OPT_##ID,
+#include "UniversalDriverOptions.inc"
+#undef OPTION
+};
+
+// Create prefix string literals used in GnuLdOptions.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "UniversalDriverOptions.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in GnuLdOptions.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 "UniversalDriverOptions.inc"
+#undef OPTION
+};
+
+// Create OptTable class for parsing actual command line arguments
+class UniversalDriverOptTable : public llvm::opt::OptTable {
+public:
+ UniversalDriverOptTable()
+ : OptTable(infoTable, llvm::array_lengthof(infoTable)) {}
+};
+
+enum class Flavor {
+ invalid,
+ gnu_ld, // -flavor gnu
+ win_link, // -flavor link
+ darwin_ld, // -flavor darwin
+ core // -flavor core OR -core
+};
+
+struct ProgramNameParts {
+ StringRef _target;
+ StringRef _flavor;
+};
+
+} // anonymous namespace
+
+static Flavor strToFlavor(StringRef str) {
+ return llvm::StringSwitch<Flavor>(str)
+ .Case("gnu", Flavor::gnu_ld)
+ .Case("link", Flavor::win_link)
+ .Case("lld-link", Flavor::win_link)
+ .Case("darwin", Flavor::darwin_ld)
+ .Case("core", Flavor::core)
+ .Case("ld", Flavor::gnu_ld)
+ .Default(Flavor::invalid);
+}
+
+static ProgramNameParts parseProgramName(StringRef programName) {
+ SmallVector<StringRef, 3> components;
+ llvm::SplitString(programName, components, "-");
+ ProgramNameParts ret;
+
+ using std::begin;
+ using std::end;
+
+ // Erase any lld components.
+ components.erase(std::remove(components.begin(), components.end(), "lld"),
+ components.end());
+
+ // Find the flavor component.
+ auto flIter = std::find_if(components.begin(), components.end(),
+ [](StringRef str) -> bool {
+ return strToFlavor(str) != Flavor::invalid;
+ });
+
+ if (flIter != components.end()) {
+ ret._flavor = *flIter;
+ components.erase(flIter);
+ }
+
+ // Any remaining component must be the target.
+ if (components.size() == 1)
+ ret._target = components[0];
+
+ return ret;
+}
+
+// Removes the argument from argv along with its value, if exists, and updates
+// argc.
+static void removeArg(llvm::opt::Arg *arg, int &argc, const char **&argv) {
+ unsigned int numToRemove = arg->getNumValues() + 1;
+ unsigned int argIndex = arg->getIndex() + 1;
+
+ std::rotate(&argv[argIndex], &argv[argIndex + numToRemove], argv + argc);
+ argc -= numToRemove;
+}
+
+static Flavor getFlavor(int &argc, const char **&argv,
+ std::unique_ptr<llvm::opt::InputArgList> &parsedArgs) {
+ if (llvm::opt::Arg *argCore = parsedArgs->getLastArg(OPT_core)) {
+ removeArg(argCore, argc, argv);
+ return Flavor::core;
+ }
+ if (llvm::opt::Arg *argFlavor = parsedArgs->getLastArg(OPT_flavor)) {
+ removeArg(argFlavor, argc, argv);
+ return strToFlavor(argFlavor->getValue());
+ }
+
+#if LLVM_ON_UNIX
+ if (llvm::sys::path::filename(argv[0]).equals("ld")) {
+#if __APPLE__
+ // On a Darwin systems, if linker binary is named "ld", use Darwin driver.
+ return Flavor::darwin_ld;
+#endif
+ // On a ELF based systems, if linker binary is named "ld", use gnu driver.
+ return Flavor::gnu_ld;
+ }
+#endif
+
+ StringRef name = llvm::sys::path::stem(argv[0]);
+ return strToFlavor(parseProgramName(name)._flavor);
+}
+
+namespace lld {
+
+bool UniversalDriver::link(int argc, const char *argv[],
+ raw_ostream &diagnostics) {
+ // Parse command line options using GnuLdOptions.td
+ std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
+ UniversalDriverOptTable table;
+ unsigned missingIndex;
+ unsigned missingCount;
+
+ // Program name
+ StringRef programName = llvm::sys::path::stem(argv[0]);
+
+ parsedArgs.reset(
+ table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
+
+ if (missingCount) {
+ diagnostics << "error: missing arg value for '"
+ << parsedArgs->getArgString(missingIndex) << "' expected "
+ << missingCount << " argument(s).\n";
+ return false;
+ }
+
+ // Handle -help
+ if (parsedArgs->getLastArg(OPT_help)) {
+ table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false);
+ return true;
+ }
+
+ // Handle -version
+ if (parsedArgs->getLastArg(OPT_version)) {
+ diagnostics << "LLVM Linker Version: " << getLLDVersion()
+ << getLLDRepositoryVersion() << "\n";
+ return true;
+ }
+
+ Flavor flavor = getFlavor(argc, argv, parsedArgs);
+ std::vector<const char *> args(argv, argv + argc);
+
+ // Switch to appropriate driver.
+ switch (flavor) {
+ case Flavor::gnu_ld:
+ return GnuLdDriver::linkELF(args.size(), args.data(), diagnostics);
+ case Flavor::darwin_ld:
+ return DarwinLdDriver::linkMachO(args.size(), args.data(), diagnostics);
+ case Flavor::win_link:
+ return WinLinkDriver::linkPECOFF(args.size(), args.data(), diagnostics);
+ case Flavor::core:
+ return CoreDriver::link(args.size(), args.data(), diagnostics);
+ case Flavor::invalid:
+ diagnostics << "Select the appropriate flavor\n";
+ table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false);
+ return false;
+ }
+ llvm_unreachable("Unrecognised flavor");
+}
+
+} // end namespace lld
diff --git a/lib/Driver/UniversalDriverOptions.td b/lib/Driver/UniversalDriverOptions.td
new file mode 100644
index 0000000000000..14abc9ce9911d
--- /dev/null
+++ b/lib/Driver/UniversalDriverOptions.td
@@ -0,0 +1,19 @@
+include "llvm/Option/OptParser.td"
+
+// Select an optional flavor
+def flavor: Separate<["-"], "flavor">,
+ HelpText<"Flavor for linking, options are gnu/darwin/link">;
+
+// Select the core flavor
+def core : Flag<["-"], "core">,
+ HelpText<"CORE linking">;
+
+def target: Separate<["-"], "target">,
+ HelpText<"Select the target">;
+
+def version: Flag<["-"], "version">,
+ HelpText<"Display the version">;
+
+// Help message
+def help : Flag<["-"], "help">,
+ HelpText<"Display this help message">;
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 &section,
+ 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
diff --git a/lib/Driver/WinLinkModuleDef.cpp b/lib/Driver/WinLinkModuleDef.cpp
new file mode 100644
index 0000000000000..e55a0bc5fe649
--- /dev/null
+++ b/lib/Driver/WinLinkModuleDef.cpp
@@ -0,0 +1,295 @@
+//===- lib/Driver/WinLinkModuleDef.cpp ------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Windows module definition file parser.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lld/Driver/WinLinkModuleDef.h"
+#include "llvm/ADT/StringSwitch.h"
+
+namespace lld {
+namespace moduledef {
+
+Token Lexer::lex() {
+ for (;;) {
+ _buffer = _buffer.trim();
+ if (_buffer.empty() || _buffer[0] == '\0')
+ return Token(Kind::eof, _buffer);
+
+ switch (_buffer[0]) {
+ case ';': {
+ size_t end = _buffer.find('\n');
+ _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end);
+ continue;
+ }
+ case '=':
+ _buffer = _buffer.drop_front();
+ return Token(Kind::equal, "=");
+ case ',':
+ _buffer = _buffer.drop_front();
+ return Token(Kind::comma, ",");
+ case '"': {
+ size_t end = _buffer.find('"', 1);
+ Token ret;
+ if (end == _buffer.npos) {
+ ret = Token(Kind::identifier, _buffer.substr(1, end));
+ _buffer = "";
+ } else {
+ ret = Token(Kind::identifier, _buffer.substr(1, end - 1));
+ _buffer = _buffer.drop_front(end + 1);
+ }
+ return ret;
+ }
+ default: {
+ size_t end = _buffer.find_first_not_of(
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789_.*~+!@#$%^&*()/");
+ StringRef word = _buffer.substr(0, end);
+ Kind kind = llvm::StringSwitch<Kind>(word)
+ .Case("BASE", Kind::kw_base)
+ .Case("DATA", Kind::kw_data)
+ .Case("EXPORTS", Kind::kw_exports)
+ .Case("HEAPSIZE", Kind::kw_heapsize)
+ .Case("LIBRARY", Kind::kw_library)
+ .Case("NAME", Kind::kw_name)
+ .Case("NONAME", Kind::kw_noname)
+ .Case("PRIVATE", Kind::kw_private)
+ .Case("STACKSIZE", Kind::kw_stacksize)
+ .Case("VERSION", Kind::kw_version)
+ .Default(Kind::identifier);
+ _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end);
+ return Token(kind, word);
+ }
+ }
+ }
+}
+
+void Parser::consumeToken() {
+ if (_tokBuf.empty()) {
+ _tok = _lex.lex();
+ return;
+ }
+ _tok = _tokBuf.back();
+ _tokBuf.pop_back();
+}
+
+bool Parser::consumeTokenAsInt(uint64_t &result) {
+ consumeToken();
+ if (_tok._kind != Kind::identifier) {
+ ungetToken();
+ error(_tok, "Integer expected");
+ return false;
+ }
+ if (_tok._range.getAsInteger(10, result)) {
+ error(_tok, "Integer expected");
+ return false;
+ }
+ return true;
+}
+
+bool Parser::expectAndConsume(Kind kind, Twine msg) {
+ consumeToken();
+ if (_tok._kind != kind) {
+ error(_tok, msg);
+ return false;
+ }
+ return true;
+}
+
+void Parser::ungetToken() { _tokBuf.push_back(_tok); }
+
+void Parser::error(const Token &tok, Twine msg) {
+ _lex.getSourceMgr().PrintMessage(
+ llvm::SMLoc::getFromPointer(tok._range.data()), llvm::SourceMgr::DK_Error,
+ msg);
+}
+
+bool Parser::parse(std::vector<Directive *> &ret) {
+ for (;;) {
+ Directive *dir = nullptr;
+ if (!parseOne(dir))
+ return false;
+ if (!dir)
+ return true;
+ ret.push_back(dir);
+ }
+}
+
+bool Parser::parseOne(Directive *&ret) {
+ consumeToken();
+ switch (_tok._kind) {
+ case Kind::eof:
+ return true;
+ case Kind::kw_exports: {
+ // EXPORTS
+ std::vector<PECOFFLinkingContext::ExportDesc> exports;
+ for (;;) {
+ PECOFFLinkingContext::ExportDesc desc;
+ if (!parseExport(desc))
+ break;
+ exports.push_back(desc);
+ }
+ ret = new (_alloc) Exports(exports);
+ return true;
+ }
+ case Kind::kw_heapsize: {
+ // HEAPSIZE
+ uint64_t reserve, commit;
+ if (!parseMemorySize(reserve, commit))
+ return false;
+ ret = new (_alloc) Heapsize(reserve, commit);
+ return true;
+ }
+ case Kind::kw_library: {
+ // LIBRARY
+ std::string name;
+ uint64_t baseaddr;
+ if (!parseName(name, baseaddr))
+ return false;
+ if (!StringRef(name).endswith_lower(".dll"))
+ name.append(".dll");
+ ret = new (_alloc) Library(name, baseaddr);
+ return true;
+ }
+ case Kind::kw_stacksize: {
+ // STACKSIZE
+ uint64_t reserve, commit;
+ if (!parseMemorySize(reserve, commit))
+ return false;
+ ret = new (_alloc) Stacksize(reserve, commit);
+ return true;
+ }
+ case Kind::kw_name: {
+ // NAME
+ std::string outputPath;
+ uint64_t baseaddr;
+ if (!parseName(outputPath, baseaddr))
+ return false;
+ ret = new (_alloc) Name(outputPath, baseaddr);
+ return true;
+ }
+ case Kind::kw_version: {
+ // VERSION
+ int major, minor;
+ if (!parseVersion(major, minor))
+ return false;
+ ret = new (_alloc) Version(major, minor);
+ return true;
+ }
+ default:
+ error(_tok, Twine("Unknown directive: ") + _tok._range);
+ return false;
+ }
+}
+
+bool Parser::parseExport(PECOFFLinkingContext::ExportDesc &result) {
+ consumeToken();
+ if (_tok._kind != Kind::identifier) {
+ ungetToken();
+ return false;
+ }
+ result.name = _tok._range;
+
+ consumeToken();
+ if (_tok._kind == Kind::equal) {
+ consumeToken();
+ if (_tok._kind != Kind::identifier)
+ return false;
+ result.externalName = result.name;
+ result.name = _tok._range;
+ } else {
+ ungetToken();
+ }
+
+ for (;;) {
+ consumeToken();
+ if (_tok._kind == Kind::identifier && _tok._range[0] == '@') {
+ _tok._range.drop_front().getAsInteger(10, result.ordinal);
+ consumeToken();
+ if (_tok._kind == Kind::kw_noname) {
+ result.noname = true;
+ } else {
+ ungetToken();
+ }
+ continue;
+ }
+ if (_tok._kind == Kind::kw_data) {
+ result.isData = true;
+ continue;
+ }
+ if (_tok._kind == Kind::kw_private) {
+ result.isPrivate = true;
+ continue;
+ }
+ ungetToken();
+ return true;
+ }
+}
+
+// HEAPSIZE/STACKSIZE reserve[,commit]
+bool Parser::parseMemorySize(uint64_t &reserve, uint64_t &commit) {
+ if (!consumeTokenAsInt(reserve))
+ return false;
+
+ consumeToken();
+ if (_tok._kind != Kind::comma) {
+ ungetToken();
+ commit = 0;
+ return true;
+ }
+
+ if (!consumeTokenAsInt(commit))
+ return false;
+ return true;
+}
+
+// NAME [outputPath] [BASE=address]
+bool Parser::parseName(std::string &outputPath, uint64_t &baseaddr) {
+ consumeToken();
+ if (_tok._kind == Kind::identifier) {
+ outputPath = _tok._range;
+ } else {
+ outputPath = "";
+ ungetToken();
+ return true;
+ }
+ consumeToken();
+ if (_tok._kind == Kind::kw_base) {
+ if (!expectAndConsume(Kind::equal, "'=' expected"))
+ return false;
+ if (!consumeTokenAsInt(baseaddr))
+ return false;
+ } else {
+ ungetToken();
+ baseaddr = 0;
+ }
+ return true;
+}
+
+// VERSION major[.minor]
+bool Parser::parseVersion(int &major, int &minor) {
+ consumeToken();
+ if (_tok._kind != Kind::identifier)
+ return false;
+ StringRef v1, v2;
+ std::tie(v1, v2) = _tok._range.split('.');
+ if (v1.getAsInteger(10, major))
+ return false;
+ if (v2.empty()) {
+ minor = 0;
+ } else if (v2.getAsInteger(10, minor)) {
+ return false;
+ }
+ return true;
+}
+
+} // moddef
+} // namespace lld
diff --git a/lib/Driver/WinLinkOptions.td b/lib/Driver/WinLinkOptions.td
new file mode 100644
index 0000000000000..a545639b5bb27
--- /dev/null
+++ b/lib/Driver/WinLinkOptions.td
@@ -0,0 +1,120 @@
+include "llvm/Option/OptParser.td"
+
+// link.exe accepts options starting with either a dash or a slash.
+
+// Flag that takes no arguments.
+class F<string name> : Flag<["/", "-", "-?"], name>;
+
+// Flag that takes one argument after ":".
+class P<string name, string help> :
+ Joined<["/", "-", "-?"], name#":">, HelpText<help>;
+
+// Boolean flag suffixed by ":no".
+multiclass B<string name, string help> {
+ def "" : F<name>;
+ def _no : F<name#":no">, HelpText<help>;
+}
+
+def alternatename : P<"alternatename", "Define weak alias">;
+def base : P<"base", "Base address of the program">;
+def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
+def nodefaultlib : P<"nodefaultlib", "Remove a default library">;
+def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
+def entry : P<"entry", "Name of entry point symbol">;
+// No help text because /failifmismatch is not intended to be used by the user.
+def export : P<"export", "Export a function">;
+def failifmismatch : P<"failifmismatch", "">;
+def heap : P<"heap", "Size of the heap">;
+def align : P<"align", "Section alignment">;
+def libpath : P<"libpath", "Additional library search path">;
+def mllvm : P<"mllvm", "Options to pass to LLVM">;
+def out : P<"out", "Path to file to write output">;
+def stack : P<"stack", "Size of the stack">;
+def machine : P<"machine", "Specify target platform">;
+def version : P<"version", "Specify a version number in the PE header">;
+def merge : P<"merge", "Combine sections">;
+def section : P<"section", "Specify section attributes">;
+def subsystem : P<"subsystem", "Specify subsystem">;
+def stub : P<"stub", "Specify DOS stub file">;
+def opt : P<"opt", "Control optimizations">;
+def implib : P<"implib", "Import library name">;
+def delayload : P<"delayload", "Delay loaded DLL name">;
+def pdb : P<"pdb", "PDB file path">;
+
+def manifest : F<"manifest">;
+def manifest_colon : P<"manifest", "Create manifest file">;
+def manifestuac : P<"manifestuac", "User access control">;
+def manifestfile : P<"manifestfile", "Manifest file path">;
+def manifestdependency : P<"manifestdependency",
+ "Attributes for <dependency> in manifest file">;
+
+// We cannot use multiclass P because class name "incl" is different
+// from its command line option name. We do this because "include" is
+// a reserved keyword in tablegen.
+def incl : Joined<["/", "-"], "include:">,
+ HelpText<"Force symbol to be added to symbol table as undefined one">;
+
+// "def" is also a keyword.
+def deffile : Joined<["/", "-"], "def:">,
+ HelpText<"Use module-definition file">;
+
+def nodefaultlib_all : F<"nodefaultlib">;
+def noentry : F<"noentry">;
+def dll : F<"dll">;
+def verbose : F<"verbose">;
+def debug : F<"debug">;
+def swaprun_cd : F<"swaprun:cd">;
+def swaprun_net : F<"swaprun:net">;
+def profile : F<"profile">;
+
+def force : F<"force">,
+ HelpText<"Allow undefined symbols when creating executables">;
+def force_unresolved : F<"force:unresolved">;
+
+defm nxcompat : B<"nxcompat", "Disable data execution provention">;
+defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">;
+defm allowbind: B<"allowbind", "Disable DLL binding">;
+defm fixed : B<"fixed", "Enable base relocations">;
+defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">;
+defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">;
+defm dynamicbase : B<"dynamicbase",
+ "Disable address space layout randomization">;
+defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">;
+defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">;
+
+def help : F<"help">;
+def help_q : Flag<["/?", "-?"], "">, Alias<help>;
+
+def DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>;
+
+// Flag for debug
+def lldmoduledeffile : Joined<["/", "-"], "lldmoduledeffile:">;
+
+//==============================================================================
+// The flags below do nothing. They are defined only for link.exe compatibility.
+//==============================================================================
+
+class QF<string name> : Joined<["/", "-", "-?"], name#":">;
+
+multiclass QB<string name> {
+ def "" : F<name>;
+ def _no : F<name#":no">;
+}
+
+def functionpadmin : F<"functionpadmin">;
+def ignoreidl : F<"ignoreidl">;
+def incremental : F<"incremental">;
+def no_incremental : F<"incremental:no">;
+def nologo : F<"nologo">;
+
+def delay : QF<"delay">;
+def errorreport : QF<"errorreport">;
+def idlout : QF<"idlout">;
+def ignore : QF<"ignore">;
+def maxilksize : QF<"maxilksize">;
+def pdbaltpath : QF<"pdbaltpath">;
+def tlbid : QF<"tlbid">;
+def tlbout : QF<"tlbout">;
+def verbose_all : QF<"verbose">;
+
+defm wx : QB<"wx">;