Add the core architecture for the lld driver.
authorMichael J. Spencer <bigcheesegs@gmail.com>
Sat, 8 Dec 2012 00:47:36 +0000 (00:47 +0000)
committerMichael J. Spencer <bigcheesegs@gmail.com>
Sat, 8 Dec 2012 00:47:36 +0000 (00:47 +0000)
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

16 files changed:
lld/include/lld/Core/ErrorOr.h [new file with mode: 0644]
lld/include/lld/Driver/Driver.h [new file with mode: 0644]
lld/include/lld/Driver/LinkerInvocation.h [new file with mode: 0644]
lld/include/lld/Driver/LinkerOptions.h [new file with mode: 0644]
lld/include/lld/Driver/Target.h [new file with mode: 0644]
lld/lib/CMakeLists.txt
lld/lib/Driver/CMakeLists.txt [new file with mode: 0644]
lld/lib/Driver/CoreOptions.td [new file with mode: 0644]
lld/lib/Driver/Driver.cpp [new file with mode: 0644]
lld/lib/Driver/Drivers.cpp [new file with mode: 0644]
lld/lib/Driver/LDOptions.td [new file with mode: 0644]
lld/lib/Driver/LinkerInvocation.cpp [new file with mode: 0644]
lld/lib/Driver/Target.cpp [new file with mode: 0644]
lld/lib/Driver/Targets.cpp [new file with mode: 0644]
lld/tools/lld/CMakeLists.txt
lld/tools/lld/lld.cpp

