Path: blob/main/contrib/llvm-project/clang/lib/Driver/ToolChains/BareMetal.cpp
35268 views
//===-- BareMetal.cpp - Bare Metal ToolChain --------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "BareMetal.h"910#include "CommonArgs.h"11#include "Gnu.h"12#include "clang/Driver/InputInfo.h"1314#include "Arch/ARM.h"15#include "Arch/RISCV.h"16#include "clang/Driver/Compilation.h"17#include "clang/Driver/Driver.h"18#include "clang/Driver/DriverDiagnostic.h"19#include "clang/Driver/MultilibBuilder.h"20#include "clang/Driver/Options.h"21#include "llvm/ADT/StringExtras.h"22#include "llvm/Option/ArgList.h"23#include "llvm/Support/Path.h"24#include "llvm/Support/VirtualFileSystem.h"25#include "llvm/Support/raw_ostream.h"2627#include <sstream>2829using namespace llvm::opt;30using namespace clang;31using namespace clang::driver;32using namespace clang::driver::tools;33using namespace clang::driver::toolchains;3435static bool findRISCVMultilibs(const Driver &D,36const llvm::Triple &TargetTriple,37const ArgList &Args, DetectedMultilibs &Result) {38Multilib::flags_list Flags;39std::string Arch = riscv::getRISCVArch(Args, TargetTriple);40StringRef Abi = tools::riscv::getRISCVABI(Args, TargetTriple);4142if (TargetTriple.isRISCV64()) {43MultilibBuilder Imac =44MultilibBuilder().flag("-march=rv64imac").flag("-mabi=lp64");45MultilibBuilder Imafdc = MultilibBuilder("/rv64imafdc/lp64d")46.flag("-march=rv64imafdc")47.flag("-mabi=lp64d");4849// Multilib reuse50bool UseImafdc =51(Arch == "rv64imafdc") || (Arch == "rv64gc"); // gc => imafdc5253addMultilibFlag((Arch == "rv64imac"), "-march=rv64imac", Flags);54addMultilibFlag(UseImafdc, "-march=rv64imafdc", Flags);55addMultilibFlag(Abi == "lp64", "-mabi=lp64", Flags);56addMultilibFlag(Abi == "lp64d", "-mabi=lp64d", Flags);5758Result.Multilibs =59MultilibSetBuilder().Either(Imac, Imafdc).makeMultilibSet();60return Result.Multilibs.select(Flags, Result.SelectedMultilibs);61}62if (TargetTriple.isRISCV32()) {63MultilibBuilder Imac =64MultilibBuilder().flag("-march=rv32imac").flag("-mabi=ilp32");65MultilibBuilder I = MultilibBuilder("/rv32i/ilp32")66.flag("-march=rv32i")67.flag("-mabi=ilp32");68MultilibBuilder Im = MultilibBuilder("/rv32im/ilp32")69.flag("-march=rv32im")70.flag("-mabi=ilp32");71MultilibBuilder Iac = MultilibBuilder("/rv32iac/ilp32")72.flag("-march=rv32iac")73.flag("-mabi=ilp32");74MultilibBuilder Imafc = MultilibBuilder("/rv32imafc/ilp32f")75.flag("-march=rv32imafc")76.flag("-mabi=ilp32f");7778// Multilib reuse79bool UseI = (Arch == "rv32i") || (Arch == "rv32ic"); // ic => i80bool UseIm = (Arch == "rv32im") || (Arch == "rv32imc"); // imc => im81bool UseImafc = (Arch == "rv32imafc") || (Arch == "rv32imafdc") ||82(Arch == "rv32gc"); // imafdc,gc => imafc8384addMultilibFlag(UseI, "-march=rv32i", Flags);85addMultilibFlag(UseIm, "-march=rv32im", Flags);86addMultilibFlag((Arch == "rv32iac"), "-march=rv32iac", Flags);87addMultilibFlag((Arch == "rv32imac"), "-march=rv32imac", Flags);88addMultilibFlag(UseImafc, "-march=rv32imafc", Flags);89addMultilibFlag(Abi == "ilp32", "-mabi=ilp32", Flags);90addMultilibFlag(Abi == "ilp32f", "-mabi=ilp32f", Flags);9192Result.Multilibs =93MultilibSetBuilder().Either(I, Im, Iac, Imac, Imafc).makeMultilibSet();94return Result.Multilibs.select(Flags, Result.SelectedMultilibs);95}96return false;97}9899BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,100const ArgList &Args)101: ToolChain(D, Triple, Args) {102getProgramPaths().push_back(getDriver().Dir);103104findMultilibs(D, Triple, Args);105SmallString<128> SysRoot(computeSysRoot());106if (!SysRoot.empty()) {107for (const Multilib &M : getOrderedMultilibs()) {108SmallString<128> Dir(SysRoot);109llvm::sys::path::append(Dir, M.osSuffix(), "lib");110getFilePaths().push_back(std::string(Dir));111getLibraryPaths().push_back(std::string(Dir));112}113}114}115116/// Is the triple {arm,armeb,thumb,thumbeb}-none-none-{eabi,eabihf} ?117static bool isARMBareMetal(const llvm::Triple &Triple) {118if (Triple.getArch() != llvm::Triple::arm &&119Triple.getArch() != llvm::Triple::thumb &&120Triple.getArch() != llvm::Triple::armeb &&121Triple.getArch() != llvm::Triple::thumbeb)122return false;123124if (Triple.getVendor() != llvm::Triple::UnknownVendor)125return false;126127if (Triple.getOS() != llvm::Triple::UnknownOS)128return false;129130if (Triple.getEnvironment() != llvm::Triple::EABI &&131Triple.getEnvironment() != llvm::Triple::EABIHF)132return false;133134return true;135}136137/// Is the triple {aarch64.aarch64_be}-none-elf?138static bool isAArch64BareMetal(const llvm::Triple &Triple) {139if (Triple.getArch() != llvm::Triple::aarch64 &&140Triple.getArch() != llvm::Triple::aarch64_be)141return false;142143if (Triple.getVendor() != llvm::Triple::UnknownVendor)144return false;145146if (Triple.getOS() != llvm::Triple::UnknownOS)147return false;148149return Triple.getEnvironmentName() == "elf";150}151152static bool isRISCVBareMetal(const llvm::Triple &Triple) {153if (!Triple.isRISCV())154return false;155156if (Triple.getVendor() != llvm::Triple::UnknownVendor)157return false;158159if (Triple.getOS() != llvm::Triple::UnknownOS)160return false;161162return Triple.getEnvironmentName() == "elf";163}164165/// Is the triple powerpc[64][le]-*-none-eabi?166static bool isPPCBareMetal(const llvm::Triple &Triple) {167return Triple.isPPC() && Triple.getOS() == llvm::Triple::UnknownOS &&168Triple.getEnvironment() == llvm::Triple::EABI;169}170171static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D,172StringRef MultilibPath, const ArgList &Args,173DetectedMultilibs &Result) {174llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =175D.getVFS().getBufferForFile(MultilibPath);176if (!MB)177return;178Multilib::flags_list Flags = TC.getMultilibFlags(Args);179llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet =180MultilibSet::parseYaml(*MB.get());181if (ErrorOrMultilibSet.getError())182return;183Result.Multilibs = ErrorOrMultilibSet.get();184if (Result.Multilibs.select(Flags, Result.SelectedMultilibs))185return;186D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " ");187std::stringstream ss;188for (const Multilib &Multilib : Result.Multilibs)189ss << "\n" << llvm::join(Multilib.flags(), " ");190D.Diag(clang::diag::note_drv_available_multilibs) << ss.str();191}192193static constexpr llvm::StringLiteral MultilibFilename = "multilib.yaml";194195// Get the sysroot, before multilib takes effect.196static std::string computeBaseSysRoot(const Driver &D,197const llvm::Triple &Triple) {198if (!D.SysRoot.empty())199return D.SysRoot;200201SmallString<128> SysRootDir(D.Dir);202llvm::sys::path::append(SysRootDir, "..", "lib", "clang-runtimes");203204SmallString<128> MultilibPath(SysRootDir);205llvm::sys::path::append(MultilibPath, MultilibFilename);206207// New behaviour: if multilib.yaml is found then use clang-runtimes as the208// sysroot.209if (D.getVFS().exists(MultilibPath))210return std::string(SysRootDir);211212// Otherwise fall back to the old behaviour of appending the target triple.213llvm::sys::path::append(SysRootDir, D.getTargetTriple());214return std::string(SysRootDir);215}216217void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,218const ArgList &Args) {219DetectedMultilibs Result;220if (isRISCVBareMetal(Triple)) {221if (findRISCVMultilibs(D, Triple, Args, Result)) {222SelectedMultilibs = Result.SelectedMultilibs;223Multilibs = Result.Multilibs;224}225} else {226llvm::SmallString<128> MultilibPath(computeBaseSysRoot(D, Triple));227llvm::sys::path::append(MultilibPath, MultilibFilename);228findMultilibsFromYAML(*this, D, MultilibPath, Args, Result);229SelectedMultilibs = Result.SelectedMultilibs;230Multilibs = Result.Multilibs;231}232}233234bool BareMetal::handlesTarget(const llvm::Triple &Triple) {235return isARMBareMetal(Triple) || isAArch64BareMetal(Triple) ||236isRISCVBareMetal(Triple) || isPPCBareMetal(Triple);237}238239Tool *BareMetal::buildLinker() const {240return new tools::baremetal::Linker(*this);241}242243Tool *BareMetal::buildStaticLibTool() const {244return new tools::baremetal::StaticLibTool(*this);245}246247std::string BareMetal::computeSysRoot() const {248return computeBaseSysRoot(getDriver(), getTriple());249}250251BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const {252// Get multilibs in reverse order because they're ordered most-specific last.253if (!SelectedMultilibs.empty())254return llvm::reverse(SelectedMultilibs);255256// No multilibs selected so return a single default multilib.257static const llvm::SmallVector<Multilib> Default = {Multilib()};258return llvm::reverse(Default);259}260261void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,262ArgStringList &CC1Args) const {263if (DriverArgs.hasArg(options::OPT_nostdinc))264return;265266if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {267SmallString<128> Dir(getDriver().ResourceDir);268llvm::sys::path::append(Dir, "include");269addSystemInclude(DriverArgs, CC1Args, Dir.str());270}271272if (DriverArgs.hasArg(options::OPT_nostdlibinc))273return;274275if (std::optional<std::string> Path = getStdlibIncludePath())276addSystemInclude(DriverArgs, CC1Args, *Path);277278const SmallString<128> SysRoot(computeSysRoot());279if (!SysRoot.empty()) {280for (const Multilib &M : getOrderedMultilibs()) {281SmallString<128> Dir(SysRoot);282llvm::sys::path::append(Dir, M.includeSuffix());283llvm::sys::path::append(Dir, "include");284addSystemInclude(DriverArgs, CC1Args, Dir.str());285}286}287}288289void BareMetal::addClangTargetOptions(const ArgList &DriverArgs,290ArgStringList &CC1Args,291Action::OffloadKind) const {292CC1Args.push_back("-nostdsysteminc");293}294295void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,296ArgStringList &CC1Args) const {297if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc,298options::OPT_nostdincxx))299return;300301const Driver &D = getDriver();302std::string Target = getTripleString();303304auto AddCXXIncludePath = [&](StringRef Path) {305std::string Version = detectLibcxxVersion(Path);306if (Version.empty())307return;308309{310// First the per-target include dir: include/<target>/c++/v1.311SmallString<128> TargetDir(Path);312llvm::sys::path::append(TargetDir, Target, "c++", Version);313addSystemInclude(DriverArgs, CC1Args, TargetDir);314}315316{317// Then the generic dir: include/c++/v1.318SmallString<128> Dir(Path);319llvm::sys::path::append(Dir, "c++", Version);320addSystemInclude(DriverArgs, CC1Args, Dir);321}322};323324switch (GetCXXStdlibType(DriverArgs)) {325case ToolChain::CST_Libcxx: {326SmallString<128> P(D.Dir);327llvm::sys::path::append(P, "..", "include");328AddCXXIncludePath(P);329break;330}331case ToolChain::CST_Libstdcxx:332// We only support libc++ toolchain installation.333break;334}335336std::string SysRoot(computeSysRoot());337if (SysRoot.empty())338return;339340for (const Multilib &M : getOrderedMultilibs()) {341SmallString<128> Dir(SysRoot);342llvm::sys::path::append(Dir, M.gccSuffix());343switch (GetCXXStdlibType(DriverArgs)) {344case ToolChain::CST_Libcxx: {345// First check sysroot/usr/include/c++/v1 if it exists.346SmallString<128> TargetDir(Dir);347llvm::sys::path::append(TargetDir, "usr", "include", "c++", "v1");348if (D.getVFS().exists(TargetDir)) {349addSystemInclude(DriverArgs, CC1Args, TargetDir.str());350break;351}352// Add generic path if nothing else succeeded so far.353llvm::sys::path::append(Dir, "include", "c++", "v1");354addSystemInclude(DriverArgs, CC1Args, Dir.str());355break;356}357case ToolChain::CST_Libstdcxx: {358llvm::sys::path::append(Dir, "include", "c++");359std::error_code EC;360Generic_GCC::GCCVersion Version = {"", -1, -1, -1, "", "", ""};361// Walk the subdirs, and find the one with the newest gcc version:362for (llvm::vfs::directory_iterator363LI = D.getVFS().dir_begin(Dir.str(), EC),364LE;365!EC && LI != LE; LI = LI.increment(EC)) {366StringRef VersionText = llvm::sys::path::filename(LI->path());367auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText);368if (CandidateVersion.Major == -1)369continue;370if (CandidateVersion <= Version)371continue;372Version = CandidateVersion;373}374if (Version.Major != -1) {375llvm::sys::path::append(Dir, Version.Text);376addSystemInclude(DriverArgs, CC1Args, Dir.str());377}378break;379}380}381}382}383384void BareMetal::AddCXXStdlibLibArgs(const ArgList &Args,385ArgStringList &CmdArgs) const {386switch (GetCXXStdlibType(Args)) {387case ToolChain::CST_Libcxx:388CmdArgs.push_back("-lc++");389if (Args.hasArg(options::OPT_fexperimental_library))390CmdArgs.push_back("-lc++experimental");391CmdArgs.push_back("-lc++abi");392break;393case ToolChain::CST_Libstdcxx:394CmdArgs.push_back("-lstdc++");395CmdArgs.push_back("-lsupc++");396break;397}398CmdArgs.push_back("-lunwind");399}400401void BareMetal::AddLinkRuntimeLib(const ArgList &Args,402ArgStringList &CmdArgs) const {403ToolChain::RuntimeLibType RLT = GetRuntimeLibType(Args);404switch (RLT) {405case ToolChain::RLT_CompilerRT: {406CmdArgs.push_back(getCompilerRTArgString(Args, "builtins"));407return;408}409case ToolChain::RLT_Libgcc:410CmdArgs.push_back("-lgcc");411return;412}413llvm_unreachable("Unhandled RuntimeLibType.");414}415416void baremetal::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA,417const InputInfo &Output,418const InputInfoList &Inputs,419const ArgList &Args,420const char *LinkingOutput) const {421const Driver &D = getToolChain().getDriver();422423// Silence warning for "clang -g foo.o -o foo"424Args.ClaimAllArgs(options::OPT_g_Group);425// and "clang -emit-llvm foo.o -o foo"426Args.ClaimAllArgs(options::OPT_emit_llvm);427// and for "clang -w foo.o -o foo". Other warning options are already428// handled somewhere else.429Args.ClaimAllArgs(options::OPT_w);430// Silence warnings when linking C code with a C++ '-stdlib' argument.431Args.ClaimAllArgs(options::OPT_stdlib_EQ);432433// ar tool command "llvm-ar <options> <output_file> <input_files>".434ArgStringList CmdArgs;435// Create and insert file members with a deterministic index.436CmdArgs.push_back("rcsD");437CmdArgs.push_back(Output.getFilename());438439for (const auto &II : Inputs) {440if (II.isFilename()) {441CmdArgs.push_back(II.getFilename());442}443}444445// Delete old output archive file if it already exists before generating a new446// archive file.447const char *OutputFileName = Output.getFilename();448if (Output.isFilename() && llvm::sys::fs::exists(OutputFileName)) {449if (std::error_code EC = llvm::sys::fs::remove(OutputFileName)) {450D.Diag(diag::err_drv_unable_to_remove_file) << EC.message();451return;452}453}454455const char *Exec = Args.MakeArgString(getToolChain().GetStaticLibToolPath());456C.addCommand(std::make_unique<Command>(JA, *this,457ResponseFileSupport::AtFileCurCP(),458Exec, CmdArgs, Inputs, Output));459}460461void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,462const InputInfo &Output,463const InputInfoList &Inputs,464const ArgList &Args,465const char *LinkingOutput) const {466ArgStringList CmdArgs;467468auto &TC = static_cast<const toolchains::BareMetal &>(getToolChain());469const Driver &D = getToolChain().getDriver();470const llvm::Triple::ArchType Arch = TC.getArch();471const llvm::Triple &Triple = getToolChain().getEffectiveTriple();472473AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);474475CmdArgs.push_back("-Bstatic");476477if (TC.getTriple().isRISCV() && Args.hasArg(options::OPT_mno_relax))478CmdArgs.push_back("--no-relax");479480if (Triple.isARM() || Triple.isThumb()) {481bool IsBigEndian = arm::isARMBigEndian(Triple, Args);482if (IsBigEndian)483arm::appendBE8LinkFlag(Args, CmdArgs, Triple);484CmdArgs.push_back(IsBigEndian ? "-EB" : "-EL");485} else if (Triple.isAArch64()) {486CmdArgs.push_back(Arch == llvm::Triple::aarch64_be ? "-EB" : "-EL");487}488489Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group,490options::OPT_s, options::OPT_t, options::OPT_r});491492TC.AddFilePathLibArgs(Args, CmdArgs);493494for (const auto &LibPath : TC.getLibraryPaths())495CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-L", LibPath)));496497if (TC.ShouldLinkCXXStdlib(Args))498TC.AddCXXStdlibLibArgs(Args, CmdArgs);499500if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {501CmdArgs.push_back("-lc");502CmdArgs.push_back("-lm");503504TC.AddLinkRuntimeLib(Args, CmdArgs);505}506507if (D.isUsingLTO()) {508assert(!Inputs.empty() && "Must have at least one input.");509// Find the first filename InputInfo object.510auto Input = llvm::find_if(511Inputs, [](const InputInfo &II) -> bool { return II.isFilename(); });512if (Input == Inputs.end())513// For a very rare case, all of the inputs to the linker are514// InputArg. If that happens, just use the first InputInfo.515Input = Inputs.begin();516517addLTOOptions(TC, Args, CmdArgs, Output, *Input,518D.getLTOMode() == LTOK_Thin);519}520if (TC.getTriple().isRISCV())521CmdArgs.push_back("-X");522523// The R_ARM_TARGET2 relocation must be treated as R_ARM_REL32 on arm*-*-elf524// and arm*-*-eabi (the default is R_ARM_GOT_PREL, used on arm*-*-linux and525// arm*-*-*bsd).526if (isARMBareMetal(TC.getTriple()))527CmdArgs.push_back("--target2=rel");528529CmdArgs.push_back("-o");530CmdArgs.push_back(Output.getFilename());531532C.addCommand(std::make_unique<Command>(533JA, *this, ResponseFileSupport::AtFileCurCP(),534Args.MakeArgString(TC.GetLinkerPath()), CmdArgs, Inputs, Output));535}536537// BareMetal toolchain allows all sanitizers where the compiler generates valid538// code, ignoring all runtime library support issues on the assumption that539// baremetal targets typically implement their own runtime support.540SanitizerMask BareMetal::getSupportedSanitizers() const {541const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64;542const bool IsAArch64 = getTriple().getArch() == llvm::Triple::aarch64 ||543getTriple().getArch() == llvm::Triple::aarch64_be;544const bool IsRISCV64 = getTriple().getArch() == llvm::Triple::riscv64;545SanitizerMask Res = ToolChain::getSupportedSanitizers();546Res |= SanitizerKind::Address;547Res |= SanitizerKind::KernelAddress;548Res |= SanitizerKind::PointerCompare;549Res |= SanitizerKind::PointerSubtract;550Res |= SanitizerKind::Fuzzer;551Res |= SanitizerKind::FuzzerNoLink;552Res |= SanitizerKind::Vptr;553Res |= SanitizerKind::SafeStack;554Res |= SanitizerKind::Thread;555Res |= SanitizerKind::Scudo;556if (IsX86_64 || IsAArch64 || IsRISCV64) {557Res |= SanitizerKind::HWAddress;558Res |= SanitizerKind::KernelHWAddress;559}560return Res;561}562563564