Files
clang-p2996/lld/lib/Driver/UniversalDriver.cpp
Rui Ueyama 411c636081 COFF: Add a new PE/COFF port.
This is an initial patch for a section-based COFF linker.

The patch has 2300 lines of code including comments and blank lines.
Before diving into details, you want to start from reading README
because it should give you an overview of the design.

All important things are written in the README file, so I write
summary here.

- The linker is already able to self-link on Windows.

- It's significantly faster than the existing implementation.
  The existing one takes 5 seconds to link LLD on my machine,
  while the new one only takes 1.2 seconds, even though the new
  one is not multi-threaded yet. (And a proof-of-concept multi-
  threaded version was able to link it in 0.5 seconds.)

- It uses much less memory (250MB vs. 2GB virtual memory space
  to self-host).

- IMHO the new code is much simpler and easier to read than
  the existing PE/COFF port.

http://reviews.llvm.org/D10036

llvm-svn: 238458
2015-05-28 19:09:30 +00:00

224 lines
6.9 KiB
C++

//===- lib/Driver/UniversalDriver.cpp -------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Driver for "universal" lld tool which can mimic any linker command line
/// parsing once it figures out which command line flavor to use.
///
//===----------------------------------------------------------------------===//
#include "lld/Driver/Driver.h"
#include "lld/Config/Version.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace lld;
namespace {
// Create enum with OPT_xxx values for each option in GnuLdOptions.td
enum {
OPT_INVALID = 0,
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELP, META) \
OPT_##ID,
#include "UniversalDriverOptions.inc"
#undef OPTION
};
// Create prefix string literals used in GnuLdOptions.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "UniversalDriverOptions.inc"
#undef PREFIX
// Create table mapping all options defined in GnuLdOptions.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ \
PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS \
} \
,
#include "UniversalDriverOptions.inc"
#undef OPTION
};
// Create OptTable class for parsing actual command line arguments
class UniversalDriverOptTable : public llvm::opt::OptTable {
public:
UniversalDriverOptTable()
: OptTable(infoTable, llvm::array_lengthof(infoTable)) {}
};
enum class Flavor {
invalid,
gnu_ld, // -flavor gnu
win_link, // -flavor link
win_link2, // -flavor link2
darwin_ld, // -flavor darwin
core // -flavor core OR -core
};
struct ProgramNameParts {
StringRef _target;
StringRef _flavor;
};
} // anonymous namespace
static Flavor strToFlavor(StringRef str) {
return llvm::StringSwitch<Flavor>(str)
.Case("gnu", Flavor::gnu_ld)
.Case("link", Flavor::win_link)
.Case("lld-link", Flavor::win_link)
.Case("link2", Flavor::win_link2)
.Case("lld-link2", Flavor::win_link2)
.Case("darwin", Flavor::darwin_ld)
.Case("core", Flavor::core)
.Case("ld", Flavor::gnu_ld)
.Default(Flavor::invalid);
}
static 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) != 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;
}
// Removes the argument from argv along with its value, if exists, and updates
// argc.
static void removeArg(llvm::opt::Arg *arg, int &argc, const char **&argv) {
unsigned int numToRemove = arg->getNumValues() + 1;
unsigned int argIndex = arg->getIndex() + 1;
std::rotate(&argv[argIndex], &argv[argIndex + numToRemove], argv + argc);
argc -= numToRemove;
}
static Flavor getFlavor(int &argc, const char **&argv,
std::unique_ptr<llvm::opt::InputArgList> &parsedArgs) {
if (llvm::opt::Arg *argCore = parsedArgs->getLastArg(OPT_core)) {
removeArg(argCore, argc, argv);
return Flavor::core;
}
if (llvm::opt::Arg *argFlavor = parsedArgs->getLastArg(OPT_flavor)) {
removeArg(argFlavor, argc, argv);
return strToFlavor(argFlavor->getValue());
}
#if LLVM_ON_UNIX
if (llvm::sys::path::filename(argv[0]).equals("ld")) {
#if __APPLE__
// On a Darwin systems, if linker binary is named "ld", use Darwin driver.
return Flavor::darwin_ld;
#endif
// On a ELF based systems, if linker binary is named "ld", use gnu driver.
return Flavor::gnu_ld;
}
#endif
StringRef name = llvm::sys::path::stem(argv[0]);
return strToFlavor(parseProgramName(name)._flavor);
}
namespace lld {
bool UniversalDriver::link(int argc, const char *argv[],
raw_ostream &diagnostics) {
// Parse command line options using GnuLdOptions.td
std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
UniversalDriverOptTable table;
unsigned missingIndex;
unsigned missingCount;
// Program name
StringRef programName = llvm::sys::path::stem(argv[0]);
parsedArgs.reset(
table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
if (missingCount) {
diagnostics << "error: missing arg value for '"
<< parsedArgs->getArgString(missingIndex) << "' expected "
<< missingCount << " argument(s).\n";
return false;
}
// Handle -help
if (parsedArgs->getLastArg(OPT_help)) {
table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false);
return true;
}
// Handle -version
if (parsedArgs->getLastArg(OPT_version)) {
diagnostics << "LLVM Linker Version: " << getLLDVersion()
<< getLLDRepositoryVersion() << "\n";
return true;
}
Flavor flavor = getFlavor(argc, argv, parsedArgs);
std::vector<const char *> args(argv, argv + argc);
// Switch to appropriate driver.
switch (flavor) {
case Flavor::gnu_ld:
return GnuLdDriver::linkELF(args.size(), args.data(), diagnostics);
case Flavor::darwin_ld:
return DarwinLdDriver::linkMachO(args.size(), args.data(), diagnostics);
case Flavor::win_link:
return WinLinkDriver::linkPECOFF(args.size(), args.data(), diagnostics);
case Flavor::win_link2:
return coff::link(args.size(), args.data());
case Flavor::core:
return CoreDriver::link(args.size(), args.data(), diagnostics);
case Flavor::invalid:
diagnostics << "Select the appropriate flavor\n";
table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false);
return false;
}
llvm_unreachable("Unrecognised flavor");
}
} // end namespace lld