From 9ff4be240a3cfc19ac0b4215967626a9ea2b04d0 Mon Sep 17 00:00:00 2001 From: "Michael J. Spencer" Date: Sat, 8 Dec 2012 00:47:36 +0000 Subject: [PATCH] Add the core architecture for the lld driver. This includes selecting which driver to emulate, option parsing, invocation building, and running the link. This currently only supports a very basic subset of ld for x86_64-linux. lld -flavor ld obj.o -o obj or symlink lld as (ld , link, ld64, core) to get the desired behavior without -flavor. llvm-svn: 169659 --- lld/include/lld/Core/ErrorOr.h | 213 ++++++++++++++++++++++++++++++ lld/include/lld/Driver/Driver.h | 64 +++++++++ lld/include/lld/Driver/LinkerInvocation.h | 34 +++++ lld/include/lld/Driver/LinkerOptions.h | 126 ++++++++++++++++++ lld/include/lld/Driver/Target.h | 46 +++++++ lld/lib/CMakeLists.txt | 1 + lld/lib/Driver/CMakeLists.txt | 29 ++++ lld/lib/Driver/CoreOptions.td | 7 + lld/lib/Driver/Driver.cpp | 14 ++ lld/lib/Driver/Drivers.cpp | 180 +++++++++++++++++++++++++ lld/lib/Driver/LDOptions.td | 13 ++ lld/lib/Driver/LinkerInvocation.cpp | 70 ++++++++++ lld/lib/Driver/Target.cpp | 14 ++ lld/lib/Driver/Targets.cpp | 75 +++++++++++ lld/tools/lld/CMakeLists.txt | 4 + lld/tools/lld/lld.cpp | 135 ++++++++++++++++++- 16 files changed, 1020 insertions(+), 5 deletions(-) create mode 100644 lld/include/lld/Core/ErrorOr.h create mode 100644 lld/include/lld/Driver/Driver.h create mode 100644 lld/include/lld/Driver/LinkerInvocation.h create mode 100644 lld/include/lld/Driver/LinkerOptions.h create mode 100644 lld/include/lld/Driver/Target.h create mode 100644 lld/lib/Driver/CMakeLists.txt create mode 100644 lld/lib/Driver/CoreOptions.td create mode 100644 lld/lib/Driver/Driver.cpp create mode 100644 lld/lib/Driver/Drivers.cpp create mode 100644 lld/lib/Driver/LDOptions.td create mode 100644 lld/lib/Driver/LinkerInvocation.cpp create mode 100644 lld/lib/Driver/Target.cpp create mode 100644 lld/lib/Driver/Targets.cpp diff --git a/lld/include/lld/Core/ErrorOr.h b/lld/include/lld/Core/ErrorOr.h new file mode 100644 index 0000000..403b4a5 --- /dev/null +++ b/lld/include/lld/Core/ErrorOr.h @@ -0,0 +1,213 @@ +//===- lld/Core/ErrorOr.h - Error Smart Pointer ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Provides ErrorOr smart pointer. +/// +/// This should be moved to LLVMSupport when someone has time to make it c++03. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_CORE_ERROR_OR_H +#define LLD_CORE_ERROR_OR_H + +#include "llvm/Support/AlignOf.h" +#include "llvm/Support/system_error.h" + +#include +#include + +namespace lld { +template +class ErrorOrBase { + static const bool isRef = std::is_reference::value; + typedef std::reference_wrapper::type> wrap; + +public: + typedef typename + std::conditional< isRef + , wrap + , T + >::type storage_type; + +private: + typedef T &reference; + typedef typename std::remove_reference::type *pointer; + + ErrorOrBase(const ErrorOrBase&) LLVM_DELETED_FUNCTION; + ErrorOrBase &operator =(const ErrorOrBase&) LLVM_DELETED_FUNCTION; + ErrorOrBase(ErrorOrBase &&other) LLVM_DELETED_FUNCTION; + ErrorOrBase &operator =(ErrorOrBase &&other) LLVM_DELETED_FUNCTION; + +public: + ErrorOrBase() : _error(llvm::make_error_code(llvm::errc::invalid_argument)) {} + + ErrorOrBase(llvm::error_code ec) { + if (!_error) + get()->~storage_type(); + _error = ec; + } + + ErrorOrBase(T t) : _error(llvm::error_code::success()) { + new (get()) storage_type(t); + } + + ~ErrorOrBase() { + if (!_error) + get()->~storage_type(); + } + + /// \brief Return false if there is an error. + operator bool() { + return !_error; + } + + operator llvm::error_code() { + return _error; + } + + operator reference() { + return *get(); + } + + pointer operator ->() { + return toPointer(get()); + } + + reference operator *() { + return *get(); + } + +private: + pointer toPointer(pointer t) { + return t; + } + + pointer toPointer(wrap *t) { + return &t->get(); + } + +protected: + storage_type *get() { + assert(!_error && "T not valid!"); + return reinterpret_cast(_t.buffer); + } + + llvm::error_code _error; + llvm::AlignedCharArrayUnion _t; +}; + +/// \brief Represents either an error or a value T. +/// +/// ErrorOr is a pointer-like class that represents the result of an +/// operation. The result is either an error, or a value of type T. This is +/// designed to emulate the usage of returning a pointer where nullptr indicates +/// failure. However instead of just knowing that the operation failed, we also +/// have an error_code that describes why it failed. +/// +/// It is used like the following. +/// \code +/// ErrorOr getBuffer(); +/// void handleError(error_code ec); +/// +/// auto buffer = getBuffer(); +/// if (!buffer) +/// handleError(buffer); +/// buffer->write("adena"); +/// \endcode +/// +/// An implicit conversion to bool provides a way to check if there was an +/// error. The unary * and -> operators provide pointer like access to the +/// value. Accessing the value when there is an error has undefined behavior. +/// +/// When T is a reference type the behaivor is slightly different. The reference +/// is held in a std::reference_wrapper::type>, and +/// there is special handling to make operator -> work as if T was not a +/// reference. +/// +/// T cannot be a rvalue reference. +template::storage_type>::value> +class ErrorOr; + +template +class ErrorOr : public ErrorOrBase { + ErrorOr(const ErrorOr &other) LLVM_DELETED_FUNCTION; + ErrorOr &operator =(const ErrorOr &other) LLVM_DELETED_FUNCTION; +public: + ErrorOr(llvm::error_code ec) : ErrorOrBase(ec) {} + ErrorOr(T t) : ErrorOrBase(t) {} + ErrorOr(ErrorOr &&other) : ErrorOrBase() { + // Get the other value. + if (!other._error) + new (this->get()) + typename ErrorOrBase::storage_type(std::move(*other.get())); + + // Get the other error. + this->_error = other._error; + + // Make sure other doesn't try to delete its storage. + other._error = llvm::make_error_code(llvm::errc::invalid_argument); + } + + ErrorOr &operator =(ErrorOr &&other) { + // Delete any existing value. + if (!this->_error) + this->get()->~storage_type(); + + // Get the other value. + if (!other._error) + new (this->get()) + typename ErrorOrBase::storage_type(std::move(*other.get())); + + // Get the other error. + this->_error = other._error; + + // Make sure other doesn't try to delete its storage. + other._error = llvm::make_error_code(llvm::errc::invalid_argument); + } +}; + +template +class ErrorOr : public ErrorOrBase { + static_assert(std::is_copy_constructible::value, + "T must be copy or move constructible!"); + + ErrorOr(ErrorOr &&other) LLVM_DELETED_FUNCTION; + ErrorOr &operator =(ErrorOr &&other) LLVM_DELETED_FUNCTION; +public: + ErrorOr(llvm::error_code ec) : ErrorOrBase(ec) {} + ErrorOr(T t) : ErrorOrBase(t) {} + ErrorOr(const ErrorOr &other) : ErrorOrBase() { + // Get the other value. + if (!other._error) + new (this->get()) typename ErrorOrBase::storage_type(*other.get()); + + // Get the other error. + this->_error = other._error; + } + + ErrorOr &operator =(const ErrorOr &other) { + // Delete any existing value. + if (!this->_error) + this->get()->~storage_type(); + + // Get the other value. + if (!other._error) + new (this->get()) typename ErrorOrBase::storage_type(*other.get()); + + // Get the other error. + this->_error = other._error; + } +}; +} + +#endif diff --git a/lld/include/lld/Driver/Driver.h b/lld/include/lld/Driver/Driver.h new file mode 100644 index 0000000..d2ff7b4 --- /dev/null +++ b/lld/include/lld/Driver/Driver.h @@ -0,0 +1,64 @@ +//===- lld/Driver/Driver.h - Linker Driver Emulator -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Interface and factory for creating a specific driver emulator. A Driver is +/// used to transform command line arguments into command line arguments for +/// core. Core arguments are used to generate a LinkerOptions object. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_DRIVER_DRIVER_H +#define LLD_DRIVER_DRIVER_H + +#include "lld/Core/LLVM.h" + +#include "llvm/Option/ArgList.h" + +#include +#include + +namespace lld { +struct LinkerOptions; + +/// \brief Base class for all Drivers. +class Driver { +protected: + Driver(StringRef defaultTargetTriple) + : _defaultTargetTriple(defaultTargetTriple) {} + + std::string _defaultTargetTriple; + +public: + enum class Flavor { + invalid, + ld, + link, + ld64, + core + }; + + virtual ~Driver(); + + virtual std::unique_ptr + transform(llvm::ArrayRef args) = 0; + + /// \param flavor driver flavor to create. + /// \param defaultTargetTriple target triple as determined by the program name + /// or host. May be overridden by -target. + /// \returns the created driver. + static std::unique_ptr create(Flavor flavor, + StringRef defaultTargetTriple); +}; + +LinkerOptions generateOptions(const llvm::opt::ArgList &args); +} // end namespace lld + +#endif diff --git a/lld/include/lld/Driver/LinkerInvocation.h b/lld/include/lld/Driver/LinkerInvocation.h new file mode 100644 index 0000000..d543ebb --- /dev/null +++ b/lld/include/lld/Driver/LinkerInvocation.h @@ -0,0 +1,34 @@ +//===- lld/Driver/LinkerInvocation.h - Linker Invocation ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Drives the actual link. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_DRIVER_LINKER_INVOCATION_H +#define LLD_DRIVER_LINKER_INVOCATION_H + +#include "lld/Driver/LinkerOptions.h" + +namespace lld { +class LinkerInvocation { +public: + LinkerInvocation(const LinkerOptions &lo) : _options(lo) {} + + /// \brief Perform the link. + void operator()(); + +private: + const LinkerOptions &_options; +}; +} + +#endif diff --git a/lld/include/lld/Driver/LinkerOptions.h b/lld/include/lld/Driver/LinkerOptions.h new file mode 100644 index 0000000..801c401 --- /dev/null +++ b/lld/include/lld/Driver/LinkerOptions.h @@ -0,0 +1,126 @@ +//===- lld/Driver/LinkerOptions.h - Linker Options ------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// All linker options to be provided to a LinkerInvocation. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_DRIVER_LINKER_OPTIONS_H +#define LLD_DRIVER_LINKER_OPTIONS_H + +#include "lld/Core/ErrorOr.h" +#include "lld/Core/LLVM.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" + +#include + +namespace lld { +enum class InputKind { + Unknown, + YAML, + Native, + Object, + LLVM, + Script +}; + +class LinkerInput { + LinkerInput(const LinkerInput &) LLVM_DELETED_FUNCTION; + +public: + LinkerInput(StringRef file, InputKind kind = InputKind::Unknown) + : _file(file) + , _kind(kind) {} + + LinkerInput(llvm::MemoryBuffer *buffer, InputKind kind = InputKind::Unknown) + : _buffer(buffer) + , _file(_buffer->getBufferIdentifier()) + , _kind(kind) {} + + LinkerInput(LinkerInput &&other) + : _buffer(std::move(other._buffer)) + , _file(std::move(other._file)) + , _kind(other._kind) {} + + ErrorOr getBuffer() const { + if (!_buffer) { + llvm::OwningPtr buf; + if (error_code ec = llvm::MemoryBuffer::getFileOrSTDIN(_file, buf)) + return ec; + _buffer.reset(buf.take()); + } + + return *_buffer; + } + + ErrorOr getKind() const { + if (_kind == InputKind::Unknown) { + _kind = llvm::StringSwitch(getPath()) + .EndsWith(".objtxt", InputKind::YAML) + .EndsWith(".yaml", InputKind::YAML) + .Default(InputKind::Unknown); + + if (_kind != InputKind::Unknown) + return _kind; + + auto buf = getBuffer(); + if (!buf) + return error_code(buf); + + llvm::sys::fs::file_magic magic = + llvm::sys::fs::identify_magic(buf->getBuffer()); + + switch (magic) { + case llvm::sys::fs::file_magic::elf_relocatable: + _kind = InputKind::Object; + break; + } + } + + return _kind; + } + + StringRef getPath() const { + return _file; + } + +private: + mutable std::unique_ptr _buffer; + std::string _file; + mutable InputKind _kind; +}; + +struct LinkerOptions { + LinkerOptions() {} + LinkerOptions(LinkerOptions &&other) + : _input(std::move(other._input)) + , _target(std::move(other._target)) + , _outputPath(std::move(other._outputPath)) + , _entrySymbol(std::move(other._entrySymbol)) + , _relocatable(other._relocatable) {} + + std::vector _input; + std::string _target; + std::string _outputPath; + std::string _entrySymbol; + unsigned _relocatable : 1; + +private: + LinkerOptions(const LinkerOptions&) LLVM_DELETED_FUNCTION; +}; +} + +#endif diff --git a/lld/include/lld/Driver/Target.h b/lld/include/lld/Driver/Target.h new file mode 100644 index 0000000..5b06aee --- /dev/null +++ b/lld/include/lld/Driver/Target.h @@ -0,0 +1,46 @@ +//===- lld/Driver/Target.h - Linker Target Abstraction --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Interface and factory for creating a specific Target. A Target is used to +/// encapsulate all of the target specific configurations for the linker. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_DRIVER_TARGET_H +#define LLD_DRIVER_TARGET_H + +#include "lld/ReaderWriter/Reader.h" +#include "lld/ReaderWriter/Writer.h" +#include "lld/Driver/LinkerOptions.h" + +namespace lld { +/// \brief Represents a specific target. +class Target { +public: + Target(const LinkerOptions &lo) : _options(lo) {} + virtual ~Target(); + + /// \brief Get a reference to a Reader for the given input. + /// + /// Will always return the same object for the same input. + virtual ErrorOr getReader(const LinkerInput &input) = 0; + + /// \brief Get the writer. + virtual ErrorOr getWriter() = 0; + + static std::unique_ptr create(const LinkerOptions&); + +protected: + const LinkerOptions &_options; +}; +} + +#endif diff --git a/lld/lib/CMakeLists.txt b/lld/lib/CMakeLists.txt index 6a0bf98..3a714e7 100644 --- a/lld/lib/CMakeLists.txt +++ b/lld/lib/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(Core) +add_subdirectory(Driver) add_subdirectory(Passes) add_subdirectory(ReaderWriter) diff --git a/lld/lib/Driver/CMakeLists.txt b/lld/lib/Driver/CMakeLists.txt new file mode 100644 index 0000000..83b3953 --- /dev/null +++ b/lld/lib/Driver/CMakeLists.txt @@ -0,0 +1,29 @@ +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) +add_public_tablegen_target(DriverOptionsTableGen) + +add_lld_library(lldDriver + Driver.cpp + Drivers.cpp + LinkerInvocation.cpp + Target.cpp + Targets.cpp + ) + +add_dependencies(lldDriver DriverOptionsTableGen) + +target_link_libraries(lldDriver + lldPasses + lldMachO + lldPECOFF + lldELF + lldNative + lldReaderWriter + lldYAML + lldCore + LLVMObject + LLVMOption + LLVMSupport + ) diff --git a/lld/lib/Driver/CoreOptions.td b/lld/lib/Driver/CoreOptions.td new file mode 100644 index 0000000..b1c8acd --- /dev/null +++ b/lld/lib/Driver/CoreOptions.td @@ -0,0 +1,7 @@ +include "llvm/Option/OptParser.td" + +def target : Separate<["-"], "target">, HelpText<"Target triple to link for">; + +def output : Joined<["-"], "output=">; +def entry : Joined<["-"], "entry=">; +def relocatable : Flag<["-"], "relocatable">; diff --git a/lld/lib/Driver/Driver.cpp b/lld/lib/Driver/Driver.cpp new file mode 100644 index 0000000..5f7f4e6 --- /dev/null +++ b/lld/lib/Driver/Driver.cpp @@ -0,0 +1,14 @@ +//===- 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/Driver/Driver.h" + +using namespace lld; + +Driver::~Driver() {} diff --git a/lld/lib/Driver/Drivers.cpp b/lld/lib/Driver/Drivers.cpp new file mode 100644 index 0000000..b664765 --- /dev/null +++ b/lld/lib/Driver/Drivers.cpp @@ -0,0 +1,180 @@ +//===- 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/Driver/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/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 final : public Driver { +public: + LDDriver(StringRef defaultTargetTriple) : Driver(defaultTargetTriple) {} + + virtual std::unique_ptr + transform(llvm::ArrayRef 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(); + } + + std::unique_ptr newArgs( + new llvm::opt::DerivedArgList(*_inputArgs)); + + if (llvm::opt::Arg *A = _inputArgs->getLastArg(ld::OPT_target)) { + llvm::errs() << A->getValue() << "\n"; + newArgs->AddSeparateArg( A, _core.getOption(core::OPT_target) + , A->getValue()); + llvm::errs() << A->getValue() << "\n"; + } 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()); + else + newArgs->AddJoinedArg(nullptr, _core.getOption(core::OPT_entry), "start"); + + 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_relocatable)) + newArgs->AddFlagArg(A, _core.getOption(core::OPT_relocatable)); + + // Copy input args. + for (llvm::opt::arg_iterator it = _inputArgs->filtered_begin(ld::OPT_INPUT), + ie = _inputArgs->filtered_end(); + it != ie; ++it) { + newArgs->AddPositionalArg(*it, _core.getOption(core::OPT_INPUT), + (*it)->getValue()); + } + + return std::move(newArgs); + } + +private: + std::unique_ptr _inputArgs; + core::CoreOptTable _core; + ld::LDOptTable _opt; +}; + +std::unique_ptr Driver::create( Driver::Flavor flavor + , StringRef defaultTargetTriple) { + switch (flavor) { + case Flavor::ld: + return std::unique_ptr(new LDDriver(defaultTargetTriple)); + case Flavor::core: + case Flavor::ld64: + case Flavor::link: + case Flavor::invalid: + llvm_unreachable("Unsupported flavor"); + } +} + +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(), InputKind::Object)); + } + + ret._target = llvm::Triple::normalize(args.getLastArgValue(core::OPT_target)); + ret._outputPath = args.getLastArgValue(core::OPT_output); + ret._entrySymbol = args.getLastArgValue(core::OPT_entry); + ret._relocatable = args.hasArg(core::OPT_relocatable); + + return std::move(ret); +} diff --git a/lld/lib/Driver/LDOptions.td b/lld/lib/Driver/LDOptions.td new file mode 100644 index 0000000..87a5026 --- /dev/null +++ b/lld/lib/Driver/LDOptions.td @@ -0,0 +1,13 @@ +include "llvm/Option/OptParser.td" + +def flavor : Separate<["-"], "flavor">; +def target : Separate<["-"], "target">, HelpText<"Target triple to link for">; + +def entry : Joined<["--"], "entry=">; +def entry_e : Separate<["-"], "e">, Alias; + +def output : Joined<["--"], "output=">; +def output_e : Separate<["-"], "o">, Alias; + +def relocatable : Flag<["--"], "relocatable">; +def relocatable_r : Flag<["-"], "r">, Alias; diff --git a/lld/lib/Driver/LinkerInvocation.cpp b/lld/lib/Driver/LinkerInvocation.cpp new file mode 100644 index 0000000..0858165 --- /dev/null +++ b/lld/lib/Driver/LinkerInvocation.cpp @@ -0,0 +1,70 @@ +//===- 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/Resolver.h" +#include "lld/Driver/Target.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace lld; + +void LinkerInvocation::operator()() { + // Create target. + std::unique_ptr target(Target::create(_options)); + + if (!target) { + llvm::errs() << "Failed to create target for " << _options._target + << "\n"; + return; + } + + // Read inputs + InputFiles inputs; + for (const auto &input : _options._input) { + auto reader = target->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> files; + if (llvm::error_code ec = reader->readFile( + buffer->getBufferIdentifier(), files)) { + llvm::errs() << "Failed to read file: " << input.getPath() << ": " + << ec.message() << "\n"; + return; + } + inputs.appendFiles(files); + } + + ResolverOptions ro; + Resolver resolver(ro, inputs); + resolver.resolve(); + File &merged = resolver.resultFile(); + + auto writer = target->getWriter(); + 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/Target.cpp b/lld/lib/Driver/Target.cpp new file mode 100644 index 0000000..8b90ebf --- /dev/null +++ b/lld/lib/Driver/Target.cpp @@ -0,0 +1,14 @@ +//===- lib/Driver/Target.cpp - Linker Target Abstraction ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Target.h" + +using namespace lld; + +Target::~Target() {} diff --git a/lld/lib/Driver/Targets.cpp b/lld/lib/Driver/Targets.cpp new file mode 100644 index 0000000..de4ca31 --- /dev/null +++ b/lld/lib/Driver/Targets.cpp @@ -0,0 +1,75 @@ +//===- lib/Driver/Targets.cpp - Linker Targets ----------------------------===// +// +// 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 Target interface. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Target.h" + +#include "lld/ReaderWriter/ReaderArchive.h" +#include "lld/ReaderWriter/ReaderELF.h" +#include "lld/ReaderWriter/ReaderYAML.h" +#include "lld/ReaderWriter/WriterELF.h" + +#include "llvm/ADT/Triple.h" + +using namespace lld; + +class X86LinuxTarget final : public Target { +public: + X86LinuxTarget(const LinkerOptions &lo) : Target(lo) { + _readerELF.reset(createReaderELF(_roe, _roa)); + _readerYAML.reset(createReaderYAML(_roy)); + _writer.reset(createWriterELF(_woe)); + } + + virtual ErrorOr getReader(const LinkerInput &input) { + auto kind = input.getKind(); + if (!kind) + return error_code(kind); + + if (*kind == InputKind::YAML) + return *_readerYAML; + + if (*kind == InputKind::Object) + return *_readerELF; + + return llvm::make_error_code(llvm::errc::invalid_argument); + } + + virtual ErrorOr getWriter() { + return *_writer; + } + +private: + lld::ReaderOptionsELF _roe; + lld::ReaderOptionsArchive _roa; + struct : lld::ReaderOptionsYAML { + virtual Reference::Kind kindFromString(StringRef kindName) const { + int k; + if (kindName.getAsInteger(0, k)) + k = 0; + return k; + } + } _roy; + lld::WriterOptionsELF _woe; + + std::unique_ptr _readerELF, _readerYAML; + std::unique_ptr _writer; +}; + +std::unique_ptr Target::create(const LinkerOptions &lo) { + llvm::Triple t(lo._target); + if (t.getOS() == llvm::Triple::Linux && t.getArch() == llvm::Triple::x86_64) + return std::unique_ptr(new X86LinuxTarget(lo)); + return std::unique_ptr(); +} diff --git a/lld/tools/lld/CMakeLists.txt b/lld/tools/lld/CMakeLists.txt index ee0b47e..d6a2b1c 100644 --- a/lld/tools/lld/CMakeLists.txt +++ b/lld/tools/lld/CMakeLists.txt @@ -2,5 +2,9 @@ add_lld_executable(lld lld.cpp ) +target_link_libraries(lld + lldDriver + ) + install(TARGETS lld RUNTIME DESTINATION bin) diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp index 74329b6..c4670b7 100644 --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld.cpp @@ -1,4 +1,4 @@ -//===- tools/lld/lld.cpp - Linker Driver Dispatcher ---------------------===// +//===- tools/lld/lld.cpp - Linker Driver Dispatcher -----------------------===// // // The LLVM Linker // @@ -6,12 +6,137 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This is the entry point to the lld driver. This is a thin wrapper which -// dispatches to the given platform specific driver. -// +/// +/// \file +/// +/// This is the entry point to the lld driver. This is a thin wrapper which +/// dispatches to the given platform specific driver. +/// //===----------------------------------------------------------------------===// +#include "lld/Core/LLVM.h" +#include "lld/Driver/Driver.h" +#include "lld/Driver/LinkerInvocation.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Signals.h" + +using namespace lld; + +Driver::Flavor strToFlavor(StringRef str) { + return llvm::StringSwitch(str) + .Case("ld", Driver::Flavor::ld) + .Case("link", Driver::Flavor::link) + .Case("ld64", Driver::Flavor::ld64) + .Case("core", Driver::Flavor::core) + .Default(Driver::Flavor::invalid); +} + +struct ProgramNameParts { + StringRef _target; + StringRef _flavor; +}; + +ProgramNameParts parseProgramName(StringRef programName) { + SmallVector 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) != Driver::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; +} + +/// \brief Pick the flavor of driver to use based on the command line and +/// host environment. +Driver::Flavor selectFlavor(int argc, const char * const * const argv) { + if (argc >= 2 && StringRef(argv[1]) == "-core") + return Driver::Flavor::core; + if (argc >= 3 && StringRef(argv[1]) == "-flavor") { + Driver::Flavor flavor = strToFlavor(argv[2]); + if (flavor == Driver::Flavor::invalid) + llvm::errs() << "error: '" << argv[2] << "' invalid value for -flavor.\n"; + return flavor; + } + + Driver::Flavor flavor = strToFlavor( + parseProgramName(llvm::sys::path::stem(argv[0]))._flavor); + + if (flavor == Driver::Flavor::invalid) + llvm::errs() << "error: failed to determine driver flavor from program name" + " '" << argv[0] << "'.\n"; + return flavor; +} + +/// \brief Get the default target triple based on either the program name or +/// the primary target llvm was configured for. +std::string getDefaultTarget(int argc, const char *const *const argv) { + std::string ret = parseProgramName(llvm::sys::path::stem(argv[0]))._target; + if (ret.empty()) + ret = llvm::sys::getDefaultTargetTriple(); + return ret; +} + int main(int argc, char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + llvm::llvm_shutdown_obj Y; + + Driver::Flavor iHazAFlavor = selectFlavor(argc, argv); + if (iHazAFlavor == Driver::Flavor::invalid) + return 1; + + std::unique_ptr coreArgs; + std::unique_ptr driver; + if (iHazAFlavor != Driver::Flavor::core) { + // Transform to core arguments. + driver = Driver::create(iHazAFlavor, getDefaultTarget(argc, argv)); + coreArgs = driver->transform( + llvm::ArrayRef(argv + 1, argv + argc)); + } + + if (!coreArgs) + return 1; + + for (auto arg : *coreArgs) { + llvm::outs() << arg->getAsString(*coreArgs) << " "; + } + llvm::outs() << "\n"; + + LinkerOptions lo(generateOptions(*coreArgs)); + LinkerInvocation invocation(lo); + invocation(); + return 0; } -- 2.7.4