You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1073 lines
34 KiB
1073 lines
34 KiB
//===- Main.cpp -----------------------------------------------------------===//
|
|
//
|
|
// The MCLinker Project
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include <mcld/Environment.h>
|
|
#include <mcld/IRBuilder.h>
|
|
#include <mcld/Linker.h>
|
|
#include <mcld/LinkerConfig.h>
|
|
#include <mcld/LinkerScript.h>
|
|
#include <mcld/Module.h>
|
|
#include <mcld/ADT/StringEntry.h>
|
|
#include <mcld/MC/InputAction.h>
|
|
#include <mcld/MC/CommandAction.h>
|
|
#include <mcld/MC/FileAction.h>
|
|
#include <mcld/MC/ZOption.h>
|
|
#include <mcld/Support/raw_ostream.h>
|
|
#include <mcld/Support/MsgHandling.h>
|
|
#include <mcld/Support/Path.h>
|
|
#include <mcld/Support/SystemUtils.h>
|
|
#include <mcld/Support/TargetRegistry.h>
|
|
|
|
#include <llvm/ADT/ArrayRef.h>
|
|
#include <llvm/ADT/SmallVector.h>
|
|
#include <llvm/ADT/STLExtras.h>
|
|
#include <llvm/ADT/StringRef.h>
|
|
#include <llvm/ADT/StringSwitch.h>
|
|
#include <llvm/Option/Arg.h>
|
|
#include <llvm/Option/ArgList.h>
|
|
#include <llvm/Option/OptTable.h>
|
|
#include <llvm/Option/Option.h>
|
|
#include <llvm/Support/ManagedStatic.h>
|
|
#include <llvm/Support/Process.h>
|
|
#include <llvm/Support/Signals.h>
|
|
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
|
|
#if defined(HAVE_UNISTD_H)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
|
#include <io.h>
|
|
#ifndef STDIN_FILENO
|
|
#define STDIN_FILENO 0
|
|
#endif
|
|
#ifndef STDOUT_FILENO
|
|
#define STDOUT_FILENO 1
|
|
#endif
|
|
#ifndef STDERR_FILENO
|
|
#define STDERR_FILENO 2
|
|
#endif
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
class Driver {
|
|
private:
|
|
enum Option {
|
|
// This is not an option.
|
|
kOpt_INVALID = 0,
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR) \
|
|
kOpt_ ## ID,
|
|
#include "Options.inc" // NOLINT
|
|
#undef OPTION
|
|
kOpt_LastOption
|
|
};
|
|
|
|
class OptTable : public llvm::opt::OptTable {
|
|
private:
|
|
#define PREFIX(NAME, VALUE) \
|
|
static const char* const NAME[];
|
|
#include "Options.inc" // NOLINT
|
|
#undef PREFIX
|
|
static const llvm::opt::OptTable::Info InfoTable[];
|
|
|
|
public:
|
|
OptTable();
|
|
};
|
|
|
|
private:
|
|
explicit Driver(const char* prog_name)
|
|
: prog_name_(prog_name),
|
|
module_(script_),
|
|
ir_builder_(module_, config_) {
|
|
return;
|
|
}
|
|
|
|
public:
|
|
static std::unique_ptr<Driver> Create(llvm::ArrayRef<const char*> argv);
|
|
|
|
bool Run();
|
|
|
|
private:
|
|
bool TranslateArguments(llvm::opt::InputArgList& args);
|
|
|
|
private:
|
|
const char* prog_name_;
|
|
|
|
mcld::LinkerScript script_;
|
|
|
|
mcld::LinkerConfig config_;
|
|
|
|
mcld::Module module_;
|
|
|
|
mcld::IRBuilder ir_builder_;
|
|
|
|
mcld::Linker linker_;
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(Driver);
|
|
};
|
|
|
|
#define PREFIX(NAME, VALUE) \
|
|
const char* const Driver::OptTable::NAME[] = VALUE;
|
|
#include "Options.inc" // NOLINT
|
|
#undef PREFIX
|
|
|
|
const llvm::opt::OptTable::Info Driver::OptTable::InfoTable[] = {
|
|
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
|
|
HELPTEXT, METAVAR) \
|
|
{ PREFIX, NAME, HELPTEXT, METAVAR, kOpt_ ## ID, \
|
|
llvm::opt::Option::KIND ## Class, PARAM, FLAGS, kOpt_ ## GROUP, \
|
|
kOpt_ ## ALIAS, ALIASARGS },
|
|
#include "Options.inc" // NOLINT
|
|
#undef OPTION
|
|
};
|
|
|
|
Driver::OptTable::OptTable()
|
|
: llvm::opt::OptTable(InfoTable) { }
|
|
|
|
inline bool ShouldColorize() {
|
|
const char* term = getenv("TERM");
|
|
return term && (0 != strcmp(term, "dumb"));
|
|
}
|
|
|
|
/// ParseProgName - Parse program name
|
|
/// This function simplifies cross-compiling by reading triple from the program
|
|
/// name. For example, if the program name is `arm-linux-eabi-ld.mcld', we can
|
|
/// get the triple is arm-linux-eabi by the program name.
|
|
inline std::string ParseProgName(const char* prog_name) {
|
|
static const char* suffixes[] = {"ld", "ld.mcld"};
|
|
|
|
std::string name(mcld::sys::fs::Path(prog_name).stem().native());
|
|
|
|
for (size_t i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) {
|
|
if (name == suffixes[i])
|
|
return std::string();
|
|
}
|
|
|
|
llvm::StringRef prog_name_ref(prog_name);
|
|
llvm::StringRef prefix;
|
|
|
|
for (size_t i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) {
|
|
if (!prog_name_ref.endswith(suffixes[i]))
|
|
continue;
|
|
|
|
llvm::StringRef::size_type last_component =
|
|
prog_name_ref.rfind('-', prog_name_ref.size() - strlen(suffixes[i]));
|
|
if (last_component == llvm::StringRef::npos)
|
|
continue;
|
|
llvm::StringRef prefix = prog_name_ref.slice(0, last_component);
|
|
std::string ignored_error;
|
|
if (!mcld::TargetRegistry::lookupTarget(prefix, ignored_error))
|
|
continue;
|
|
return prefix.str();
|
|
}
|
|
return std::string();
|
|
}
|
|
|
|
inline void ParseEmulation(llvm::Triple& triple, const char* emulation) {
|
|
llvm::Triple emu_triple =
|
|
llvm::StringSwitch<llvm::Triple>(emulation)
|
|
.Case("aarch64linux", llvm::Triple("aarch64", "", "linux", "gnu"))
|
|
.Case("armelf_linux_eabi", llvm::Triple("arm", "", "linux", "gnu"))
|
|
.Case("elf_i386", llvm::Triple("i386", "", "", "gnu"))
|
|
.Case("elf_x86_64", llvm::Triple("x86_64", "", "", "gnu"))
|
|
.Case("elf32_x86_64", llvm::Triple("x86_64", "", "", "gnux32"))
|
|
.Case("elf_i386_fbsd", llvm::Triple("i386", "", "freebsd", "gnu"))
|
|
.Case("elf_x86_64_fbsd", llvm::Triple("x86_64", "", "freebsd", "gnu"))
|
|
.Case("elf32ltsmip", llvm::Triple("mipsel", "", "", "gnu"))
|
|
.Case("elf64ltsmip", llvm::Triple("mips64el", "", "", "gnu"))
|
|
.Default(llvm::Triple());
|
|
|
|
if (emu_triple.getArch() == llvm::Triple::UnknownArch &&
|
|
emu_triple.getOS() == llvm::Triple::UnknownOS &&
|
|
emu_triple.getEnvironment() == llvm::Triple::UnknownEnvironment)
|
|
mcld::error(mcld::diag::err_invalid_emulation) << emulation << "\n";
|
|
|
|
if (emu_triple.getArch() != llvm::Triple::UnknownArch)
|
|
triple.setArch(emu_triple.getArch());
|
|
|
|
if (emu_triple.getOS() != llvm::Triple::UnknownOS)
|
|
triple.setOS(emu_triple.getOS());
|
|
|
|
if (emu_triple.getEnvironment() != llvm::Triple::UnknownEnvironment)
|
|
triple.setEnvironment(emu_triple.getEnvironment());
|
|
}
|
|
|
|
/// Configure the output filename.
|
|
inline bool ConfigureOutputName(llvm::StringRef output_name,
|
|
mcld::Module& module,
|
|
mcld::LinkerConfig& config) {
|
|
std::string output(output_name.str());
|
|
if (output.empty()) {
|
|
if (config.targets().triple().getOS() == llvm::Triple::Win32) {
|
|
output.assign("_out");
|
|
switch (config.codeGenType()) {
|
|
case mcld::LinkerConfig::Object: {
|
|
output += ".obj";
|
|
break;
|
|
}
|
|
case mcld::LinkerConfig::DynObj: {
|
|
output += ".dll";
|
|
break;
|
|
}
|
|
case mcld::LinkerConfig::Exec: {
|
|
output += ".exe";
|
|
break;
|
|
}
|
|
case mcld::LinkerConfig::External:
|
|
break;
|
|
default: {
|
|
return false;
|
|
break;
|
|
}
|
|
} // switch (config.codeGenType())
|
|
} else {
|
|
output.assign("a.out");
|
|
}
|
|
} // if (output.empty())
|
|
|
|
module.setName(output);
|
|
return true;
|
|
}
|
|
|
|
bool InitializeInputs(mcld::IRBuilder& ir_builder,
|
|
std::vector<std::unique_ptr<mcld::InputAction>>& input_actions) {
|
|
for (auto& action : input_actions) {
|
|
assert(action != nullptr);
|
|
action->activate(ir_builder.getInputBuilder());
|
|
}
|
|
|
|
if (ir_builder.getInputBuilder().isInGroup()) {
|
|
mcld::fatal(mcld::diag::fatal_forbid_nest_group);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Driver::TranslateArguments(llvm::opt::InputArgList& args) {
|
|
//===--------------------------------------------------------------------===//
|
|
// Preference
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// --color=mode
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Color)) {
|
|
bool res = llvm::StringSwitch<bool>(arg->getValue())
|
|
.Case("never", false)
|
|
.Case("always", true)
|
|
.Case("auto", ShouldColorize() &&
|
|
llvm::sys::Process::FileDescriptorIsDisplayed(
|
|
STDOUT_FILENO))
|
|
.Default(false);
|
|
config_.options().setColor(res);
|
|
mcld::outs().setColor(res);
|
|
mcld::errs().setColor(res);
|
|
}
|
|
|
|
// --trace
|
|
config_.options().setTrace(args.hasArg(kOpt_Trace));
|
|
|
|
// --verbose=level
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Verbose)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
int level;
|
|
if (value.getAsInteger(0, level)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue();
|
|
return false;
|
|
}
|
|
config_.options().setVerbose(level);
|
|
}
|
|
|
|
// --error-limit NUMBER
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ErrorLimit)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
int num;
|
|
if (value.getAsInteger(0, num) || (num < 0)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue();
|
|
return false;
|
|
}
|
|
config_.options().setMaxErrorNum(num);
|
|
}
|
|
|
|
// --warning-limit NUMBER
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_WarningLimit)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
int num;
|
|
if (value.getAsInteger(0, num) || (num < 0)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue();
|
|
return false;
|
|
}
|
|
config_.options().setMaxWarnNum(num);
|
|
}
|
|
|
|
// --warn-shared-textrel
|
|
config_.options().setWarnSharedTextrel(args.hasArg(kOpt_WarnSharedTextrel));
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Target
|
|
//===--------------------------------------------------------------------===//
|
|
llvm::Triple triple;
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Triple)) {
|
|
// 1. Use the triple from command.
|
|
// -mtriple=value
|
|
triple.setTriple(arg->getValue());
|
|
} else {
|
|
std::string prog_triple = ParseProgName(prog_name_);
|
|
if (!prog_triple.empty()) {
|
|
// 2. Use the triple from the program name prefix.
|
|
triple.setTriple(prog_triple);
|
|
} else {
|
|
// 3. Use the default target triple.
|
|
triple.setTriple(mcld::sys::getDefaultTargetTriple());
|
|
}
|
|
}
|
|
|
|
// If a specific emulation was requested, apply it now.
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Emulation)) {
|
|
// -m emulation
|
|
ParseEmulation(triple, arg->getValue());
|
|
} else if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Arch)) {
|
|
// -march=value
|
|
config_.targets().setArch(arg->getValue());
|
|
}
|
|
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_CPU)) {
|
|
config_.targets().setTargetCPU(arg->getValue());
|
|
}
|
|
|
|
config_.targets().setTriple(triple);
|
|
|
|
// --gpsize=value
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_GPSize)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
int size;
|
|
if (value.getAsInteger(0, size) || (size< 0)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
config_.targets().setGPSize(size);
|
|
}
|
|
|
|
// --stub-group-size=value
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_StubGroupSize)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
int size;
|
|
if (value.getAsInteger(0, size) || (size< 0)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
config_.targets().setStubGroupSize(size);
|
|
}
|
|
|
|
// --fix-cortex-a53-835769
|
|
config_.targets().setFixCA53Erratum835769(
|
|
args.hasArg(kOpt_FixCA53Erratum835769));
|
|
|
|
// --fix-cortex-a53-843419
|
|
config_.targets().setFixCA53Erratum843419(
|
|
args.hasArg(kOpt_FixCA53Erratum843419));
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Dynamic
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// --entry=entry
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Entry)) {
|
|
script_.setEntry(arg->getValue());
|
|
}
|
|
|
|
// -Bsymbolic
|
|
config_.options().setBsymbolic(args.hasArg(kOpt_Bsymbolic));
|
|
|
|
// -Bgroup
|
|
config_.options().setBgroup(args.hasArg(kOpt_Bgroup));
|
|
|
|
// -soname=name
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_SOName)) {
|
|
config_.options().setSOName(arg->getValue());
|
|
}
|
|
|
|
// --no-undefined
|
|
if (args.hasArg(kOpt_NoUndef)) {
|
|
config_.options().setNoUndefined(true);
|
|
}
|
|
|
|
// --allow-multiple-definition
|
|
if (args.hasArg(kOpt_AllowMulDefs)) {
|
|
config_.options().setMulDefs(true);
|
|
}
|
|
|
|
// -z options
|
|
for (llvm::opt::Arg* arg : args.filtered(kOpt_Z)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
mcld::ZOption z_opt =
|
|
llvm::StringSwitch<mcld::ZOption>(value)
|
|
.Case("combreloc", mcld::ZOption(mcld::ZOption::CombReloc))
|
|
.Case("nocombreloc", mcld::ZOption(mcld::ZOption::NoCombReloc))
|
|
.Case("defs", mcld::ZOption(mcld::ZOption::Defs))
|
|
.Case("execstack", mcld::ZOption(mcld::ZOption::ExecStack))
|
|
.Case("noexecstack", mcld::ZOption(mcld::ZOption::NoExecStack))
|
|
.Case("initfirst", mcld::ZOption(mcld::ZOption::InitFirst))
|
|
.Case("interpose", mcld::ZOption(mcld::ZOption::InterPose))
|
|
.Case("loadfltr", mcld::ZOption(mcld::ZOption::LoadFltr))
|
|
.Case("muldefs", mcld::ZOption(mcld::ZOption::MulDefs))
|
|
.Case("nocopyreloc", mcld::ZOption(mcld::ZOption::NoCopyReloc))
|
|
.Case("nodefaultlib", mcld::ZOption(mcld::ZOption::NoDefaultLib))
|
|
.Case("nodelete", mcld::ZOption(mcld::ZOption::NoDelete))
|
|
.Case("nodlopen", mcld::ZOption(mcld::ZOption::NoDLOpen))
|
|
.Case("nodump", mcld::ZOption(mcld::ZOption::NoDump))
|
|
.Case("relro", mcld::ZOption(mcld::ZOption::Relro))
|
|
.Case("norelro", mcld::ZOption(mcld::ZOption::NoRelro))
|
|
.Case("lazy", mcld::ZOption(mcld::ZOption::Lazy))
|
|
.Case("now", mcld::ZOption(mcld::ZOption::Now))
|
|
.Case("origin", mcld::ZOption(mcld::ZOption::Origin))
|
|
.Default(mcld::ZOption());
|
|
|
|
if (z_opt.kind() == mcld::ZOption::Unknown) {
|
|
if (value.startswith("common-page-size=")) {
|
|
// -z common-page-size=value
|
|
z_opt.setKind(mcld::ZOption::CommPageSize);
|
|
long long unsigned size = 0;
|
|
value.drop_front(17).getAsInteger(0, size);
|
|
z_opt.setPageSize(static_cast<uint64_t>(size));
|
|
} else if (value.startswith("max-page-size=")) {
|
|
// -z max-page-size=value
|
|
z_opt.setKind(mcld::ZOption::MaxPageSize);
|
|
long long unsigned size = 0;
|
|
value.drop_front(14).getAsInteger(0, size);
|
|
z_opt.setPageSize(static_cast<uint64_t>(size));
|
|
}
|
|
}
|
|
config_.options().addZOption(z_opt);
|
|
}
|
|
|
|
// --dynamic-linker=file
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Dyld)) {
|
|
config_.options().setDyld(arg->getValue());
|
|
}
|
|
|
|
// --enable-new-dtags
|
|
config_.options().setNewDTags(args.hasArg(kOpt_EnableNewDTags));
|
|
|
|
// --spare-dyanmic-tags COUNT
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_SpareDTags)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
int num;
|
|
if (value.getAsInteger(0, num) || (num < 0)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
config_.options().setNumSpareDTags(num);
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Output
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Setup the codegen type.
|
|
if (args.hasArg(kOpt_Shared) || args.hasArg(kOpt_PIE)) {
|
|
// -shared, -pie
|
|
config_.setCodeGenType(mcld::LinkerConfig::DynObj);
|
|
} else if (args.hasArg(kOpt_Relocatable)) {
|
|
// -r
|
|
config_.setCodeGenType(mcld::LinkerConfig::Object);
|
|
} else if (llvm::opt::Arg* arg = args.getLastArg(kOpt_OutputFormat)) {
|
|
// --oformat=value
|
|
llvm::StringRef value = arg->getValue();
|
|
if (value.equals("binary")) {
|
|
config_.setCodeGenType(mcld::LinkerConfig::Binary);
|
|
}
|
|
} else {
|
|
config_.setCodeGenType(mcld::LinkerConfig::Exec);
|
|
}
|
|
|
|
// Setup the output filename.
|
|
llvm::StringRef output_name;
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Output)) {
|
|
output_name = arg->getValue();
|
|
}
|
|
if (!ConfigureOutputName(output_name, module_, config_)) {
|
|
mcld::unreachable(mcld::diag::unrecognized_output_file) << module_.name();
|
|
return false;
|
|
} else {
|
|
if (!args.hasArg(kOpt_SOName)) {
|
|
config_.options().setSOName(module_.name());
|
|
}
|
|
}
|
|
|
|
// --format=value
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_InputFormat)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
if (value.equals("binary")) {
|
|
config_.options().setBinaryInput();
|
|
}
|
|
}
|
|
|
|
// Setup debug info stripping.
|
|
config_.options().setStripDebug(args.hasArg(kOpt_StripDebug) ||
|
|
args.hasArg(kOpt_StripAll));
|
|
|
|
// Setup symbol stripping mode.
|
|
if (args.hasArg(kOpt_StripAll)) {
|
|
config_.options().setStripSymbols(
|
|
mcld::GeneralOptions::StripSymbolMode::StripAllSymbols);
|
|
} else if (args.hasArg(kOpt_DiscardAll)) {
|
|
config_.options().setStripSymbols(
|
|
mcld::GeneralOptions::StripSymbolMode::StripLocals);
|
|
} else if (args.hasArg(kOpt_DiscardLocals)) {
|
|
config_.options().setStripSymbols(
|
|
mcld::GeneralOptions::StripSymbolMode::StripTemporaries);
|
|
} else {
|
|
config_.options().setStripSymbols(
|
|
mcld::GeneralOptions::StripSymbolMode::KeepAllSymbols);
|
|
}
|
|
|
|
// --eh-frame-hdr
|
|
config_.options().setEhFrameHdr(args.hasArg(kOpt_EHFrameHdr));
|
|
|
|
// -pie
|
|
config_.options().setPIE(args.hasArg(kOpt_PIE));
|
|
|
|
// --nmagic
|
|
config_.options().setNMagic(args.hasArg(kOpt_NMagic));
|
|
|
|
// --omagic
|
|
config_.options().setOMagic(args.hasArg(kOpt_OMagic));
|
|
|
|
// --hash-style=style
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_HashStyle)) {
|
|
mcld::GeneralOptions::HashStyle style =
|
|
llvm::StringSwitch<mcld::GeneralOptions::HashStyle>(arg->getValue())
|
|
.Case("sysv", mcld::GeneralOptions::HashStyle::SystemV)
|
|
.Case("gnu", mcld::GeneralOptions::HashStyle::GNU)
|
|
.Case("both", mcld::GeneralOptions::HashStyle::Both)
|
|
.Default(mcld::GeneralOptions::HashStyle::Unknown);
|
|
if (style != mcld::GeneralOptions::HashStyle::Unknown) {
|
|
config_.options().setHashStyle(style);
|
|
}
|
|
}
|
|
|
|
// --[no]-export-dynamic
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ExportDynamic,
|
|
kOpt_NoExportDynamic)) {
|
|
if (arg->getOption().matches(kOpt_ExportDynamic)) {
|
|
config_.options().setExportDynamic(true);
|
|
} else {
|
|
config_.options().setExportDynamic(false);
|
|
}
|
|
}
|
|
|
|
// --no-warn-mismatch
|
|
config_.options().setWarnMismatch(!args.hasArg(kOpt_NoWarnMismatch));
|
|
|
|
// --exclude-libs
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ExcludeLibs)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
do {
|
|
std::pair<llvm::StringRef, llvm::StringRef> res = value.split(',');
|
|
config_.options().excludeLIBS().insert(res.first.str());
|
|
value = res.second;
|
|
} while (!value.empty());
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Search Path
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// --sysroot
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Sysroot)) {
|
|
mcld::sys::fs::Path path(arg->getValue());
|
|
if (mcld::sys::fs::exists(path) && mcld::sys::fs::is_directory(path)) {
|
|
script_.setSysroot(path);
|
|
}
|
|
}
|
|
|
|
// -L searchdir
|
|
for (llvm::opt::Arg* arg : args.filtered(kOpt_LibraryPath)) {
|
|
if (!script_.directories().insert(arg->getValue()))
|
|
mcld::warning(mcld::diag::warn_cannot_open_search_dir) << arg->getValue();
|
|
}
|
|
|
|
// -nostdlib
|
|
config_.options().setNoStdlib(args.hasArg(kOpt_NoStdlib));
|
|
|
|
// -rpath=path
|
|
for (llvm::opt::Arg* arg : args.filtered(kOpt_RPath)) {
|
|
config_.options().getRpathList().push_back(arg->getValue());
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Symbol
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// -d/-dc/-dp
|
|
config_.options().setDefineCommon(args.hasArg(kOpt_DefineCommon));
|
|
|
|
// -u symbol
|
|
for (llvm::opt::Arg* arg : args.filtered(kOpt_Undefined)) {
|
|
config_.options().getUndefSymList().push_back(arg->getValue());
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Script
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// --wrap=symbol
|
|
for (llvm::opt::Arg* arg : args.filtered(kOpt_Wrap)) {
|
|
bool exist = false;
|
|
const char* symbol = arg->getValue();
|
|
// symbol -> __wrap_symbol
|
|
mcld::StringEntry<llvm::StringRef>* to_wrap =
|
|
script_.renameMap().insert(symbol, exist);
|
|
|
|
std::string to_wrap_str;
|
|
to_wrap_str.append("__wrap_")
|
|
.append(symbol);
|
|
to_wrap->setValue(to_wrap_str);
|
|
|
|
if (exist)
|
|
mcld::warning(mcld::diag::rewrap) << symbol << to_wrap_str;
|
|
|
|
// __real_symbol -> symbol
|
|
std::string from_real_str;
|
|
to_wrap_str.append("__real_")
|
|
.append(symbol);
|
|
mcld::StringEntry<llvm::StringRef>* from_real =
|
|
script_.renameMap().insert(from_real_str, exist);
|
|
from_real->setValue(symbol);
|
|
|
|
if (exist)
|
|
mcld::warning(mcld::diag::rewrap) << symbol << from_real_str;
|
|
}
|
|
|
|
// --portalbe=symbol
|
|
for (llvm::opt::Arg* arg : args.filtered(kOpt_Portable)) {
|
|
bool exist = false;
|
|
const char* symbol = arg->getValue();
|
|
// symbol -> symbol_portable
|
|
mcld::StringEntry<llvm::StringRef>* to_wrap =
|
|
script_.renameMap().insert(symbol, exist);
|
|
|
|
std::string to_wrap_str;
|
|
to_wrap_str.append(symbol)
|
|
.append("_portable");
|
|
to_wrap->setValue(to_wrap_str);
|
|
|
|
if (exist)
|
|
mcld::warning(mcld::diag::rewrap) << symbol << to_wrap_str;
|
|
|
|
// __real_symbol -> symbol
|
|
std::string from_real_str;
|
|
to_wrap_str.append("__real_")
|
|
.append(symbol);
|
|
mcld::StringEntry<llvm::StringRef>* from_real =
|
|
script_.renameMap().insert(from_real_str, exist);
|
|
from_real->setValue(symbol);
|
|
|
|
if (exist)
|
|
mcld::warning(mcld::diag::rewrap) << symbol << from_real_str;
|
|
}
|
|
|
|
// --section-start=section=addr
|
|
for (llvm::opt::Arg* arg : args.filtered(kOpt_SectionStart)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
const size_t pos = value.find('=');
|
|
uint64_t addr = 0;
|
|
value.substr(pos + 1).getAsInteger(0, addr);
|
|
bool exist = false;
|
|
mcld::StringEntry<uint64_t>* mapping =
|
|
script_.addressMap().insert(value.substr(0, pos), exist);
|
|
mapping->setValue(addr);
|
|
}
|
|
|
|
// -Tbss=value
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Tbss)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
uint64_t addr = 0;
|
|
if (value.getAsInteger(0, addr)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
bool exist = false;
|
|
mcld::StringEntry<uint64_t>* mapping =
|
|
script_.addressMap().insert(".bss", exist);
|
|
mapping->setValue(addr);
|
|
}
|
|
|
|
// -Tdata=value
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Tdata)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
uint64_t addr = 0;
|
|
if (value.getAsInteger(0, addr)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
bool exist = false;
|
|
mcld::StringEntry<uint64_t>* mapping =
|
|
script_.addressMap().insert(".data", exist);
|
|
mapping->setValue(addr);
|
|
}
|
|
|
|
// -Ttext=value
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Ttext)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
uint64_t addr = 0;
|
|
if (value.getAsInteger(0, addr)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
bool exist = false;
|
|
mcld::StringEntry<uint64_t>* mapping =
|
|
script_.addressMap().insert(".text", exist);
|
|
mapping->setValue(addr);
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Optimization
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// --[no-]gc-sections
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_GCSections,
|
|
kOpt_NoGCSections)) {
|
|
if (arg->getOption().matches(kOpt_GCSections)) {
|
|
config_.options().setGCSections(true);
|
|
} else {
|
|
config_.options().setGCSections(false);
|
|
}
|
|
}
|
|
|
|
// --[no-]print-gc-sections
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_PrintGCSections,
|
|
kOpt_NoPrintGCSections)) {
|
|
if (arg->getOption().matches(kOpt_PrintGCSections)) {
|
|
config_.options().setPrintGCSections(true);
|
|
} else {
|
|
config_.options().setPrintGCSections(false);
|
|
}
|
|
}
|
|
|
|
// --[no-]ld-generated-unwind-info
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_LDGeneratedUnwindInfo,
|
|
kOpt_NoLDGeneratedUnwindInfo)) {
|
|
if (arg->getOption().matches(kOpt_LDGeneratedUnwindInfo)) {
|
|
config_.options().setGenUnwindInfo(true);
|
|
} else {
|
|
config_.options().setGenUnwindInfo(false);
|
|
}
|
|
}
|
|
|
|
// --icf=mode
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ICF)) {
|
|
mcld::GeneralOptions::ICF mode =
|
|
llvm::StringSwitch<mcld::GeneralOptions::ICF>(arg->getValue())
|
|
.Case("none", mcld::GeneralOptions::ICF::None)
|
|
.Case("all", mcld::GeneralOptions::ICF::All)
|
|
.Case("safe", mcld::GeneralOptions::ICF::Safe)
|
|
.Default(mcld::GeneralOptions::ICF::Unknown);
|
|
if (mode == mcld::GeneralOptions::ICF::Unknown) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
config_.options().setICFMode(mode);
|
|
}
|
|
|
|
// --icf-iterations
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ICFIters)) {
|
|
llvm::StringRef value = arg->getValue();
|
|
int num;
|
|
if (value.getAsInteger(0, num) || (num < 0)) {
|
|
mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
|
|
<< ": " << arg->getValue() << "\n";
|
|
return false;
|
|
}
|
|
config_.options().setICFIterations(num);
|
|
}
|
|
|
|
// --[no-]print-icf-sections
|
|
if (llvm::opt::Arg* arg = args.getLastArg(kOpt_PrintICFSections,
|
|
kOpt_NoPrintICFSections)) {
|
|
if (arg->getOption().matches(kOpt_PrintICFSections)) {
|
|
config_.options().setPrintICFSections(true);
|
|
} else {
|
|
config_.options().setPrintICFSections(false);
|
|
}
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Positional
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// # of regular objects, script, and namespec.
|
|
size_t input_num = 0;
|
|
typedef std::unique_ptr<mcld::InputAction> Action;
|
|
|
|
std::vector<Action> actions;
|
|
Action action;
|
|
actions.reserve(32);
|
|
|
|
for (llvm::opt::Arg* arg : args) {
|
|
const unsigned index = arg->getIndex();
|
|
|
|
switch (arg->getOption().getID()) {
|
|
// -T script
|
|
case kOpt_Script: {
|
|
const char* value = arg->getValue();
|
|
config_.options().getScriptList().push_back(value);
|
|
|
|
// FIXME: Let index of script file be 0.
|
|
action.reset(new mcld::ScriptAction(
|
|
0x0, value, mcld::ScriptFile::LDScript, script_.directories()));
|
|
actions.push_back(std::move(action));
|
|
|
|
action.reset(new mcld::ContextAction(0x0));
|
|
actions.push_back(std::move(action));
|
|
|
|
action.reset(new mcld::MemoryAreaAction(0x0,
|
|
mcld::FileHandle::ReadOnly));
|
|
actions.push_back(std::move(action));
|
|
|
|
++input_num;
|
|
break;
|
|
}
|
|
|
|
// --defsym=symbol=expr
|
|
case kOpt_DefSym: {
|
|
std::string expr;
|
|
expr.append(arg->getValue())
|
|
.append(";");
|
|
script_.defsyms().push_back(std::move(expr));
|
|
action.reset(new mcld::DefSymAction(index, script_.defsyms().back()));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// -l namespec
|
|
case kOpt_Namespec: {
|
|
action.reset(new mcld::NamespecAction(
|
|
index, arg->getValue(), script_.directories()));
|
|
actions.push_back(std::move(action));
|
|
|
|
action.reset(new mcld::ContextAction(index));
|
|
actions.push_back(std::move(action));
|
|
|
|
action.reset(new mcld::MemoryAreaAction(index,
|
|
mcld::FileHandle::ReadOnly));
|
|
actions.push_back(std::move(action));
|
|
|
|
++input_num;
|
|
break;
|
|
}
|
|
|
|
// --whole-archive
|
|
case kOpt_WholeArchive: {
|
|
action.reset(new mcld::WholeArchiveAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// --no-whole-archive
|
|
case kOpt_NoWholeArchive: {
|
|
action.reset(new mcld::NoWholeArchiveAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// --as-needed
|
|
case kOpt_AsNeeded: {
|
|
action.reset(new mcld::AsNeededAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// --no-as-needed
|
|
case kOpt_NoAsNeeded: {
|
|
action.reset(new mcld::NoAsNeededAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// --add-needed
|
|
// FIXME: This is deprecated. Should be --copy-dt-needed-entries.
|
|
case kOpt_AddNeeded:
|
|
case kOpt_CopyDTNeeded: {
|
|
action.reset(new mcld::AddNeededAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// --no-add-needed
|
|
// FIXME: This is deprecated. Should be --no-copy-dt-needed-entries.
|
|
case kOpt_NoAddNeeded:
|
|
case kOpt_NoCopyDTNeeded: {
|
|
action.reset(new mcld::AddNeededAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// -Bdynamic
|
|
case kOpt_Bdynamic: {
|
|
action.reset(new mcld::BDynamicAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// -Bstatic
|
|
case kOpt_Bstatic: {
|
|
action.reset(new mcld::BStaticAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// --start-group
|
|
case kOpt_StartGroup: {
|
|
action.reset(new mcld::StartGroupAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
// --end-group
|
|
case kOpt_EndGroup: {
|
|
action.reset(new mcld::EndGroupAction(index));
|
|
actions.push_back(std::move(action));
|
|
break;
|
|
}
|
|
|
|
case kOpt_INPUT: {
|
|
action.reset(new mcld::InputFileAction(index, arg->getValue()));
|
|
actions.push_back(std::move(action));
|
|
|
|
action.reset(new mcld::ContextAction(index));
|
|
actions.push_back(std::move(action));
|
|
|
|
action.reset(new mcld::MemoryAreaAction(index,
|
|
mcld::FileHandle::ReadOnly));
|
|
actions.push_back(std::move(action));
|
|
|
|
++input_num;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (input_num == 0) {
|
|
mcld::fatal(mcld::diag::err_no_inputs);
|
|
return false;
|
|
}
|
|
|
|
// Stable sort
|
|
std::stable_sort(actions.begin(),
|
|
actions.end(),
|
|
[] (const Action& X, const Action& Y) {
|
|
return X->position() < Y->position();
|
|
});
|
|
|
|
if (!InitializeInputs(ir_builder_, actions)) {
|
|
mcld::errs() << "Failed to initialize input tree!\n";
|
|
return false;
|
|
}
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Unknown
|
|
//===--------------------------------------------------------------------===//
|
|
std::vector<std::string> unknown_args = args.getAllArgValues(kOpt_UNKNOWN);
|
|
for (std::string arg : unknown_args)
|
|
mcld::warning(mcld::diag::warn_unsupported_option) << arg;
|
|
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<Driver> Driver::Create(llvm::ArrayRef<const char*> argv) {
|
|
// Parse command line options.
|
|
OptTable opt_table;
|
|
unsigned missing_arg_idx;
|
|
unsigned missing_arg_count;
|
|
llvm::opt::InputArgList args =
|
|
opt_table.ParseArgs(argv.slice(1), missing_arg_idx, missing_arg_count);
|
|
if (missing_arg_count > 0) {
|
|
mcld::errs() << "Argument to '" << args.getArgString(missing_arg_idx)
|
|
<< "' is missing (expected " << missing_arg_count
|
|
<< ((missing_arg_count > 1) ? " values" : " value") << ")\n";
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<Driver> result(new Driver(argv[0]));
|
|
|
|
// Return quickly if -help is specified.
|
|
if (args.hasArg(kOpt_Help)) {
|
|
opt_table.PrintHelp(mcld::outs(), argv[0], "MCLinker",
|
|
/* FlagsToInclude */0, /* FlagsToExclude */0);
|
|
return nullptr;
|
|
}
|
|
|
|
// Print version information if requested.
|
|
if (args.hasArg(kOpt_Version)) {
|
|
mcld::outs() << result->config_.options().getVersionString() << "\n";
|
|
}
|
|
|
|
// Setup instance from arguments.
|
|
if (!result->TranslateArguments(args)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Driver::Run() {
|
|
mcld::Initialize();
|
|
|
|
if (!linker_.emulate(script_, config_)) {
|
|
mcld::errs() << "Failed to emulate target!\n";
|
|
return false;
|
|
}
|
|
|
|
if (!linker_.link(module_, ir_builder_)) {
|
|
mcld::errs() << "Failed to link objects!\n";
|
|
return false;
|
|
}
|
|
|
|
if (!linker_.emit(module_, module_.name())) {
|
|
mcld::errs() << "Failed to emit output!\n";
|
|
return false;
|
|
}
|
|
|
|
mcld::Finalize();
|
|
return true;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
std::unique_ptr<Driver> driver =
|
|
Driver::Create(llvm::makeArrayRef(argv, argc));
|
|
|
|
if ((driver == nullptr) || !driver->Run()) {
|
|
return EXIT_FAILURE;
|
|
} else {
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|