In many situations, we don't want to exit at the first error even in the process model. For example, it is better to report all undefined symbols rather than reporting the first one that the linker picked up randomly. In order to handle such errors, we don't need to wrap everything with ErrorOr (thanks for David Blaikie for pointing this out!) Instead, we can set a flag to record the fact that we found an error and keep it going until it reaches a reasonable checkpoint. This idea should be applicable to other places. For example, we can ignore broken relocations and check for errors after visiting all relocs. In this patch, I rename error to fatal, and introduce another version of error which doesn't call exit. That function instead sets HasError to true. Once HasError becomes true, it stays true, so that we know that there was an error if it is true. I think introducing a non-noreturn error reporting function is by itself a good idea, and it looks to me that this also provides a gradual path towards lld-as-a-library (or at least embed-lld-to-your-program) without sacrificing code readability with lots of ErrorOr's. http://reviews.llvm.org/D16641 llvm-svn: 259069
225 lines
6.8 KiB
C++
225 lines
6.8 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) {}
|
|
};
|
|
|
|
enum class Flavor {
|
|
invalid,
|
|
old_gnu_ld, // -flavor old-gnu
|
|
gnu_ld, // -flavor gnu
|
|
win_link, // -flavor link
|
|
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("old-gnu", Flavor::old_gnu_ld)
|
|
.Case("gnu", Flavor::gnu_ld)
|
|
.Case("ld.lld", Flavor::gnu_ld)
|
|
.Case("link", Flavor::win_link)
|
|
.Case("lld-link", Flavor::win_link)
|
|
.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,
|
|
llvm::MutableArrayRef<const char *> &args) {
|
|
unsigned int numToRemove = arg->getNumValues() + 1;
|
|
auto sub = args.slice(arg->getIndex() + 1);
|
|
std::rotate(sub.begin(), sub.begin() + numToRemove, sub.end());
|
|
args = args.drop_back(numToRemove);
|
|
}
|
|
|
|
static Flavor getFlavor(llvm::MutableArrayRef<const char *> &args,
|
|
const llvm::opt::InputArgList &parsedArgs) {
|
|
if (llvm::opt::Arg *argCore = parsedArgs.getLastArg(OPT_core)) {
|
|
removeArg(argCore, args);
|
|
return Flavor::core;
|
|
}
|
|
if (llvm::opt::Arg *argFlavor = parsedArgs.getLastArg(OPT_flavor)) {
|
|
removeArg(argFlavor, args);
|
|
return strToFlavor(argFlavor->getValue());
|
|
}
|
|
|
|
#if LLVM_ON_UNIX
|
|
if (llvm::sys::path::filename(args[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::filename(args[0]);
|
|
if (name.endswith_lower(".exe"))
|
|
name = llvm::sys::path::stem(name);
|
|
return strToFlavor(parseProgramName(name)._flavor);
|
|
}
|
|
|
|
namespace lld {
|
|
|
|
bool UniversalDriver::link(llvm::MutableArrayRef<const char *> args,
|
|
raw_ostream &diagnostics) {
|
|
// Parse command line options using GnuLdOptions.td
|
|
UniversalDriverOptTable table;
|
|
unsigned missingIndex;
|
|
unsigned missingCount;
|
|
|
|
// Program name
|
|
StringRef programName = llvm::sys::path::stem(args[0]);
|
|
|
|
llvm::opt::InputArgList parsedArgs =
|
|
table.ParseArgs(args.slice(1), 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(args, parsedArgs);
|
|
|
|
// Switch to appropriate driver.
|
|
switch (flavor) {
|
|
case Flavor::old_gnu_ld:
|
|
return GnuLdDriver::linkELF(args, diagnostics);
|
|
case Flavor::gnu_ld:
|
|
return elf2::link(args);
|
|
case Flavor::darwin_ld:
|
|
return DarwinLdDriver::linkMachO(args, diagnostics);
|
|
case Flavor::win_link:
|
|
coff::link(args);
|
|
return true;
|
|
case Flavor::core:
|
|
return CoreDriver::link(args, 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
|