diff options
Diffstat (limited to 'lld/lib/Driver/GnuLdDriver.cpp')
-rw-r--r-- | lld/lib/Driver/GnuLdDriver.cpp | 241 |
1 files changed, 241 insertions, 0 deletions
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()); +} + |