//===- TripleOptions.cpp --------------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include <mcld/TripleOptions.h> #include <mcld/LinkerConfig.h> #include <mcld/Support/Path.h> #include <mcld/Support/TargetRegistry.h> #include <mcld/Support/MsgHandling.h> #include <mcld/Support/SystemUtils.h> #include <llvm/ADT/StringSwitch.h> #include <llvm/MC/SubtargetFeature.h> namespace { llvm::cl::opt<std::string> ArgTargetTriple("mtriple", llvm::cl::desc("Override target triple for module")); llvm::cl::opt<std::string> ArgMArch("march", llvm::cl::desc("Architecture to generate code for (see --version)")); llvm::cl::opt<std::string> ArgMCPU("mcpu", llvm::cl::desc("Target a specific cpu type (-mcpu=help for details)"), llvm::cl::value_desc("cpu-name"), llvm::cl::init("")); llvm::cl::list<std::string> ArgMAttrs("mattr", llvm::cl::CommaSeparated, llvm::cl::desc("Target specific attributes (-mattr=help for details)"), llvm::cl::value_desc("a1,+a2,-a3,...")); llvm::cl::opt<std::string> ArgEmulation("m", llvm::cl::ZeroOrMore, llvm::cl::desc("Set GNU linker emulation"), llvm::cl::value_desc("emulation"), llvm::cl::Prefix); /// 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 *pProgName) { static const char *suffixes[] = { "ld", "ld.mcld" }; std::string ProgName(mcld::sys::fs::Path(pProgName).stem().native()); for (size_t i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) { if (ProgName == suffixes[i]) return std::string(); } llvm::StringRef ProgNameRef(ProgName); llvm::StringRef Prefix; for (size_t i = 0; i < sizeof(suffixes) / sizeof(suffixes[0]); ++i) { if (!ProgNameRef.endswith(suffixes[i])) continue; llvm::StringRef::size_type LastComponent = ProgNameRef.rfind('-', ProgNameRef.size() - strlen(suffixes[i])); if (LastComponent == llvm::StringRef::npos) continue; llvm::StringRef Prefix = ProgNameRef.slice(0, LastComponent); std::string IgnoredError; if (!mcld::TargetRegistry::lookupTarget(Prefix, IgnoredError)) continue; return Prefix.str(); } return std::string(); } inline void ParseEmulation(llvm::Triple& pTriple, const std::string& pEmulation) { llvm::Triple triple = llvm::StringSwitch<llvm::Triple>(pEmulation) .Case("aarch64linux", llvm::Triple("aarch64", "", "linux", "gnu")) .Case("armelf_linux_eabi", llvm::Triple("arm", "", "linux", "gnueabi")) .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")) .Default(llvm::Triple()); if (triple.getArch() == llvm::Triple::UnknownArch && triple.getOS() == llvm::Triple::UnknownOS && triple.getEnvironment() == llvm::Triple::UnknownEnvironment) mcld::error(mcld::diag::err_invalid_emulation) << pEmulation << "\n"; if (triple.getArch() != llvm::Triple::UnknownArch) pTriple.setArch(triple.getArch()); if (triple.getOS() != llvm::Triple::UnknownOS) pTriple.setOS(triple.getOS()); if (triple.getEnvironment() != llvm::Triple::UnknownEnvironment) pTriple.setEnvironment(triple.getEnvironment()); } } // anonymous namespace using namespace mcld; //===----------------------------------------------------------------------===// // TripleOptions //===----------------------------------------------------------------------===// TripleOptions::TripleOptions() : m_TargetTriple(ArgTargetTriple), m_MArch(ArgMArch), m_MCPU(ArgMCPU), m_MAttrs(ArgMAttrs), m_Emulation(ArgEmulation) { } bool TripleOptions::parse(int pArgc, char* pArgv[], LinkerConfig& pConfig) { llvm::Triple triple; if (!m_TargetTriple.empty()) { // 1. Use the triple from command. triple.setTriple(m_TargetTriple); } else { std::string prog_triple = ParseProgName(pArgv[0]); 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 (!m_Emulation.empty()) ParseEmulation(triple, m_Emulation); else pConfig.targets().setArch(m_MArch); pConfig.targets().setTriple(triple); pConfig.targets().setTargetCPU(m_MCPU); // Package up features to be passed to target/subtarget std::string feature_str; if (m_MAttrs.size()) { llvm::SubtargetFeatures features; for (unsigned i = 0; i != m_MAttrs.size(); ++i) features.AddFeature(m_MAttrs[i]); feature_str = features.getString(); } pConfig.targets().setTargetFeatureString(feature_str); return true; }