//===- Main.cpp -----------------------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_UNISTD_H) #include #endif #if defined(_MSC_VER) || defined(__MINGW32__) #include #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 Create(llvm::ArrayRef 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(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>& 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(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(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(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(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(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 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* 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* 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* 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* 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* 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* 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* 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* 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(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 Action; std::vector 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 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::Create(llvm::ArrayRef 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 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::Create(llvm::makeArrayRef(argv, argc)); if ((driver == nullptr) || !driver->Run()) { return EXIT_FAILURE; } else { return EXIT_SUCCESS; } }