mirror of
https://github.com/intel/llvm.git
synced 2026-01-17 06:40:01 +08:00
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
This commit is contained in:
213
lld/include/lld/Core/ErrorOr.h
Normal file
213
lld/include/lld/Core/ErrorOr.h
Normal file
@@ -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
|
||||
64
lld/include/lld/Driver/Driver.h
Normal file
64
lld/include/lld/Driver/Driver.h
Normal file
@@ -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
|
||||
34
lld/include/lld/Driver/LinkerInvocation.h
Normal file
34
lld/include/lld/Driver/LinkerInvocation.h
Normal file
@@ -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
|
||||
126
lld/include/lld/Driver/LinkerOptions.h
Normal file
126
lld/include/lld/Driver/LinkerOptions.h
Normal file
@@ -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
|
||||
46
lld/include/lld/Driver/Target.h
Normal file
46
lld/include/lld/Driver/Target.h
Normal file
@@ -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
|
||||
@@ -1,3 +1,4 @@
|
||||
add_subdirectory(Core)
|
||||
add_subdirectory(Driver)
|
||||
add_subdirectory(Passes)
|
||||
add_subdirectory(ReaderWriter)
|
||||
|
||||
29
lld/lib/Driver/CMakeLists.txt
Normal file
29
lld/lib/Driver/CMakeLists.txt
Normal file
@@ -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
|
||||
)
|
||||
7
lld/lib/Driver/CoreOptions.td
Normal file
7
lld/lib/Driver/CoreOptions.td
Normal file
@@ -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">;
|
||||
14
lld/lib/Driver/Driver.cpp
Normal file
14
lld/lib/Driver/Driver.cpp
Normal file
@@ -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() {}
|
||||
180
lld/lib/Driver/Drivers.cpp
Normal file
180
lld/lib/Driver/Drivers.cpp
Normal file
@@ -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);
|
||||
}
|
||||
13
lld/lib/Driver/LDOptions.td
Normal file
13
lld/lib/Driver/LDOptions.td
Normal file
@@ -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>;
|
||||
70
lld/lib/Driver/LinkerInvocation.cpp
Normal file
70
lld/lib/Driver/LinkerInvocation.cpp
Normal file
@@ -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";
|
||||
}
|
||||
14
lld/lib/Driver/Target.cpp
Normal file
14
lld/lib/Driver/Target.cpp
Normal file
@@ -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() {}
|
||||
75
lld/lib/Driver/Targets.cpp
Normal file
75
lld/lib/Driver/Targets.cpp
Normal file
@@ -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>();
|
||||
}
|
||||
@@ -2,5 +2,9 @@ add_lld_executable(lld
|
||||
lld.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(lld
|
||||
lldDriver
|
||||
)
|
||||
|
||||
install(TARGETS lld
|
||||
RUNTIME DESTINATION bin)
|
||||
|
||||
@@ -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<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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user