diff options
Diffstat (limited to 'lld/lib/Driver')
-rw-r--r-- | lld/lib/Driver/CMakeLists.txt | 8 | ||||
-rw-r--r-- | lld/lib/Driver/CoreDriver.cpp | 158 | ||||
-rw-r--r-- | lld/lib/Driver/CoreOptions.td | 25 | ||||
-rw-r--r-- | lld/lib/Driver/DarwinLdDriver.cpp | 197 | ||||
-rw-r--r-- | lld/lib/Driver/DarwinOptions.td | 30 | ||||
-rw-r--r-- | lld/lib/Driver/Driver.cpp | 78 | ||||
-rw-r--r-- | lld/lib/Driver/Drivers.cpp | 303 | ||||
-rw-r--r-- | lld/lib/Driver/GnuLdDriver.cpp | 241 | ||||
-rw-r--r-- | lld/lib/Driver/LDOptions.td | 39 | ||||
-rw-r--r-- | lld/lib/Driver/LinkerInvocation.cpp | 98 | ||||
-rw-r--r-- | lld/lib/Driver/UniversalDriver.cpp | 109 |
11 files changed, 853 insertions, 433 deletions
diff --git a/lld/lib/Driver/CMakeLists.txt b/lld/lib/Driver/CMakeLists.txt index 13e6298b9a2..30aa26aa9f1 100644 --- a/lld/lib/Driver/CMakeLists.txt +++ b/lld/lib/Driver/CMakeLists.txt @@ -2,12 +2,16 @@ set(LLVM_TARGET_DEFINITIONS LDOptions.td) tablegen(LLVM LDOptions.inc -gen-opt-parser-defs) set(LLVM_TARGET_DEFINITIONS CoreOptions.td) tablegen(LLVM CoreOptions.inc -gen-opt-parser-defs) +set(LLVM_TARGET_DEFINITIONS DarwinOptions.td) +tablegen(LLVM DarwinOptions.inc -gen-opt-parser-defs) add_public_tablegen_target(DriverOptionsTableGen) add_lld_library(lldDriver + CoreDriver.cpp + DarwinLdDriver.cpp Driver.cpp - Drivers.cpp - LinkerInvocation.cpp + GnuLdDriver.cpp + UniversalDriver.cpp ) add_dependencies(lldDriver DriverOptionsTableGen) diff --git a/lld/lib/Driver/CoreDriver.cpp b/lld/lib/Driver/CoreDriver.cpp new file mode 100644 index 00000000000..a4fb2166a4b --- /dev/null +++ b/lld/lib/Driver/CoreDriver.cpp @@ -0,0 +1,158 @@ +//===- 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/Driver/Driver.h" +#include "lld/ReaderWriter/CoreTargetInfo.h" +#include "lld/ReaderWriter/Reader.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/raw_ostream.h" +#include "llvm/Support/Signals.h" + +using namespace lld; + +namespace { + +// Create enum with OPT_xxx values for each option in DarwinOptions.td +enum CoreOpt { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, HELP, META) \ + OPT_##ID, +#include "CoreOptions.inc" + LastOption +#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, FLAGS, PARAM, \ + HELPTEXT, METAVAR) \ + { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS }, +#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 { + +bool CoreDriver::link(int argc, const char *argv[], raw_ostream &diagnostics) { + CoreTargetInfo info; + if (parse(argc, argv, info)) + return true; + + return Driver::link(info); +} + + +bool CoreDriver::parse(int argc, const char *argv[], + CoreTargetInfo &info, 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 true; + } + + for (auto it = parsedArgs->filtered_begin(OPT_UNKNOWN), + ie = parsedArgs->filtered_end(); it != ie; ++it) { + diagnostics << "warning: ignoring unknown argument: " + << (*it)->getAsString(*parsedArgs) << "\n"; + } + + + // Handle -e xxx + if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry)) + info.setEntrySymbolName(entry->getValue()); + + // Handle -o xxx + if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_output)) + info.setOutputPath(outpath->getValue()); + else + info.setOutputPath("-"); + + // Handle --dead_strip + if (parsedArgs->getLastArg(OPT_dead_strip)) + info.setDeadStripping(true); + else + info.setDeadStripping(false); + + // Handle --keep-globals + if (parsedArgs->getLastArg(OPT_keep_globals)) + info.setGlobalsAreDeadStripRoots(true); + else + info.setGlobalsAreDeadStripRoots(false); + + // Handle --undefines-are-errors + if (parsedArgs->getLastArg(OPT_undefines_are_errors)) { + info.setPrintRemainingUndefines(true); + info.setAllowRemainingUndefines(false); + } + else { + info.setPrintRemainingUndefines(false); + info.setAllowRemainingUndefines(true); + } + + // Handle --commons-search-archives + if (parsedArgs->getLastArg(OPT_commons_search_archives)) + info.setSearchArchivesToOverrideTentativeDefinitions(true); + else + info.setSearchArchivesToOverrideTentativeDefinitions(false); + + // Handle --add-pass xxx option + for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_add_pass), + ie = parsedArgs->filtered_end(); + it != ie; ++it) { + info.addPassNamed((*it)->getValue()); + } + + // Handle input files + for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_INPUT), + ie = parsedArgs->filtered_end(); + it != ie; ++it) { + info.appendInputFile((*it)->getValue()); + } + + return false; +} + +} // namespace lld + diff --git a/lld/lib/Driver/CoreOptions.td b/lld/lib/Driver/CoreOptions.td index 8a53fd35cbe..df7cb41737d 100644 --- a/lld/lib/Driver/CoreOptions.td +++ b/lld/lib/Driver/CoreOptions.td @@ -1,22 +1,15 @@ include "llvm/Option/OptParser.td" -def core : Flag<["-"], "core">; -def flavor : Separate<["-"], "flavor">; -def target : Separate<["-"], "target">, HelpText<"Target triple to link for">; -def mllvm : Separate<["-"], "mllvm">, HelpText<"Options to pass to LLVM">; - -def output : Joined<["-"], "output=">; -def entry : Joined<["-"], "entry=">; -def input_search_path : Joined<["-"], "input-search-path=">; -def output_type : Joined<["-"], "output-type=">; -def relocatable : Flag<["-"], "relocatable">; +def output : Separate<["-"], "o">; +def entry : Separate<["-"], "e">; -def OCTOTHORPE_OCTOTHORPE_OCTOTHORPE : Flag<["-"], "###">; +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 emit_yaml : Flag<["-"], "emit-yaml">; +def add_pass : Separate<["--"], "add-pass">; -def noinhibit_exec : Flag<["-"], "noinhibit-exec">, - HelpText<"Retain the executable output file whenever it is still usable">; +def target : Separate<["-"], "target">, HelpText<"Target triple to link for">; +def mllvm : Separate<["-"], "mllvm">, HelpText<"Options to pass to LLVM">; -def merge_strings : Flag<["-"], "merge-strings">, - HelpText<"Merge common strings across mergeable sections">; diff --git a/lld/lib/Driver/DarwinLdDriver.cpp b/lld/lib/Driver/DarwinLdDriver.cpp new file mode 100644 index 00000000000..1dbe5dfec0d --- /dev/null +++ b/lld/lib/Driver/DarwinLdDriver.cpp @@ -0,0 +1,197 @@ +//===- 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/Driver/Driver.h" +#include "lld/ReaderWriter/MachOTargetInfo.h" +#include "../ReaderWriter/MachO/MachOFormat.hpp" + +#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/raw_ostream.h" +#include "llvm/Support/Signals.h" + + +namespace { + +// Create enum with OPT_xxx values for each option in DarwinOptions.td +enum DarwinOpt { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, HELP, META) \ + OPT_##ID, +#include "DarwinOptions.inc" + LastOption +#undef OPTION +}; + +// Create prefix string literals used in DarwinOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "DarwinOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in DarwinOptions.td +static const llvm::opt::OptTable::Info infoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ + HELPTEXT, METAVAR) \ + { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS }, +#include "DarwinOptions.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)){} +}; + + +} // namespace anonymous + +namespace lld { + +bool DarwinLdDriver::linkMachO(int argc, const char *argv[], + raw_ostream &diagnostics) { + MachOTargetInfo info; + if (parse(argc, argv, info, diagnostics)) + return true; + + return link(info, diagnostics); +} + + + +bool DarwinLdDriver::parse(int argc, const char *argv[], + MachOTargetInfo &info, raw_ostream &diagnostics) { + // Parse command line options using DarwinOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + DarwinLdOptTable 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 true; + } + + for (auto it = parsedArgs->filtered_begin(OPT_UNKNOWN), + ie = parsedArgs->filtered_end(); it != ie; ++it) { + diagnostics << "warning: ignoring unknown argument: " + << (*it)->getAsString(*parsedArgs) << "\n"; + } + + // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static ) + if ( llvm::opt::Arg *kind = parsedArgs->getLastArg(OPT_dylib, OPT_relocatable, + OPT_bundle, OPT_static, OPT_preload)) { + switch (kind->getOption().getID()) { + case OPT_dylib: + info.setOutputFileType(mach_o::MH_DYLIB); + break; + case OPT_relocatable: + info.setPrintRemainingUndefines(false); + info.setAllowRemainingUndefines(true); + info.setOutputFileType(mach_o::MH_OBJECT); + break; + case OPT_bundle: + info.setOutputFileType(mach_o::MH_BUNDLE); + break; + case OPT_static: + info.setOutputFileType(mach_o::MH_EXECUTE); + break; + case OPT_preload: + info.setOutputFileType(mach_o::MH_PRELOAD); + break; + } + } + + // Handle -e xxx + if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry)) + info.setEntrySymbolName(entry->getValue()); + + // Handle -o xxx + if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_output)) + info.setOutputPath(outpath->getValue()); + + // Handle -dead_strip + if (parsedArgs->getLastArg(OPT_dead_strip)) + info.setDeadStripping(true); + + // Handle -arch xxx + if (llvm::opt::Arg *archStr = parsedArgs->getLastArg(OPT_arch)) { + info.setArch(llvm::StringSwitch<MachOTargetInfo::Arch>(archStr->getValue()) + .Case("x86_64", MachOTargetInfo::arch_x86_64) + .Case("i386", MachOTargetInfo::arch_x86) + .Case("armv6", MachOTargetInfo::arch_armv6) + .Case("armv7", MachOTargetInfo::arch_armv7) + .Case("armv7s", MachOTargetInfo::arch_armv7s) + .Default(MachOTargetInfo::arch_unknown)); + } + + // Handle -macosx_version_min or -ios_version_min + 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: + if (info.setOS(MachOTargetInfo::OS::macOSX, minOS->getValue())) { + diagnostics << "error: malformed macosx_version_min value\n"; + return true; + } + break; + case OPT_ios_version_min: + if (info.setOS(MachOTargetInfo::OS::iOS, minOS->getValue())) { + diagnostics << "error: malformed ios_version_min value\n"; + return true; + } + break; + case OPT_ios_simulator_version_min: + if (info.setOS(MachOTargetInfo::OS::iOS_simulator, minOS->getValue())) { + diagnostics << "error: malformed ios_simulator_version_min value\n"; + return true; + } + break; + } + } + else { + // No min-os version on command line, check environment variables + + } + + // Handle input files + for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_INPUT), + ie = parsedArgs->filtered_end(); + it != ie; ++it) { + info.appendInputFile((*it)->getValue()); + } + + return false; +} + + +} // namespace lld + + diff --git a/lld/lib/Driver/DarwinOptions.td b/lld/lib/Driver/DarwinOptions.td new file mode 100644 index 00000000000..b51d97d5602 --- /dev/null +++ b/lld/lib/Driver/DarwinOptions.td @@ -0,0 +1,30 @@ +include "llvm/Option/OptParser.td" + +// output kinds +def relocatable : Flag<["-"], "r">, HelpText<"create relocatable output">; +def static : Flag<["-"], "static">; +def dynamic : Flag<["-"], "dynamic">; +def dylib : Flag<["-"], "dylib">; +def bundle : Flag<["-"], "bundle">; +def execute : Flag<["-"], "execute">; +def preload : Flag<["-"], "preload">; + +// linking options +def dead_strip : Flag<["-"], "dead_strip">; +def entry : Separate<["-"], "e">, HelpText<"entry symbol name">; +def mllvm : Separate<["-"], "mllvm">, HelpText<"Options to pass to LLVM">; +def arch : Separate<["-"], "arch">, HelpText<"architecture">; +def macosx_version_min : Separate<["-"], "macosx_version_min">, + HelpText<"min Mac OS X version">; +def ios_version_min : Separate<["-"], "ios_version_min">, + HelpText<"min iOS version">; +def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">, + HelpText<"min iOS simulator version">; + +// search options +def L : Joined<["-"], "L">; + +// inputs and outputs +def output : Separate<["-"], "o">, HelpText<"output file name">; + + diff --git a/lld/lib/Driver/Driver.cpp b/lld/lib/Driver/Driver.cpp index 5f7f4e6f555..cb3c611c2ce 100644 --- a/lld/lib/Driver/Driver.cpp +++ b/lld/lib/Driver/Driver.cpp @@ -8,7 +8,81 @@ //===----------------------------------------------------------------------===// #include "lld/Driver/Driver.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/InputFiles.h" +#include "lld/Core/Resolver.h" +#include "lld/Core/PassManager.h" +#include "lld/ReaderWriter/Reader.h" +#include "lld/ReaderWriter/Writer.h" -using namespace lld; +#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/raw_ostream.h" + +namespace lld { + +/// This is where the link is actually performed. +bool Driver::link(const TargetInfo &targetInfo, raw_ostream &diagnostics) { + // Honor -mllvm + if (!targetInfo.llvmOptions().empty()) { + unsigned numArgs = targetInfo.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] = targetInfo.llvmOptions()[i]; + args[numArgs + 1] = 0; + llvm::cl::ParseCommandLineOptions(numArgs + 1, args); + } + + // Read inputs + InputFiles inputs; + for (const auto &input : targetInfo.inputFiles()) { + std::vector<std::unique_ptr<File>> files; + if (targetInfo.logInputFiles()) + llvm::outs() << input.getPath() << "\n"; + + error_code ec = targetInfo.readFile(input.getPath(), files); + if (ec) { + diagnostics << "Failed to read file: " << input.getPath() << ": " + << ec.message() << "\n"; + return true; + } + inputs.appendFiles(files); + } + + // Give target a chance to add files. + targetInfo.addImplicitFiles(inputs); + + // assign an ordinal to each file so sort() can preserve command line order + inputs.assignFileOrdinals(); + + // Do core linking. + Resolver resolver(targetInfo, inputs); + if (resolver.resolve()) { + if (!targetInfo.allowRemainingUndefines()) + return true; + } + MutableFile &merged = resolver.resultFile(); + + // Run passes on linked atoms. + PassManager pm; + targetInfo.addPasses(pm); + pm.runOnFile(merged); + + // Give linked atoms to Writer to generate output file. + if (error_code ec = targetInfo.writeFile(merged)) { + diagnostics << "Failed to write file '" << targetInfo.outputPath() + << "': " << ec.message() << "\n"; + return true; + } + + return false; +} + + +} // namespace -Driver::~Driver() {} diff --git a/lld/lib/Driver/Drivers.cpp b/lld/lib/Driver/Drivers.cpp deleted file mode 100644 index d2c1b732046..00000000000 --- a/lld/lib/Driver/Drivers.cpp +++ /dev/null @@ -1,303 +0,0 @@ -//===- lib/Driver/Drivers.cpp - Linker Driver Emulators -------------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// Concrete instances of the Driver interface. -/// -//===----------------------------------------------------------------------===// - -#include "lld/Driver/Driver.h" - -#include "lld/Core/LinkerOptions.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/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" - -using namespace lld; - -namespace core { -enum ID { - OPT_INVALID = 0, -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, HELP, META) \ - OPT_##ID, -#include "CoreOptions.inc" - LastOption -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "CoreOptions.inc" -#undef PREFIX - -static const llvm::opt::OptTable::Info InfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) \ - { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS }, -#include "CoreOptions.inc" -#undef OPTION -}; - -class CoreOptTable : public llvm::opt::OptTable { -public: - CoreOptTable() : OptTable(InfoTable, llvm::array_lengthof(InfoTable)){} -}; -} - -namespace ld { -enum LDOpt { - OPT_INVALID = 0, -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, HELP, META) \ - OPT_##ID, -#include "LDOptions.inc" - LastOption -#undef OPTION -}; - -#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "LDOptions.inc" -#undef PREFIX - -static const llvm::opt::OptTable::Info InfoTable[] = { -#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) \ - { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ - PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS }, -#include "LDOptions.inc" -#undef OPTION -}; - -class LDOptTable : public llvm::opt::OptTable { -public: - LDOptTable() : OptTable(InfoTable, llvm::array_lengthof(InfoTable)){} -}; -} - -class LDDriver LLVM_FINAL : public Driver { -public: - LDDriver(StringRef defaultTargetTriple) : Driver(defaultTargetTriple) {} - - virtual std::unique_ptr<llvm::opt::DerivedArgList> - transform(llvm::ArrayRef<const char *> args) { - assert(!_inputArgs && "transform may only be called once!"); - - unsigned missingIndex, missingCount; - _inputArgs.reset(_opt.ParseArgs( args.begin(), args.end() - , missingIndex, missingCount)); - - if (missingCount) { - llvm::errs() << "error: missing arg value for '" - << _inputArgs->getArgString(missingIndex) - << "' expected " << missingCount << " argument(s).\n"; - return std::unique_ptr<llvm::opt::DerivedArgList>(); - } - - for (llvm::opt::arg_iterator it = _inputArgs->filtered_begin(ld::OPT_UNKNOWN), - ie = _inputArgs->filtered_end(); - it != ie; ++it) { - llvm::errs() << "warning: ignoring unknown argument: " - << (*it)->getAsString(*_inputArgs) << "\n"; - } - - std::unique_ptr<llvm::opt::DerivedArgList> newArgs( - new llvm::opt::DerivedArgList(*_inputArgs)); - - bool isOutputDynamic = false; - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_target)) { - newArgs->AddSeparateArg( A, _core.getOption(core::OPT_target) - , A->getValue()); - } else { - assert(!_defaultTargetTriple.empty() && "Got empty target triple!"); - newArgs->AddSeparateArg(nullptr, _core.getOption(core::OPT_target) - , _defaultTargetTriple); - } - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_entry)) - newArgs->AddJoinedArg(A, _core.getOption(core::OPT_entry), A->getValue()); - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_output)) - newArgs->AddJoinedArg(A, _core.getOption(core::OPT_output), - A->getValue()); - else - newArgs->AddJoinedArg(nullptr, _core.getOption(core::OPT_output), - "a.out"); - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_static)) - newArgs->AddJoinedArg(A, _core.getOption(core::OPT_output_type), - newArgs->MakeArgString("static")); - else { - newArgs->AddJoinedArg(nullptr, _core.getOption(core::OPT_output_type), - newArgs->MakeArgString("dynamic")); - isOutputDynamic = true; - } - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_relocatable)) - newArgs->AddFlagArg(A, _core.getOption(core::OPT_relocatable)); - - if (llvm::opt::Arg *A = - _inputArgs->getLastArg(ld::OPT_OCTOTHORPE_OCTOTHORPE_OCTOTHORPE)) - newArgs->AddFlagArg(A, _core.getOption( - core::OPT_OCTOTHORPE_OCTOTHORPE_OCTOTHORPE)); - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_emit_yaml)) - newArgs->AddFlagArg(A, _core.getOption(core::OPT_emit_yaml)); - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_noinhibit_exec)) - newArgs->AddFlagArg(A, _core.getOption(core::OPT_noinhibit_exec)); - - if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_merge_strings)) - newArgs->AddFlagArg(A, _core.getOption(core::OPT_merge_strings)); - - // Copy search paths. - for (llvm::opt::arg_iterator it = _inputArgs->filtered_begin(ld::OPT_L), - ie = _inputArgs->filtered_end(); - it != ie; ++it) { - newArgs->AddPositionalArg( - *it, _core.getOption(core::OPT_input_search_path), (*it)->getValue()); - _inputSearchPaths.push_back((*it)->getValue()); - } - - // Copy input args. - for (llvm::opt::arg_iterator it = _inputArgs->filtered_begin(ld::OPT_INPUT, - ld::OPT_l), - ie = _inputArgs->filtered_end(); - it != ie; ++it) { - StringRef inputPath; - if ((*it)->getOption().getID() == ld::OPT_l) { - StringRef libName = (*it)->getValue(); - SmallString<128> p; - for (const auto &path : _inputSearchPaths) { - if (isOutputDynamic) { - p = path; - llvm::sys::path::append(p, Twine("lib") + libName + ".so"); - if (llvm::sys::fs::exists(p.str())) { - inputPath = newArgs->MakeArgString(p); - break; - } - } - p = path; - llvm::sys::path::append(p, Twine("lib") + libName + ".a"); - if (llvm::sys::fs::exists(p.str())) { - inputPath = newArgs->MakeArgString(p); - break; - } - } - if (inputPath.empty()) - llvm_unreachable("Failed to lookup library!"); - } else - inputPath = (*it)->getValue(); - newArgs->AddPositionalArg(*it, _core.getOption(core::OPT_INPUT), - inputPath); - } - - // Copy mllvm - for (llvm::opt::arg_iterator it = _inputArgs->filtered_begin(ld::OPT_mllvm), - ie = _inputArgs->filtered_end(); - it != ie; ++it) { - newArgs->AddPositionalArg(*it, _core.getOption(core::OPT_mllvm), - (*it)->getValue()); - } - - return std::move(newArgs); - } - -private: - std::unique_ptr<llvm::opt::InputArgList> _inputArgs; - core::CoreOptTable _core; - ld::LDOptTable _opt; - // Local cache of search paths so we can do lookups on -l. - std::vector<std::string> _inputSearchPaths; -}; - -std::unique_ptr<Driver> Driver::create( Driver::Flavor flavor - , StringRef defaultTargetTriple) { - switch (flavor) { - case Flavor::ld: - return std::unique_ptr<Driver>(new LDDriver(defaultTargetTriple)); - case Flavor::core: - case Flavor::ld64: - case Flavor::link: - case Flavor::invalid: - break; - } - llvm_unreachable("Unsupported flavor"); -} - -std::unique_ptr<llvm::opt::ArgList> -lld::parseCoreArgs(llvm::ArrayRef<const char *> args) { - core::CoreOptTable core; - unsigned missingIndex, missingCount; - std::unique_ptr<llvm::opt::ArgList> list( - core.ParseArgs( args.begin(), args.end(), missingIndex, missingCount)); - - if (missingCount) { - llvm::errs() << "error: missing arg value for '" - << list->getArgString(missingIndex) - << "' expected " << missingCount << " argument(s).\n"; - return std::unique_ptr<llvm::opt::ArgList>(); - } - - bool hasUnknown = false; - for (llvm::opt::arg_iterator it = list->filtered_begin(ld::OPT_UNKNOWN), - ie = list->filtered_end(); - it != ie; ++it) { - llvm::errs() << "error: ignoring unknown argument: " - << (*it)->getAsString(*list) << "\n"; - hasUnknown = true; - } - if (hasUnknown) - return std::unique_ptr<llvm::opt::ArgList>(); - - return list; -} - -LinkerOptions lld::generateOptions(const llvm::opt::ArgList &args) { - LinkerOptions ret; - - for (llvm::opt::arg_iterator it = args.filtered_begin(ld::OPT_INPUT), - ie = args.filtered_end(); - it != ie; ++it) { - ret._input.push_back(LinkerInput((*it)->getValue())); - } - - StringRef outputType = args.getLastArgValue(core::OPT_output_type); - ret._outputKind = llvm::StringSwitch<OutputKind>(outputType) - .Case("static", OutputKind::StaticExecutable) - .Case("dynamic", OutputKind::DynamicExecutable) - .Case("relocatable", OutputKind::Relocatable) - .Case("shared", OutputKind::Shared) - .Case("stubs", OutputKind::SharedStubs) - .Case("core", OutputKind::Core) - .Case("debug-symbols", OutputKind::DebugSymbols) - .Case("bundle", OutputKind::Bundle) - .Case("preload", OutputKind::Preload) - .Default(OutputKind::Invalid); - - ret._inputSearchPaths = args.getAllArgValues(core::OPT_input_search_path); - ret._llvmArgs = args.getAllArgValues(core::OPT_mllvm); - ret._target = llvm::Triple::normalize(args.getLastArgValue(core::OPT_target)); - ret._outputPath = args.getLastArgValue(core::OPT_output); - ret._entrySymbol = args.getLastArgValue(core::OPT_entry); - if (args.hasArg(core::OPT_relocatable)) - ret._outputKind = OutputKind::Relocatable; - ret._outputCommands = args.hasArg(core::OPT_OCTOTHORPE_OCTOTHORPE_OCTOTHORPE); - ret._outputYAML = args.hasArg(core::OPT_emit_yaml); - ret._noInhibitExec = args.hasArg(core::OPT_noinhibit_exec); - ret._mergeCommonStrings = args.hasArg(core::OPT_merge_strings); - - return std::move(ret); -} diff --git a/lld/lib/Driver/GnuLdDriver.cpp b/lld/lib/Driver/GnuLdDriver.cpp new file mode 100644 index 00000000000..55e38580029 --- /dev/null +++ b/lld/lib/Driver/GnuLdDriver.cpp @@ -0,0 +1,241 @@ +//===- 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/ELFTargetInfo.h" + +#include "llvm/ADT/ArrayRef.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/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/raw_ostream.h" +#include "llvm/Support/Signals.h" + +using namespace lld; + + +namespace { + +// Create enum with OPT_xxx values for each option in LDOptions.td +enum LDOpt { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, HELP, META) \ + OPT_##ID, +#include "LDOptions.inc" + LastOption +#undef OPTION +}; + +// Create prefix string literals used in LDOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "LDOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in LDOptions.td +static const llvm::opt::OptTable::Info infoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ + HELPTEXT, METAVAR) \ + { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS }, +#include "LDOptions.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)){} +}; + +} // namespace + + + +bool GnuLdDriver::linkELF(int argc, const char *argv[], + raw_ostream &diagnostics) { + std::unique_ptr<ELFTargetInfo> options(parse(argc, argv, diagnostics)); + if (!options) + return true; + + return link(*options, diagnostics); +} + + +std::unique_ptr<ELFTargetInfo> GnuLdDriver::parse(int argc, const char *argv[], + raw_ostream &diagnostics) { + // Parse command line options using LDOptions.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) { + diagnostics << "error: missing arg value for '" + << parsedArgs->getArgString(missingIndex) + << "' expected " << missingCount << " argument(s).\n"; + return nullptr; + } + + for (auto it = parsedArgs->filtered_begin(OPT_UNKNOWN), + ie = parsedArgs->filtered_end(); it != ie; ++it) { + diagnostics << "warning: ignoring unknown argument: " + << (*it)->getAsString(*parsedArgs) << "\n"; + } + + // Handle --help + if (parsedArgs->getLastArg(OPT_help)) { + table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); + return nullptr; + } + + // Use -target or use default target triple to instantiate TargetInfo + llvm::Triple triple; + if (llvm::opt::Arg *trip = parsedArgs->getLastArg(OPT_target)) + triple = llvm::Triple(trip->getValue()); + else + triple = getDefaultTarget(argv[0]); + std::unique_ptr<ELFTargetInfo> options(ELFTargetInfo::create(triple)); + + if (!options) { + diagnostics << "unknown target triple\n"; + return nullptr; + } + + // Handle -e xxx + if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry)) + options->setEntrySymbolName(entry->getValue()); + + // Handle -emit-yaml + if (parsedArgs->getLastArg(OPT_emit_yaml)) + options->setOutputYAML(true); + + // Handle -o xxx + if (llvm::opt::Arg *output = parsedArgs->getLastArg(OPT_output)) + options->setOutputPath(output->getValue()); + else if (options->outputYAML()) + options->setOutputPath("-"); // yaml writes to stdout by default + else + options->setOutputPath("a.out"); + + // Handle -r, -shared, or -static + if ( llvm::opt::Arg *kind = parsedArgs->getLastArg(OPT_relocatable, + OPT_shared, + OPT_static)) { + switch (kind->getOption().getID()) { + case OPT_relocatable: + options->setOutputFileType(llvm::ELF::ET_REL); + options->setPrintRemainingUndefines(false); + options->setAllowRemainingUndefines(true); + break; + case OPT_shared: + options->setOutputFileType(llvm::ELF::ET_DYN); + break; + case OPT_static: + options->setOutputFileType(llvm::ELF::ET_EXEC); + options->setIsStaticExecutable(true); + break; + } + } + else { + options->setOutputFileType(llvm::ELF::ET_EXEC); + options->setIsStaticExecutable(false); + } + + // Handle --noinhibit-exec + if (parsedArgs->getLastArg(OPT_noinhibit_exec)) + options->setAllowRemainingUndefines(true); + + // Handle --force-load + if (parsedArgs->getLastArg(OPT_force_load)) + options->setForceLoadAllArchives(true); + + // Handle --merge-strings + if (parsedArgs->getLastArg(OPT_merge_strings)) + options->setMergeCommonStrings(true); + + // Handle -t + if (parsedArgs->getLastArg(OPT_t)) + options->setLogInputFiles(true); + + // Handle -Lxxx + for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_L), + ie = parsedArgs->filtered_end(); + it != ie; ++it) { + options->appendSearchPath((*it)->getValue()); + } + + // Copy mllvm + for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_mllvm), + ie = parsedArgs->filtered_end(); + it != ie; ++it) { + options->appendLLVMOption((*it)->getValue()); + } + + // Handle input files (full paths and -lxxx) + for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_INPUT,OPT_l), + ie = parsedArgs->filtered_end(); + it != ie; ++it) { + switch ((*it)->getOption().getID()) { + case OPT_INPUT: + options->appendInputFile((*it)->getValue()); + break; + case OPT_l: + if (options->appendLibrary((*it)->getValue())) { + diagnostics << "Failed to find library for " + << (*it)->getValue() << "\n"; + return nullptr; + } + break; + default: + llvm_unreachable("input option type not handled"); + } + } + + // Validate the combination of options used. + if (options->validate(diagnostics)) + return nullptr; + + return options; +} + + +/// 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/lld/lib/Driver/LDOptions.td b/lld/lib/Driver/LDOptions.td index 516d487b41a..47e8a3cf78a 100644 --- a/lld/lib/Driver/LDOptions.td +++ b/lld/lib/Driver/LDOptions.td @@ -1,27 +1,29 @@ include "llvm/Option/OptParser.td" -def flavor : Separate<["-"], "flavor">; -def target : Separate<["-"], "target">, HelpText<"Target triple to link for">; +def target : Separate<["-"], "target">, + MetaVarName<"<triple>">, + HelpText<"Target triple to link for">; + def mllvm : Separate<["-"], "mllvm">, HelpText<"Options to pass to LLVM">; -def entry : Joined<["--"], "entry=">; +def entry : Joined<["--"], "entry=">, + HelpText<"Name of entry point symbol">; def entry_e : Separate<["-"], "e">, Alias<entry>; -def output : Joined<["--"], "output=">; -def output_e : Separate<["-"], "o">, Alias<output>; - +def output : Separate<["-"], "o">, + MetaVarName<"<path>">, + HelpText<"Path to file to write output">; + def relocatable : Flag<["--"], "relocatable">; def relocatable_r : Flag<["-"], "r">, Alias<relocatable>; def dynamic_linker : Separate<["-"], "dynamic-linker">; -def OCTOTHORPE_OCTOTHORPE_OCTOTHORPE : Flag<["-"], "###">; - -def emit_yaml : Flag<["-"], "emit-yaml">; def m : Separate<["-"], "m">; def z : Separate<["-"], "z">; +def t : Flag<["-"], "t">; def rpath : Separate<["-"], "rpath">; def soname : Separate<["-"], "soname">; @@ -32,12 +34,25 @@ def end_group : Flag<["--"], "end-group">; def build_id : Flag<["--"], "build-id">; def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">; -def L : Joined<["-"], "L">; -def l : Joined<["-"], "l">; +def help : Flag<["--"], "help">; + +def L : Joined<["-"], "L">, + MetaVarName<"<dir>">, + HelpText<"Directory to search for libraries">; +def l : Joined<["-"], "l">, + MetaVarName<"<libName>">, + HelpText<"Root name of library to use">; def hash_style : Joined <["--"], "hash-style=">; def noinhibit_exec : Flag<["--"], "noinhibit-exec">, HelpText<"Retain the executable output file whenever it is still usable">; -def merge_strings : Flag<["-"], "merge-strings">, +def merge_strings : Flag<["--"], "merge-strings">, HelpText<"Merge common strings across mergeable sections">; + + +// extensions +def emit_yaml : Flag<["-"], "emit-yaml">, + HelpText<"Write YAML instead of ELF">; +def force_load : Flag<["--"], "force-load">, + HelpText<"Force load of all members in all static libraries">; diff --git a/lld/lib/Driver/LinkerInvocation.cpp b/lld/lib/Driver/LinkerInvocation.cpp deleted file mode 100644 index 21662774bd9..00000000000 --- a/lld/lib/Driver/LinkerInvocation.cpp +++ /dev/null @@ -1,98 +0,0 @@ -//===- lib/Driver/LinkerInvocation.cpp - Linker Invocation ----------------===// -// -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lld/Driver/LinkerInvocation.h" - -#include "lld/Core/InputFiles.h" -#include "lld/Core/PassManager.h" -#include "lld/Core/Resolver.h" -#include "lld/ReaderWriter/ELFTargetInfo.h" -#include "lld/ReaderWriter/Reader.h" -#include "lld/ReaderWriter/Writer.h" - -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/raw_ostream.h" - -using namespace lld; - -namespace { -std::unique_ptr<TargetInfo> createTargetInfo(const LinkerOptions &lo) { - return ELFTargetInfo::create(lo); -} -} - -void LinkerInvocation::operator()() { - // Honor -mllvm - if (!_options._llvmArgs.empty()) { - unsigned NumArgs = _options._llvmArgs.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] = _options._llvmArgs[i].c_str(); - Args[NumArgs + 1] = 0; - llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args); - } - - // Create target. - std::unique_ptr<TargetInfo> targetInfo(createTargetInfo(_options)); - - if (!targetInfo) { - llvm::errs() << "Failed to create target for " << _options._target - << "\n"; - return; - } - - // Read inputs - InputFiles inputs; - for (const auto &input : _options._input) { - auto reader = targetInfo->getReader(input); - if (error_code ec = reader) { - llvm::errs() << "Failed to get reader for: " << input.getPath() << ": " - << ec.message() << "\n"; - return; - } - - auto buffer = input.getBuffer(); - if (error_code ec = buffer) { - llvm::errs() << "Failed to read file: " << input.getPath() << ": " - << ec.message() << "\n"; - return; - } - - std::vector<std::unique_ptr<File>> files; - if (llvm::error_code ec = reader->parseFile(std::unique_ptr<MemoryBuffer>(MemoryBuffer::getMemBuffer(buffer->getBuffer(), buffer->getBufferIdentifier())), files)) { - llvm::errs() << "Failed to read file: " << input.getPath() << ": " - << ec.message() << "\n"; - return; - } - inputs.appendFiles(files); - } - inputs.assignFileOrdinals(); - - auto writer = targetInfo->getWriter(); - - // Give writer a chance to add files - writer->addFiles(inputs); - - Resolver resolver(*targetInfo, inputs); - resolver.resolve(); - MutableFile &merged = resolver.resultFile(); - - PassManager pm; - targetInfo->addPasses(pm); - pm.runOnFile(merged); - - if (error_code ec = writer) { - llvm::errs() << "Failed to get writer: " << ec.message() << ".\n"; - return; - } - - if (error_code ec = writer->writeFile(merged, _options._outputPath)) - llvm::errs() << "Failed to write file: " << ec.message() << "\n"; -} diff --git a/lld/lib/Driver/UniversalDriver.cpp b/lld/lib/Driver/UniversalDriver.cpp new file mode 100644 index 00000000000..5164f9da69c --- /dev/null +++ b/lld/lib/Driver/UniversalDriver.cpp @@ -0,0 +1,109 @@ +//===- 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 +/// +/// Concrete instance of the Driver for darwin's ld. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "lld/ReaderWriter/MachOTargetInfo.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringExtras.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/raw_ostream.h" +#include "llvm/Support/Signals.h" + +#include <memory> + +namespace lld { + + +bool UniversalDriver::link(int argc, const char *argv[]) { + // Convert argv[] C-array to vector. + std::vector<const char *> args; + args.assign(&argv[0], &argv[argc]); + + // Determine flavor of link based on command name or -flavor argument. + // Note: 'args' is modified to remove -flavor option. + Flavor flavor = selectFlavor(args); + + // Switch to appropriate driver. + switch (flavor) { + case Flavor::gnu_ld: + return GnuLdDriver::linkELF(args.size(), &args[0]); + case Flavor::darwin_ld: + return DarwinLdDriver::linkMachO(args.size(), &args[0]); + case Flavor::core: + return CoreDriver::link(args.size(), &args[0]); + case Flavor::win_link: + llvm_unreachable("Unsupported flavor"); + case Flavor::invalid: + return true; + } +} + + + + + +/// Pick the flavor of driver to use based on the command line and +/// host environment. +UniversalDriver::Flavor UniversalDriver::selectFlavor( + std::vector<const char*> &args) { + // -core as first arg is shorthand for -flavor core. + if (args.size() >= 1 && StringRef(args[1]) == "-core") { + args.erase(args.begin() + 1); + return Flavor::core; + } + // Handle -flavor as first arg. + if (args.size() >= 2 && StringRef(args[1]) == "-flavor") { + Flavor flavor = strToFlavor(args[2]); + args.erase(args.begin() + 1); + args.erase(args.begin() + 1); + if (flavor == Flavor::invalid) + llvm::errs() << "error: '" << args[2] << "' invalid value for -flavor.\n"; + return flavor; + } + + // Check if flavor is at end of program name (e.g. "lld-gnu"); + SmallVector<StringRef, 3> components; + llvm::SplitString(args[0], components, "-"); + Flavor flavor = strToFlavor(components.back()); + + // If flavor still undetermined, then error out. + if (flavor == Flavor::invalid) + llvm::errs() << "error: failed to determine driver flavor from program name" + " '" << args[0] << "'.\n"; + return flavor; +} + +/// Maps flavor strings to Flavor enum values. +UniversalDriver::Flavor UniversalDriver::strToFlavor(StringRef str) { + return llvm::StringSwitch<Flavor>(str) + .Case("gnu", Flavor::gnu_ld) + .Case("darwin", Flavor::darwin_ld) + .Case("link", Flavor::win_link) + .Case("core", Flavor::core) + .Case("ld", Flavor::gnu_ld) // deprecated + .Default(Flavor::invalid); +} + + +} // namespace lld |