diff --git a/lld/include/lld/Core/ErrorOr.h b/lld/include/lld/Core/ErrorOr.h
new file mode 100644 (file)
index 0000000..403b4a5
--- /dev/null
@@ -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<T> 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 <cassert>
+#include <type_traits>
+
+namespace lld {
+template<class T>
+class ErrorOrBase {
+  static const bool isRef = std::is_reference<T>::value;
+  typedef std::reference_wrapper<typename std::remove_reference<T>::type> wrap;
+
+public:
+  typedef typename
+    std::conditional< isRef
+                    , wrap
+                    , T
+                    >::type storage_type;
+
+private:
+  typedef T &reference;
+  typedef typename std::remove_reference<T>::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<storage_type*>(_t.buffer);
+  }
+
+  llvm::error_code _error;
+  llvm::AlignedCharArrayUnion<storage_type> _t;
+};
+
+/// \brief Represents either an error or a value T.
+///
+/// ErrorOr<T> 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<Buffer> 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<std::remove_reference<T>::type>, and
+/// there is special handling to make operator -> work as if T was not a
+/// reference.
+///
+/// T cannot be a rvalue reference.
+template<class T,
+  bool isMoveable =
+    std::is_move_constructible<typename ErrorOrBase<T>::storage_type>::value>
+class ErrorOr;
+
+template<class T>
+class ErrorOr<T, true> : public ErrorOrBase<T> {
+  ErrorOr(const ErrorOr &other) LLVM_DELETED_FUNCTION;
+  ErrorOr &operator =(const ErrorOr &other) LLVM_DELETED_FUNCTION;
+public:
+  ErrorOr(llvm::error_code ec) : ErrorOrBase<T>(ec) {}
+  ErrorOr(T t) : ErrorOrBase<T>(t) {}
+  ErrorOr(ErrorOr &&other) : ErrorOrBase<T>() {
+    // Get the other value.
+    if (!other._error)
+      new (this->get())
+        typename ErrorOrBase<T>::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<T>::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 T>
+class ErrorOr<T, false> : public ErrorOrBase<T> {
+  static_assert(std::is_copy_constructible<T>::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<T>(ec) {}
+  ErrorOr(T t) : ErrorOrBase<T>(t) {}
+  ErrorOr(const ErrorOr &other) : ErrorOrBase<T>() {
+    // Get the other value.
+    if (!other._error)
+      new (this->get()) typename ErrorOrBase<T>::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<T>::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 (file)
index 0000000..d2ff7b4
--- /dev/null
@@ -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 <memory>
+#include <string>
+
+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<llvm::opt::DerivedArgList>
+    transform(llvm::ArrayRef<const char *const> 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<Driver> 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 (file)
index 0000000..d543ebb
--- /dev/null
@@ -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 (file)
index 0000000..801c401
--- /dev/null
@@ -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 <memory>
+
+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<llvm::MemoryBuffer&> getBuffer() const {
+    if (!_buffer) {
+      llvm::OwningPtr<llvm::MemoryBuffer> buf;
+      if (error_code ec = llvm::MemoryBuffer::getFileOrSTDIN(_file, buf))
+        return ec;
+      _buffer.reset(buf.take());
+    }
+
+    return *_buffer;
+  }
+
+  ErrorOr<InputKind> getKind() const {
+    if (_kind == InputKind::Unknown) {
+      _kind = llvm::StringSwitch<InputKind>(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<llvm::MemoryBuffer> _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<LinkerInput> _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 (file)
index 0000000..5b06aee
--- /dev/null
@@ -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<lld::Reader&> getReader(const LinkerInput &input) = 0;
+
+  /// \brief Get the writer.
+  virtual ErrorOr<lld::Writer&> getWriter() = 0;
+
+  static std::unique_ptr<Target> create(const LinkerOptions&);
+
+protected:
+  const LinkerOptions &_options;
+};
+}
+
+#endif
index 6a0bf98..3a714e7 100644 (file)
@@ -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 (file)
index 0000000..83b3953
--- /dev/null
@@ -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 (file)
index 0000000..b1c8acd
--- /dev/null
@@ -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 (file)
index 0000000..5f7f4e6
--- /dev/null
@@ -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 (file)
index 0000000..b664765
--- /dev/null
@@ -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<llvm::opt::DerivedArgList>
+  transform(llvm::ArrayRef<const char *const> 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>();
+    }
+
+    std::unique_ptr<llvm::opt::DerivedArgList> 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<llvm::opt::InputArgList> _inputArgs;
+  core::CoreOptTable _core;
+  ld::LDOptTable _opt;
+};
+
+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:
+    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 (file)
index 0000000..87a5026
--- /dev/null
@@ -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<entry>;
+
+def output : Joined<["--"], "output=">;
+def output_e : Separate<["-"], "o">, Alias<output>;
+
+def relocatable : Flag<["--"], "relocatable">;
+def relocatable_r : Flag<["-"], "r">, Alias<relocatable>;
diff --git a/lld/lib/Driver/LinkerInvocation.cpp b/lld/lib/Driver/LinkerInvocation.cpp
new file mode 100644 (file)
index 0000000..0858165
--- /dev/null
@@ -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(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<std::unique_ptr<File>> 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 (file)
index 0000000..8b90ebf
--- /dev/null
@@ -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 (file)
index 0000000..de4ca31
--- /dev/null
@@ -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<lld::Reader&> 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<lld::Writer&> 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<lld::Reader> _readerELF, _readerYAML;
+  std::unique_ptr<lld::Writer> _writer;
+};
+
+std::unique_ptr<Target> 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<Target>(new X86LinuxTarget(lo));
+  return std::unique_ptr<Target>();
+}
index ee0b47e..d6a2b1c 100644 (file)
@@ -2,5 +2,9 @@ add_lld_executable(lld
   lld.cpp
   )
 
+target_link_libraries(lld
+  lldDriver
+  )
+
 install(TARGETS lld
   RUNTIME DESTINATION bin)
index 74329b6..c4670b7 100644 (file)
@@ -1,4 +1,4 @@
-//===- tools/lld/lld.cpp - Linker Driver Dispatcher ---------------------===//
+//===- tools/lld/lld.cpp - Linker Driver Dispatcher -----------------------===//
 //
 //                             The LLVM Linker
 //
 // 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<Driver::Flavor>(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<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) != 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<llvm::opt::ArgList> coreArgs;
+  std::unique_ptr<Driver> driver;
+  if (iHazAFlavor != Driver::Flavor::core) {
+    // Transform to core arguments.
+    driver = Driver::create(iHazAFlavor, getDefaultTarget(argc, argv));
+    coreArgs = driver->transform(
+      llvm::ArrayRef<const char *const>(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;
 }