1 //===-- BareMetal.cpp - Bare Metal ToolChain --------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "BareMetal.h" 10 11 #include "CommonArgs.h" 12 #include "Gnu.h" 13 #include "clang/Driver/InputInfo.h" 14 15 #include "Arch/ARM.h" 16 #include "Arch/RISCV.h" 17 #include "clang/Driver/Compilation.h" 18 #include "clang/Driver/Driver.h" 19 #include "clang/Driver/DriverDiagnostic.h" 20 #include "clang/Driver/MultilibBuilder.h" 21 #include "clang/Driver/Options.h" 22 #include "llvm/ADT/StringExtras.h" 23 #include "llvm/Option/ArgList.h" 24 #include "llvm/Support/Path.h" 25 #include "llvm/Support/VirtualFileSystem.h" 26 #include "llvm/Support/raw_ostream.h" 27 28 #include <sstream> 29 30 using namespace llvm::opt; 31 using namespace clang; 32 using namespace clang::driver; 33 using namespace clang::driver::tools; 34 using namespace clang::driver::toolchains; 35 36 static bool findRISCVMultilibs(const Driver &D, 37 const llvm::Triple &TargetTriple, 38 const ArgList &Args, DetectedMultilibs &Result) { 39 Multilib::flags_list Flags; 40 StringRef Arch = riscv::getRISCVArch(Args, TargetTriple); 41 StringRef Abi = tools::riscv::getRISCVABI(Args, TargetTriple); 42 43 if (TargetTriple.isRISCV64()) { 44 MultilibBuilder Imac = 45 MultilibBuilder().flag("-march=rv64imac").flag("-mabi=lp64"); 46 MultilibBuilder Imafdc = MultilibBuilder("/rv64imafdc/lp64d") 47 .flag("-march=rv64imafdc") 48 .flag("-mabi=lp64d"); 49 50 // Multilib reuse 51 bool UseImafdc = 52 (Arch == "rv64imafdc") || (Arch == "rv64gc"); // gc => imafdc 53 54 addMultilibFlag((Arch == "rv64imac"), "-march=rv64imac", Flags); 55 addMultilibFlag(UseImafdc, "-march=rv64imafdc", Flags); 56 addMultilibFlag(Abi == "lp64", "-mabi=lp64", Flags); 57 addMultilibFlag(Abi == "lp64d", "-mabi=lp64d", Flags); 58 59 Result.Multilibs = 60 MultilibSetBuilder().Either(Imac, Imafdc).makeMultilibSet(); 61 return Result.Multilibs.select(Flags, Result.SelectedMultilibs); 62 } 63 if (TargetTriple.isRISCV32()) { 64 MultilibBuilder Imac = 65 MultilibBuilder().flag("-march=rv32imac").flag("-mabi=ilp32"); 66 MultilibBuilder I = MultilibBuilder("/rv32i/ilp32") 67 .flag("-march=rv32i") 68 .flag("-mabi=ilp32"); 69 MultilibBuilder Im = MultilibBuilder("/rv32im/ilp32") 70 .flag("-march=rv32im") 71 .flag("-mabi=ilp32"); 72 MultilibBuilder Iac = MultilibBuilder("/rv32iac/ilp32") 73 .flag("-march=rv32iac") 74 .flag("-mabi=ilp32"); 75 MultilibBuilder Imafc = MultilibBuilder("/rv32imafc/ilp32f") 76 .flag("-march=rv32imafc") 77 .flag("-mabi=ilp32f"); 78 79 // Multilib reuse 80 bool UseI = (Arch == "rv32i") || (Arch == "rv32ic"); // ic => i 81 bool UseIm = (Arch == "rv32im") || (Arch == "rv32imc"); // imc => im 82 bool UseImafc = (Arch == "rv32imafc") || (Arch == "rv32imafdc") || 83 (Arch == "rv32gc"); // imafdc,gc => imafc 84 85 addMultilibFlag(UseI, "-march=rv32i", Flags); 86 addMultilibFlag(UseIm, "-march=rv32im", Flags); 87 addMultilibFlag((Arch == "rv32iac"), "-march=rv32iac", Flags); 88 addMultilibFlag((Arch == "rv32imac"), "-march=rv32imac", Flags); 89 addMultilibFlag(UseImafc, "-march=rv32imafc", Flags); 90 addMultilibFlag(Abi == "ilp32", "-mabi=ilp32", Flags); 91 addMultilibFlag(Abi == "ilp32f", "-mabi=ilp32f", Flags); 92 93 Result.Multilibs = 94 MultilibSetBuilder().Either(I, Im, Iac, Imac, Imafc).makeMultilibSet(); 95 return Result.Multilibs.select(Flags, Result.SelectedMultilibs); 96 } 97 return false; 98 } 99 100 BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple, 101 const ArgList &Args) 102 : ToolChain(D, Triple, Args) { 103 getProgramPaths().push_back(getDriver().getInstalledDir()); 104 if (getDriver().getInstalledDir() != getDriver().Dir) 105 getProgramPaths().push_back(getDriver().Dir); 106 107 findMultilibs(D, Triple, Args); 108 SmallString<128> SysRoot(computeSysRoot()); 109 if (!SysRoot.empty()) { 110 for (const Multilib &M : getOrderedMultilibs()) { 111 SmallString<128> Dir(SysRoot); 112 llvm::sys::path::append(Dir, M.osSuffix(), "lib"); 113 getFilePaths().push_back(std::string(Dir)); 114 getLibraryPaths().push_back(std::string(Dir)); 115 } 116 } 117 } 118 119 /// Is the triple {arm,armeb,thumb,thumbeb}-none-none-{eabi,eabihf} ? 120 static bool isARMBareMetal(const llvm::Triple &Triple) { 121 if (Triple.getArch() != llvm::Triple::arm && 122 Triple.getArch() != llvm::Triple::thumb && 123 Triple.getArch() != llvm::Triple::armeb && 124 Triple.getArch() != llvm::Triple::thumbeb) 125 return false; 126 127 if (Triple.getVendor() != llvm::Triple::UnknownVendor) 128 return false; 129 130 if (Triple.getOS() != llvm::Triple::UnknownOS) 131 return false; 132 133 if (Triple.getEnvironment() != llvm::Triple::EABI && 134 Triple.getEnvironment() != llvm::Triple::EABIHF) 135 return false; 136 137 return true; 138 } 139 140 /// Is the triple {aarch64.aarch64_be}-none-elf? 141 static bool isAArch64BareMetal(const llvm::Triple &Triple) { 142 if (Triple.getArch() != llvm::Triple::aarch64 && 143 Triple.getArch() != llvm::Triple::aarch64_be) 144 return false; 145 146 if (Triple.getVendor() != llvm::Triple::UnknownVendor) 147 return false; 148 149 if (Triple.getOS() != llvm::Triple::UnknownOS) 150 return false; 151 152 return Triple.getEnvironmentName() == "elf"; 153 } 154 155 static bool isRISCVBareMetal(const llvm::Triple &Triple) { 156 if (!Triple.isRISCV()) 157 return false; 158 159 if (Triple.getVendor() != llvm::Triple::UnknownVendor) 160 return false; 161 162 if (Triple.getOS() != llvm::Triple::UnknownOS) 163 return false; 164 165 return Triple.getEnvironmentName() == "elf"; 166 } 167 168 /// Is the triple powerpc[64][le]-*-none-eabi? 169 static bool isPPCBareMetal(const llvm::Triple &Triple) { 170 return Triple.isPPC() && Triple.getOS() == llvm::Triple::UnknownOS && 171 Triple.getEnvironment() == llvm::Triple::EABI; 172 } 173 174 static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D, 175 StringRef MultilibPath, const ArgList &Args, 176 DetectedMultilibs &Result) { 177 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = 178 D.getVFS().getBufferForFile(MultilibPath); 179 if (!MB) 180 return; 181 Multilib::flags_list Flags = TC.getMultilibFlags(Args); 182 llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet = 183 MultilibSet::parseYaml(*MB.get()); 184 if (ErrorOrMultilibSet.getError()) 185 return; 186 Result.Multilibs = ErrorOrMultilibSet.get(); 187 if (Result.Multilibs.select(Flags, Result.SelectedMultilibs)) 188 return; 189 D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " "); 190 std::stringstream ss; 191 for (const Multilib &Multilib : Result.Multilibs) 192 ss << "\n" << llvm::join(Multilib.flags(), " "); 193 D.Diag(clang::diag::note_drv_available_multilibs) << ss.str(); 194 } 195 196 static constexpr llvm::StringLiteral MultilibFilename = "multilib.yaml"; 197 198 // Get the sysroot, before multilib takes effect. 199 static std::string computeBaseSysRoot(const Driver &D, 200 const llvm::Triple &Triple) { 201 if (!D.SysRoot.empty()) 202 return D.SysRoot; 203 204 SmallString<128> SysRootDir(D.Dir); 205 llvm::sys::path::append(SysRootDir, "..", "lib", "clang-runtimes"); 206 207 SmallString<128> MultilibPath(SysRootDir); 208 llvm::sys::path::append(MultilibPath, MultilibFilename); 209 210 // New behaviour: if multilib.yaml is found then use clang-runtimes as the 211 // sysroot. 212 if (D.getVFS().exists(MultilibPath)) 213 return std::string(SysRootDir); 214 215 // Otherwise fall back to the old behaviour of appending the target triple. 216 llvm::sys::path::append(SysRootDir, D.getTargetTriple()); 217 return std::string(SysRootDir); 218 } 219 220 void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple, 221 const ArgList &Args) { 222 DetectedMultilibs Result; 223 if (isRISCVBareMetal(Triple)) { 224 if (findRISCVMultilibs(D, Triple, Args, Result)) { 225 SelectedMultilibs = Result.SelectedMultilibs; 226 Multilibs = Result.Multilibs; 227 } 228 } else { 229 llvm::SmallString<128> MultilibPath(computeBaseSysRoot(D, Triple)); 230 llvm::sys::path::append(MultilibPath, MultilibFilename); 231 findMultilibsFromYAML(*this, D, MultilibPath, Args, Result); 232 SelectedMultilibs = Result.SelectedMultilibs; 233 Multilibs = Result.Multilibs; 234 } 235 } 236 237 bool BareMetal::handlesTarget(const llvm::Triple &Triple) { 238 return isARMBareMetal(Triple) || isAArch64BareMetal(Triple) || 239 isRISCVBareMetal(Triple) || isPPCBareMetal(Triple); 240 } 241 242 Tool *BareMetal::buildLinker() const { 243 return new tools::baremetal::Linker(*this); 244 } 245 246 Tool *BareMetal::buildStaticLibTool() const { 247 return new tools::baremetal::StaticLibTool(*this); 248 } 249 250 std::string BareMetal::computeSysRoot() const { 251 return computeBaseSysRoot(getDriver(), getTriple()); 252 } 253 254 BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const { 255 // Get multilibs in reverse order because they're ordered most-specific last. 256 if (!SelectedMultilibs.empty()) 257 return llvm::reverse(SelectedMultilibs); 258 259 // No multilibs selected so return a single default multilib. 260 static const llvm::SmallVector<Multilib> Default = {Multilib()}; 261 return llvm::reverse(Default); 262 } 263 264 void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 265 ArgStringList &CC1Args) const { 266 if (DriverArgs.hasArg(options::OPT_nostdinc)) 267 return; 268 269 if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { 270 SmallString<128> Dir(getDriver().ResourceDir); 271 llvm::sys::path::append(Dir, "include"); 272 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 273 } 274 275 if (!DriverArgs.hasArg(options::OPT_nostdlibinc)) { 276 const SmallString<128> SysRoot(computeSysRoot()); 277 if (!SysRoot.empty()) { 278 for (const Multilib &M : getOrderedMultilibs()) { 279 SmallString<128> Dir(SysRoot); 280 llvm::sys::path::append(Dir, M.includeSuffix()); 281 llvm::sys::path::append(Dir, "include"); 282 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 283 } 284 } 285 } 286 } 287 288 void BareMetal::addClangTargetOptions(const ArgList &DriverArgs, 289 ArgStringList &CC1Args, 290 Action::OffloadKind) const { 291 CC1Args.push_back("-nostdsysteminc"); 292 } 293 294 void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, 295 ArgStringList &CC1Args) const { 296 if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc, 297 options::OPT_nostdincxx)) 298 return; 299 300 const Driver &D = getDriver(); 301 std::string SysRoot(computeSysRoot()); 302 if (SysRoot.empty()) 303 return; 304 305 for (const Multilib &M : getOrderedMultilibs()) { 306 SmallString<128> Dir(SysRoot); 307 llvm::sys::path::append(Dir, M.gccSuffix()); 308 switch (GetCXXStdlibType(DriverArgs)) { 309 case ToolChain::CST_Libcxx: { 310 // First check sysroot/usr/include/c++/v1 if it exists. 311 SmallString<128> TargetDir(Dir); 312 llvm::sys::path::append(TargetDir, "usr", "include", "c++", "v1"); 313 if (D.getVFS().exists(TargetDir)) { 314 addSystemInclude(DriverArgs, CC1Args, TargetDir.str()); 315 break; 316 } 317 // Add generic path if nothing else succeeded so far. 318 llvm::sys::path::append(Dir, "include", "c++", "v1"); 319 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 320 break; 321 } 322 case ToolChain::CST_Libstdcxx: { 323 llvm::sys::path::append(Dir, "include", "c++"); 324 std::error_code EC; 325 Generic_GCC::GCCVersion Version = {"", -1, -1, -1, "", "", ""}; 326 // Walk the subdirs, and find the one with the newest gcc version: 327 for (llvm::vfs::directory_iterator 328 LI = D.getVFS().dir_begin(Dir.str(), EC), 329 LE; 330 !EC && LI != LE; LI = LI.increment(EC)) { 331 StringRef VersionText = llvm::sys::path::filename(LI->path()); 332 auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText); 333 if (CandidateVersion.Major == -1) 334 continue; 335 if (CandidateVersion <= Version) 336 continue; 337 Version = CandidateVersion; 338 } 339 if (Version.Major != -1) { 340 llvm::sys::path::append(Dir, Version.Text); 341 addSystemInclude(DriverArgs, CC1Args, Dir.str()); 342 } 343 break; 344 } 345 } 346 } 347 } 348 349 void BareMetal::AddCXXStdlibLibArgs(const ArgList &Args, 350 ArgStringList &CmdArgs) const { 351 switch (GetCXXStdlibType(Args)) { 352 case ToolChain::CST_Libcxx: 353 CmdArgs.push_back("-lc++"); 354 if (Args.hasArg(options::OPT_fexperimental_library)) 355 CmdArgs.push_back("-lc++experimental"); 356 CmdArgs.push_back("-lc++abi"); 357 break; 358 case ToolChain::CST_Libstdcxx: 359 CmdArgs.push_back("-lstdc++"); 360 CmdArgs.push_back("-lsupc++"); 361 break; 362 } 363 CmdArgs.push_back("-lunwind"); 364 } 365 366 void BareMetal::AddLinkRuntimeLib(const ArgList &Args, 367 ArgStringList &CmdArgs) const { 368 ToolChain::RuntimeLibType RLT = GetRuntimeLibType(Args); 369 switch (RLT) { 370 case ToolChain::RLT_CompilerRT: { 371 const std::string FileName = getCompilerRT(Args, "builtins"); 372 llvm::StringRef BaseName = llvm::sys::path::filename(FileName); 373 BaseName.consume_front("lib"); 374 BaseName.consume_back(".a"); 375 CmdArgs.push_back(Args.MakeArgString("-l" + BaseName)); 376 return; 377 } 378 case ToolChain::RLT_Libgcc: 379 CmdArgs.push_back("-lgcc"); 380 return; 381 } 382 llvm_unreachable("Unhandled RuntimeLibType."); 383 } 384 385 void baremetal::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA, 386 const InputInfo &Output, 387 const InputInfoList &Inputs, 388 const ArgList &Args, 389 const char *LinkingOutput) const { 390 const Driver &D = getToolChain().getDriver(); 391 392 // Silence warning for "clang -g foo.o -o foo" 393 Args.ClaimAllArgs(options::OPT_g_Group); 394 // and "clang -emit-llvm foo.o -o foo" 395 Args.ClaimAllArgs(options::OPT_emit_llvm); 396 // and for "clang -w foo.o -o foo". Other warning options are already 397 // handled somewhere else. 398 Args.ClaimAllArgs(options::OPT_w); 399 // Silence warnings when linking C code with a C++ '-stdlib' argument. 400 Args.ClaimAllArgs(options::OPT_stdlib_EQ); 401 402 // ar tool command "llvm-ar <options> <output_file> <input_files>". 403 ArgStringList CmdArgs; 404 // Create and insert file members with a deterministic index. 405 CmdArgs.push_back("rcsD"); 406 CmdArgs.push_back(Output.getFilename()); 407 408 for (const auto &II : Inputs) { 409 if (II.isFilename()) { 410 CmdArgs.push_back(II.getFilename()); 411 } 412 } 413 414 // Delete old output archive file if it already exists before generating a new 415 // archive file. 416 const char *OutputFileName = Output.getFilename(); 417 if (Output.isFilename() && llvm::sys::fs::exists(OutputFileName)) { 418 if (std::error_code EC = llvm::sys::fs::remove(OutputFileName)) { 419 D.Diag(diag::err_drv_unable_to_remove_file) << EC.message(); 420 return; 421 } 422 } 423 424 const char *Exec = Args.MakeArgString(getToolChain().GetStaticLibToolPath()); 425 C.addCommand(std::make_unique<Command>(JA, *this, 426 ResponseFileSupport::AtFileCurCP(), 427 Exec, CmdArgs, Inputs, Output)); 428 } 429 430 void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA, 431 const InputInfo &Output, 432 const InputInfoList &Inputs, 433 const ArgList &Args, 434 const char *LinkingOutput) const { 435 ArgStringList CmdArgs; 436 437 auto &TC = static_cast<const toolchains::BareMetal &>(getToolChain()); 438 const llvm::Triple::ArchType Arch = TC.getArch(); 439 const llvm::Triple &Triple = getToolChain().getEffectiveTriple(); 440 441 AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); 442 443 CmdArgs.push_back("-Bstatic"); 444 445 if (Triple.isARM() || Triple.isThumb()) { 446 bool IsBigEndian = arm::isARMBigEndian(Triple, Args); 447 if (IsBigEndian) 448 arm::appendBE8LinkFlag(Args, CmdArgs, Triple); 449 CmdArgs.push_back(IsBigEndian ? "-EB" : "-EL"); 450 } else if (Triple.isAArch64()) { 451 CmdArgs.push_back(Arch == llvm::Triple::aarch64_be ? "-EB" : "-EL"); 452 } 453 454 Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group, 455 options::OPT_s, options::OPT_t, options::OPT_r}); 456 457 TC.AddFilePathLibArgs(Args, CmdArgs); 458 459 for (const auto &LibPath : TC.getLibraryPaths()) 460 CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-L", LibPath))); 461 462 const std::string FileName = TC.getCompilerRT(Args, "builtins"); 463 llvm::SmallString<128> PathBuf{FileName}; 464 llvm::sys::path::remove_filename(PathBuf); 465 CmdArgs.push_back(Args.MakeArgString("-L" + PathBuf)); 466 467 if (TC.ShouldLinkCXXStdlib(Args)) 468 TC.AddCXXStdlibLibArgs(Args, CmdArgs); 469 470 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { 471 CmdArgs.push_back("-lc"); 472 CmdArgs.push_back("-lm"); 473 474 TC.AddLinkRuntimeLib(Args, CmdArgs); 475 } 476 477 if (TC.getTriple().isRISCV()) 478 CmdArgs.push_back("-X"); 479 480 // The R_ARM_TARGET2 relocation must be treated as R_ARM_REL32 on arm*-*-elf 481 // and arm*-*-eabi (the default is R_ARM_GOT_PREL, used on arm*-*-linux and 482 // arm*-*-*bsd). 483 if (isARMBareMetal(TC.getTriple())) 484 CmdArgs.push_back("--target2=rel"); 485 486 CmdArgs.push_back("-o"); 487 CmdArgs.push_back(Output.getFilename()); 488 489 C.addCommand(std::make_unique<Command>( 490 JA, *this, ResponseFileSupport::AtFileCurCP(), 491 Args.MakeArgString(TC.GetLinkerPath()), CmdArgs, Inputs, Output)); 492 } 493 494 // BareMetal toolchain allows all sanitizers where the compiler generates valid 495 // code, ignoring all runtime library support issues on the assumption that 496 // baremetal targets typically implement their own runtime support. 497 SanitizerMask BareMetal::getSupportedSanitizers() const { 498 const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64; 499 const bool IsAArch64 = getTriple().getArch() == llvm::Triple::aarch64 || 500 getTriple().getArch() == llvm::Triple::aarch64_be; 501 const bool IsRISCV64 = getTriple().getArch() == llvm::Triple::riscv64; 502 SanitizerMask Res = ToolChain::getSupportedSanitizers(); 503 Res |= SanitizerKind::Address; 504 Res |= SanitizerKind::KernelAddress; 505 Res |= SanitizerKind::PointerCompare; 506 Res |= SanitizerKind::PointerSubtract; 507 Res |= SanitizerKind::Fuzzer; 508 Res |= SanitizerKind::FuzzerNoLink; 509 Res |= SanitizerKind::Vptr; 510 Res |= SanitizerKind::SafeStack; 511 Res |= SanitizerKind::Thread; 512 Res |= SanitizerKind::Scudo; 513 if (IsX86_64 || IsAArch64 || IsRISCV64) { 514 Res |= SanitizerKind::HWAddress; 515 Res |= SanitizerKind::KernelHWAddress; 516 } 517 return Res; 518 } 519