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 "Gnu.h"
12#include "clang/Driver/CommonArgs.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/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
26#include <sstream>
27
28using namespace llvm::opt;
29using namespace clang;
30using namespace clang::driver;
31using namespace clang::driver::tools;
32using namespace clang::driver::toolchains;
33
34/// Is the triple {aarch64.aarch64_be}-none-elf?
35static bool isAArch64BareMetal(const llvm::Triple &Triple) {
36 if (Triple.getArch() != llvm::Triple::aarch64 &&
37 Triple.getArch() != llvm::Triple::aarch64_be)
38 return false;
39
40 if (Triple.getVendor() != llvm::Triple::UnknownVendor)
41 return false;
42
43 if (Triple.getOS() != llvm::Triple::UnknownOS)
44 return false;
45
46 return Triple.getEnvironmentName() == "elf";
47}
48
49static bool isRISCVBareMetal(const llvm::Triple &Triple) {
50 if (!Triple.isRISCV())
51 return false;
52
53 if (Triple.getVendor() != llvm::Triple::UnknownVendor)
54 return false;
55
56 if (Triple.getOS() != llvm::Triple::UnknownOS)
57 return false;
58
59 return Triple.getEnvironmentName() == "elf";
60}
61
62/// Is the triple powerpc[64][le]-*-none-eabi?
63static bool isPPCBareMetal(const llvm::Triple &Triple) {
64 return Triple.isPPC() && Triple.getOS() == llvm::Triple::UnknownOS &&
65 Triple.getEnvironment() == llvm::Triple::EABI;
66}
67
68static bool findRISCVMultilibs(const Driver &D,
69 const llvm::Triple &TargetTriple,
70 const ArgList &Args, DetectedMultilibs &Result) {
71 Multilib::flags_list Flags;
72 std::string Arch = riscv::getRISCVArch(Args, Triple: TargetTriple);
73 StringRef Abi = tools::riscv::getRISCVABI(Args, Triple: TargetTriple);
74
75 if (TargetTriple.isRISCV64()) {
76 MultilibBuilder Imac =
77 MultilibBuilder().flag(Flag: "-march=rv64imac").flag(Flag: "-mabi=lp64");
78 MultilibBuilder Imafdc = MultilibBuilder("/rv64imafdc/lp64d")
79 .flag(Flag: "-march=rv64imafdc")
80 .flag(Flag: "-mabi=lp64d");
81
82 // Multilib reuse
83 bool UseImafdc =
84 (Arch == "rv64imafdc") || (Arch == "rv64gc"); // gc => imafdc
85
86 addMultilibFlag(Enabled: (Arch == "rv64imac"), Flag: "-march=rv64imac", Flags);
87 addMultilibFlag(Enabled: UseImafdc, Flag: "-march=rv64imafdc", Flags);
88 addMultilibFlag(Enabled: Abi == "lp64", Flag: "-mabi=lp64", Flags);
89 addMultilibFlag(Enabled: Abi == "lp64d", Flag: "-mabi=lp64d", Flags);
90
91 Result.Multilibs =
92 MultilibSetBuilder().Either(M1: Imac, M2: Imafdc).makeMultilibSet();
93 return Result.Multilibs.select(D, Flags, Result.SelectedMultilibs);
94 }
95 if (TargetTriple.isRISCV32()) {
96 MultilibBuilder Imac =
97 MultilibBuilder().flag(Flag: "-march=rv32imac").flag(Flag: "-mabi=ilp32");
98 MultilibBuilder I = MultilibBuilder("/rv32i/ilp32")
99 .flag(Flag: "-march=rv32i")
100 .flag(Flag: "-mabi=ilp32");
101 MultilibBuilder Im = MultilibBuilder("/rv32im/ilp32")
102 .flag(Flag: "-march=rv32im")
103 .flag(Flag: "-mabi=ilp32");
104 MultilibBuilder Iac = MultilibBuilder("/rv32iac/ilp32")
105 .flag(Flag: "-march=rv32iac")
106 .flag(Flag: "-mabi=ilp32");
107 MultilibBuilder Imafc = MultilibBuilder("/rv32imafc/ilp32f")
108 .flag(Flag: "-march=rv32imafc")
109 .flag(Flag: "-mabi=ilp32f");
110
111 // Multilib reuse
112 bool UseI = (Arch == "rv32i") || (Arch == "rv32ic"); // ic => i
113 bool UseIm = (Arch == "rv32im") || (Arch == "rv32imc"); // imc => im
114 bool UseImafc = (Arch == "rv32imafc") || (Arch == "rv32imafdc") ||
115 (Arch == "rv32gc"); // imafdc,gc => imafc
116
117 addMultilibFlag(Enabled: UseI, Flag: "-march=rv32i", Flags);
118 addMultilibFlag(Enabled: UseIm, Flag: "-march=rv32im", Flags);
119 addMultilibFlag(Enabled: (Arch == "rv32iac"), Flag: "-march=rv32iac", Flags);
120 addMultilibFlag(Enabled: (Arch == "rv32imac"), Flag: "-march=rv32imac", Flags);
121 addMultilibFlag(Enabled: UseImafc, Flag: "-march=rv32imafc", Flags);
122 addMultilibFlag(Enabled: Abi == "ilp32", Flag: "-mabi=ilp32", Flags);
123 addMultilibFlag(Enabled: Abi == "ilp32f", Flag: "-mabi=ilp32f", Flags);
124
125 Result.Multilibs =
126 MultilibSetBuilder().Either(M1: I, M2: Im, M3: Iac, M4: Imac, M5: Imafc).makeMultilibSet();
127 return Result.Multilibs.select(D, Flags, Result.SelectedMultilibs);
128 }
129 return false;
130}
131
132static std::string computeClangRuntimesSysRoot(const Driver &D,
133 bool IncludeTriple) {
134 if (!D.SysRoot.empty())
135 return D.SysRoot;
136
137 SmallString<128> SysRootDir(D.Dir);
138 llvm::sys::path::append(path&: SysRootDir, a: "..", b: "lib", c: "clang-runtimes");
139
140 if (IncludeTriple)
141 llvm::sys::path::append(path&: SysRootDir, a: D.getTargetTriple());
142
143 return std::string(SysRootDir);
144}
145
146// Only consider the GCC toolchain based on the values provided through the
147// `--gcc-toolchain` and `--gcc-install-dir` flags. The function below returns
148// whether the GCC toolchain was initialized successfully.
149bool BareMetal::initGCCInstallation(const llvm::Triple &Triple,
150 const llvm::opt::ArgList &Args) {
151 if (Args.getLastArg(Ids: options::OPT_gcc_toolchain) ||
152 Args.getLastArg(Ids: clang::driver::options::OPT_gcc_install_dir_EQ)) {
153 GCCInstallation.init(TargetTriple: Triple, Args);
154 return GCCInstallation.isValid();
155 }
156 return false;
157}
158
159// This logic is adapted from RISCVToolChain.cpp as part of the ongoing effort
160// to merge RISCVToolChain into the Baremetal toolchain. It infers the presence
161// of a valid GCC toolchain by checking whether the `crt0.o` file exists in the
162// `bin/../<target-triple>/lib` directory.
163static bool detectGCCToolchainAdjacent(const Driver &D) {
164 SmallString<128> GCCDir;
165 llvm::sys::path::append(path&: GCCDir, a: D.Dir, b: "..", c: D.getTargetTriple(),
166 d: "lib/crt0.o");
167 return llvm::sys::fs::exists(Path: GCCDir);
168}
169
170// If no sysroot is provided the driver will first attempt to infer it from the
171// values of `--gcc-install-dir` or `--gcc-toolchain`, which specify the
172// location of a GCC toolchain.
173// If neither flag is used, the sysroot defaults to either:
174//    - `bin/../<target-triple>`
175//    - `bin/../lib/clang-runtimes/<target-triple>`
176//
177// To use the `clang-runtimes` path, ensure that `../<target-triple>/lib/crt0.o`
178// does not exist relative to the driver.
179std::string BareMetal::computeSysRoot() const {
180 // Use Baremetal::sysroot if it has already been set.
181 if (!SysRoot.empty())
182 return SysRoot;
183
184 // Use the sysroot specified via the `--sysroot` command-line flag, if
185 // provided.
186 const Driver &D = getDriver();
187 if (!D.SysRoot.empty())
188 return D.SysRoot;
189
190 // Attempt to infer sysroot from a valid GCC installation.
191 // If no valid GCC installation, check for a GCC toolchain alongside Clang.
192 SmallString<128> inferredSysRoot;
193 if (IsGCCInstallationValid) {
194 llvm::sys::path::append(path&: inferredSysRoot, a: GCCInstallation.getParentLibPath(),
195 b: "..", c: GCCInstallation.getTriple().str());
196 } else if (detectGCCToolchainAdjacent(D)) {
197 // Use the triple as provided to the driver. Unlike the parsed triple
198 // this has not been normalized to always contain every field.
199 llvm::sys::path::append(path&: inferredSysRoot, a: D.Dir, b: "..", c: D.getTargetTriple());
200 }
201 // If a valid sysroot was inferred and exists, use it
202 if (!inferredSysRoot.empty() && llvm::sys::fs::exists(Path: inferredSysRoot))
203 return std::string(inferredSysRoot);
204
205 // Use the clang-runtimes path.
206 return computeClangRuntimesSysRoot(D, /*IncludeTriple*/ true);
207}
208
209static void addMultilibsFilePaths(const Driver &D, const MultilibSet &Multilibs,
210 const Multilib &Multilib,
211 StringRef InstallPath,
212 ToolChain::path_list &Paths) {
213 if (const auto &PathsCallback = Multilibs.filePathsCallback())
214 for (const auto &Path : PathsCallback(Multilib))
215 addPathIfExists(D, Path: InstallPath + Path, Paths);
216}
217
218// GCC mutltilibs will only work for those targets that have their multlib
219// structure encoded into GCCInstallation. Baremetal toolchain supports ARM,
220// AArch64, RISCV and PPC and of these only RISCV have GCC multilibs hardcoded
221// in GCCInstallation.
222BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
223 const ArgList &Args)
224 : Generic_ELF(D, Triple, Args) {
225 IsGCCInstallationValid = initGCCInstallation(Triple, Args);
226 std::string ComputedSysRoot = computeSysRoot();
227 if (IsGCCInstallationValid) {
228 if (!isRISCVBareMetal(Triple))
229 D.Diag(DiagID: clang::diag::warn_drv_multilib_not_available_for_target);
230
231 Multilibs = GCCInstallation.getMultilibs();
232 SelectedMultilibs.assign(IL: {GCCInstallation.getMultilib()});
233
234 path_list &Paths = getFilePaths();
235 // Add toolchain/multilib specific file paths.
236 addMultilibsFilePaths(D, Multilibs, Multilib: SelectedMultilibs.back(),
237 InstallPath: GCCInstallation.getInstallPath(), Paths);
238 // Adding filepath for locating crt{begin,end}.o files.
239 Paths.push_back(Elt: GCCInstallation.getInstallPath().str());
240 // Adding filepath for locating crt0.o file.
241 Paths.push_back(Elt: ComputedSysRoot + "/lib");
242
243 ToolChain::path_list &PPaths = getProgramPaths();
244 // Multilib cross-compiler GCC installations put ld in a triple-prefixed
245 // directory off of the parent of the GCC installation.
246 PPaths.push_back(Elt: Twine(GCCInstallation.getParentLibPath() + "/../" +
247 GCCInstallation.getTriple().str() + "/bin")
248 .str());
249 PPaths.push_back(Elt: (GCCInstallation.getParentLibPath() + "/../bin").str());
250 } else {
251 getProgramPaths().push_back(Elt: getDriver().Dir);
252 findMultilibs(D, Triple, Args);
253 const SmallString<128> SysRootDir(computeSysRoot());
254 if (!SysRootDir.empty()) {
255 for (const Multilib &M : getOrderedMultilibs()) {
256 SmallString<128> Dir(SysRootDir);
257 llvm::sys::path::append(path&: Dir, a: M.osSuffix(), b: "lib");
258 getFilePaths().push_back(Elt: std::string(Dir));
259 getLibraryPaths().push_back(Elt: std::string(Dir));
260 }
261 }
262 }
263}
264
265static void
266findMultilibsFromYAML(const ToolChain &TC, const Driver &D,
267 StringRef MultilibPath, const ArgList &Args,
268 DetectedMultilibs &Result,
269 SmallVector<StringRef> &CustomFlagsMacroDefines) {
270 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
271 D.getVFS().getBufferForFile(Name: MultilibPath);
272 if (!MB)
273 return;
274 Multilib::flags_list Flags = TC.getMultilibFlags(Args);
275 llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet =
276 MultilibSet::parseYaml(*MB.get());
277 if (ErrorOrMultilibSet.getError())
278 return;
279 Result.Multilibs = ErrorOrMultilibSet.get();
280 if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs,
281 &CustomFlagsMacroDefines))
282 return;
283 D.Diag(DiagID: clang::diag::warn_drv_missing_multilib) << llvm::join(R&: Flags, Separator: " ");
284 std::stringstream ss;
285
286 // If multilib selection didn't complete successfully, report a list
287 // of all the configurations the user could have provided.
288 for (const Multilib &Multilib : Result.Multilibs)
289 if (!Multilib.isError())
290 ss << "\n" << llvm::join(R: Multilib.flags(), Separator: " ");
291 D.Diag(DiagID: clang::diag::note_drv_available_multilibs) << ss.str();
292
293 // Now report any custom error messages requested by the YAML. We do
294 // this after displaying the list of available multilibs, because
295 // that list is probably large, and (in interactive use) risks
296 // scrolling the useful error message off the top of the user's
297 // terminal.
298 for (const Multilib &Multilib : Result.SelectedMultilibs)
299 if (Multilib.isError())
300 D.Diag(DiagID: clang::diag::err_drv_multilib_custom_error)
301 << Multilib.getErrorMessage();
302
303 // If there was an error, clear the SelectedMultilibs vector, in
304 // case it contains partial data.
305 Result.SelectedMultilibs.clear();
306}
307
308static constexpr llvm::StringLiteral MultilibFilename = "multilib.yaml";
309
310static std::optional<llvm::SmallString<128>>
311getMultilibConfigPath(const Driver &D, const llvm::Triple &Triple,
312 const ArgList &Args) {
313 llvm::SmallString<128> MultilibPath;
314 if (Arg *ConfigFileArg = Args.getLastArg(Ids: options::OPT_multi_lib_config)) {
315 MultilibPath = ConfigFileArg->getValue();
316 if (!D.getVFS().exists(Path: MultilibPath)) {
317 D.Diag(DiagID: clang::diag::err_drv_no_such_file) << MultilibPath.str();
318 return {};
319 }
320 } else {
321 MultilibPath = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
322 llvm::sys::path::append(path&: MultilibPath, a: MultilibFilename);
323 }
324 return MultilibPath;
325}
326
327void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,
328 const ArgList &Args) {
329 DetectedMultilibs Result;
330 // Look for a multilib.yaml before trying target-specific hardwired logic.
331 // If it exists, always do what it specifies.
332 std::optional<llvm::SmallString<128>> MultilibPath =
333 getMultilibConfigPath(D, Triple, Args);
334 if (!MultilibPath)
335 return;
336 if (D.getVFS().exists(Path: *MultilibPath)) {
337 // If multilib.yaml is found, update sysroot so it doesn't use a target
338 // specific suffix
339 SysRoot = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
340 SmallVector<StringRef> CustomFlagMacroDefines;
341 findMultilibsFromYAML(TC: *this, D, MultilibPath: *MultilibPath, Args, Result,
342 CustomFlagsMacroDefines&: CustomFlagMacroDefines);
343 SelectedMultilibs = Result.SelectedMultilibs;
344 Multilibs = Result.Multilibs;
345 MultilibMacroDefines.append(in_start: CustomFlagMacroDefines.begin(),
346 in_end: CustomFlagMacroDefines.end());
347 } else if (isRISCVBareMetal(Triple) && !detectGCCToolchainAdjacent(D)) {
348 if (findRISCVMultilibs(D, TargetTriple: Triple, Args, Result)) {
349 SelectedMultilibs = Result.SelectedMultilibs;
350 Multilibs = Result.Multilibs;
351 }
352 }
353}
354
355bool BareMetal::handlesTarget(const llvm::Triple &Triple) {
356 return arm::isARMEABIBareMetal(Triple) || isAArch64BareMetal(Triple) ||
357 isRISCVBareMetal(Triple) || isPPCBareMetal(Triple);
358}
359
360Tool *BareMetal::buildLinker() const {
361 return new tools::baremetal::Linker(*this);
362}
363
364Tool *BareMetal::buildStaticLibTool() const {
365 return new tools::baremetal::StaticLibTool(*this);
366}
367
368BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const {
369 // Get multilibs in reverse order because they're ordered most-specific last.
370 if (!SelectedMultilibs.empty())
371 return llvm::reverse(C: SelectedMultilibs);
372
373 // No multilibs selected so return a single default multilib.
374 static const llvm::SmallVector<Multilib> Default = {Multilib()};
375 return llvm::reverse(C: Default);
376}
377
378ToolChain::CXXStdlibType BareMetal::GetDefaultCXXStdlibType() const {
379 if (getTriple().isRISCV() && IsGCCInstallationValid)
380 return ToolChain::CST_Libstdcxx;
381 return ToolChain::CST_Libcxx;
382}
383
384ToolChain::RuntimeLibType BareMetal::GetDefaultRuntimeLibType() const {
385 if (getTriple().isRISCV() && IsGCCInstallationValid)
386 return ToolChain::RLT_Libgcc;
387 return ToolChain::RLT_CompilerRT;
388}
389
390// TODO: Add a validity check for GCCInstallation.
391// If valid, use `UNW_Libgcc`; otherwise, use `UNW_None`.
392ToolChain::UnwindLibType
393BareMetal::GetUnwindLibType(const llvm::opt::ArgList &Args) const {
394 if (getTriple().isRISCV())
395 return ToolChain::UNW_None;
396
397 return ToolChain::GetUnwindLibType(Args);
398}
399
400void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
401 ArgStringList &CC1Args) const {
402 if (DriverArgs.hasArg(Ids: options::OPT_nostdinc))
403 return;
404
405 if (!DriverArgs.hasArg(Ids: options::OPT_nobuiltininc)) {
406 SmallString<128> Dir(getDriver().ResourceDir);
407 llvm::sys::path::append(path&: Dir, a: "include");
408 addSystemInclude(DriverArgs, CC1Args, Path: Dir.str());
409 }
410
411 if (DriverArgs.hasArg(Ids: options::OPT_nostdlibinc))
412 return;
413
414 if (std::optional<std::string> Path = getStdlibIncludePath())
415 addSystemInclude(DriverArgs, CC1Args, Path: *Path);
416
417 const SmallString<128> SysRootDir(computeSysRoot());
418 if (!SysRootDir.empty()) {
419 for (const Multilib &M : getOrderedMultilibs()) {
420 SmallString<128> Dir(SysRootDir);
421 llvm::sys::path::append(path&: Dir, a: M.includeSuffix());
422 llvm::sys::path::append(path&: Dir, a: "include");
423 addSystemInclude(DriverArgs, CC1Args, Path: Dir.str());
424 }
425 }
426}
427
428void BareMetal::addClangTargetOptions(const ArgList &DriverArgs,
429 ArgStringList &CC1Args,
430 Action::OffloadKind) const {
431 CC1Args.push_back(Elt: "-nostdsysteminc");
432}
433
434void BareMetal::addLibStdCxxIncludePaths(
435 const llvm::opt::ArgList &DriverArgs,
436 llvm::opt::ArgStringList &CC1Args) const {
437 if (!IsGCCInstallationValid)
438 return;
439 const GCCVersion &Version = GCCInstallation.getVersion();
440 StringRef TripleStr = GCCInstallation.getTriple().str();
441 const Multilib &Multilib = GCCInstallation.getMultilib();
442 addLibStdCXXIncludePaths(IncludeDir: computeSysRoot() + "/include/c++/" + Version.Text,
443 Triple: TripleStr, IncludeSuffix: Multilib.includeSuffix(), DriverArgs,
444 CC1Args);
445}
446
447void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
448 ArgStringList &CC1Args) const {
449 if (DriverArgs.hasArg(Ids: options::OPT_nostdinc, Ids: options::OPT_nostdlibinc,
450 Ids: options::OPT_nostdincxx))
451 return;
452
453 const Driver &D = getDriver();
454 std::string Target = getTripleString();
455
456 auto AddCXXIncludePath = [&](StringRef Path) {
457 std::string Version = detectLibcxxVersion(IncludePath: Path);
458 if (Version.empty())
459 return;
460
461 {
462 // First the per-target include dir: include/<target>/c++/v1.
463 SmallString<128> TargetDir(Path);
464 llvm::sys::path::append(path&: TargetDir, a: Target, b: "c++", c: Version);
465 addSystemInclude(DriverArgs, CC1Args, Path: TargetDir);
466 }
467
468 {
469 // Then the generic dir: include/c++/v1.
470 SmallString<128> Dir(Path);
471 llvm::sys::path::append(path&: Dir, a: "c++", b: Version);
472 addSystemInclude(DriverArgs, CC1Args, Path: Dir);
473 }
474 };
475
476 switch (GetCXXStdlibType(Args: DriverArgs)) {
477 case ToolChain::CST_Libcxx: {
478 SmallString<128> P(D.Dir);
479 llvm::sys::path::append(path&: P, a: "..", b: "include");
480 AddCXXIncludePath(P);
481 break;
482 }
483 case ToolChain::CST_Libstdcxx:
484 addLibStdCxxIncludePaths(DriverArgs, CC1Args);
485 break;
486 }
487
488 std::string SysRootDir(computeSysRoot());
489 if (SysRootDir.empty())
490 return;
491
492 for (const Multilib &M : getOrderedMultilibs()) {
493 SmallString<128> Dir(SysRootDir);
494 llvm::sys::path::append(path&: Dir, a: M.gccSuffix());
495 switch (GetCXXStdlibType(Args: DriverArgs)) {
496 case ToolChain::CST_Libcxx: {
497 // First check sysroot/usr/include/c++/v1 if it exists.
498 SmallString<128> TargetDir(Dir);
499 llvm::sys::path::append(path&: TargetDir, a: "usr", b: "include", c: "c++", d: "v1");
500 if (D.getVFS().exists(Path: TargetDir)) {
501 addSystemInclude(DriverArgs, CC1Args, Path: TargetDir.str());
502 break;
503 }
504 // Add generic path if nothing else succeeded so far.
505 llvm::sys::path::append(path&: Dir, a: "include", b: "c++", c: "v1");
506 addSystemInclude(DriverArgs, CC1Args, Path: Dir.str());
507 break;
508 }
509 case ToolChain::CST_Libstdcxx: {
510 llvm::sys::path::append(path&: Dir, a: "include", b: "c++");
511 std::error_code EC;
512 Generic_GCC::GCCVersion Version = {.Text: "", .Major: -1, .Minor: -1, .Patch: -1, .MajorStr: "", .MinorStr: "", .PatchSuffix: ""};
513 // Walk the subdirs, and find the one with the newest gcc version:
514 for (llvm::vfs::directory_iterator
515 LI = D.getVFS().dir_begin(Dir: Dir.str(), EC),
516 LE;
517 !EC && LI != LE; LI = LI.increment(EC)) {
518 StringRef VersionText = llvm::sys::path::filename(path: LI->path());
519 auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText);
520 if (CandidateVersion.Major == -1)
521 continue;
522 if (CandidateVersion <= Version)
523 continue;
524 Version = CandidateVersion;
525 }
526 if (Version.Major != -1) {
527 llvm::sys::path::append(path&: Dir, a: Version.Text);
528 addSystemInclude(DriverArgs, CC1Args, Path: Dir.str());
529 }
530 break;
531 }
532 }
533 }
534}
535
536void baremetal::StaticLibTool::ConstructJob(Compilation &C, const JobAction &JA,
537 const InputInfo &Output,
538 const InputInfoList &Inputs,
539 const ArgList &Args,
540 const char *LinkingOutput) const {
541 const Driver &D = getToolChain().getDriver();
542
543 // Silence warning for "clang -g foo.o -o foo"
544 Args.ClaimAllArgs(Id0: options::OPT_g_Group);
545 // and "clang -emit-llvm foo.o -o foo"
546 Args.ClaimAllArgs(Id0: options::OPT_emit_llvm);
547 // and for "clang -w foo.o -o foo". Other warning options are already
548 // handled somewhere else.
549 Args.ClaimAllArgs(Id0: options::OPT_w);
550 // Silence warnings when linking C code with a C++ '-stdlib' argument.
551 Args.ClaimAllArgs(Id0: options::OPT_stdlib_EQ);
552
553 // ar tool command "llvm-ar <options> <output_file> <input_files>".
554 ArgStringList CmdArgs;
555 // Create and insert file members with a deterministic index.
556 CmdArgs.push_back(Elt: "rcsD");
557 CmdArgs.push_back(Elt: Output.getFilename());
558
559 for (const auto &II : Inputs) {
560 if (II.isFilename()) {
561 CmdArgs.push_back(Elt: II.getFilename());
562 }
563 }
564
565 // Delete old output archive file if it already exists before generating a new
566 // archive file.
567 const char *OutputFileName = Output.getFilename();
568 if (Output.isFilename() && llvm::sys::fs::exists(Path: OutputFileName)) {
569 if (std::error_code EC = llvm::sys::fs::remove(path: OutputFileName)) {
570 D.Diag(DiagID: diag::err_drv_unable_to_remove_file) << EC.message();
571 return;
572 }
573 }
574
575 const char *Exec = Args.MakeArgString(Str: getToolChain().GetStaticLibToolPath());
576 C.addCommand(C: std::make_unique<Command>(args: JA, args: *this,
577 args: ResponseFileSupport::AtFileCurCP(),
578 args&: Exec, args&: CmdArgs, args: Inputs, args: Output));
579}
580
581void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
582 const InputInfo &Output,
583 const InputInfoList &Inputs,
584 const ArgList &Args,
585 const char *LinkingOutput) const {
586 ArgStringList CmdArgs;
587
588 auto &TC = static_cast<const toolchains::BareMetal &>(getToolChain());
589 const Driver &D = getToolChain().getDriver();
590 const llvm::Triple::ArchType Arch = TC.getArch();
591 const llvm::Triple &Triple = getToolChain().getEffectiveTriple();
592
593 if (!D.SysRoot.empty())
594 CmdArgs.push_back(Elt: Args.MakeArgString(Str: "--sysroot=" + D.SysRoot));
595
596 CmdArgs.push_back(Elt: "-Bstatic");
597
598 if (const char *LDMOption = getLDMOption(T: TC.getTriple(), Args)) {
599 CmdArgs.push_back(Elt: "-m");
600 CmdArgs.push_back(Elt: LDMOption);
601 } else {
602 D.Diag(DiagID: diag::err_target_unknown_triple) << Triple.str();
603 return;
604 }
605
606 if (Triple.isRISCV()) {
607 CmdArgs.push_back(Elt: "-X");
608 if (Args.hasArg(Ids: options::OPT_mno_relax))
609 CmdArgs.push_back(Elt: "--no-relax");
610 }
611
612 if (Triple.isARM() || Triple.isThumb()) {
613 bool IsBigEndian = arm::isARMBigEndian(Triple, Args);
614 if (IsBigEndian)
615 arm::appendBE8LinkFlag(Args, CmdArgs, Triple);
616 CmdArgs.push_back(Elt: IsBigEndian ? "-EB" : "-EL");
617 } else if (Triple.isAArch64()) {
618 CmdArgs.push_back(Elt: Arch == llvm::Triple::aarch64_be ? "-EB" : "-EL");
619 }
620
621 bool NeedCRTs =
622 !Args.hasArg(Ids: options::OPT_nostdlib, Ids: options::OPT_nostartfiles);
623
624 const char *CRTBegin, *CRTEnd;
625 if (NeedCRTs) {
626 if (!Args.hasArg(Ids: options::OPT_r))
627 CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "crt0.o")));
628 if (TC.hasValidGCCInstallation() || detectGCCToolchainAdjacent(D)) {
629 auto RuntimeLib = TC.GetRuntimeLibType(Args);
630 switch (RuntimeLib) {
631 case (ToolChain::RLT_Libgcc): {
632 CRTBegin = "crtbegin.o";
633 CRTEnd = "crtend.o";
634 break;
635 }
636 case (ToolChain::RLT_CompilerRT): {
637 CRTBegin =
638 TC.getCompilerRTArgString(Args, Component: "crtbegin", Type: ToolChain::FT_Object);
639 CRTEnd =
640 TC.getCompilerRTArgString(Args, Component: "crtend", Type: ToolChain::FT_Object);
641 break;
642 }
643 }
644 CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: CRTBegin)));
645 }
646 }
647
648 Args.addAllArgs(Output&: CmdArgs,
649 Ids: {options::OPT_L, options::OPT_u, options::OPT_T_Group,
650 options::OPT_s, options::OPT_t, options::OPT_r});
651
652 TC.AddFilePathLibArgs(Args, CmdArgs);
653
654 for (const auto &LibPath : TC.getLibraryPaths())
655 CmdArgs.push_back(Elt: Args.MakeArgString(Str: llvm::Twine("-L", LibPath)));
656
657 if (D.isUsingLTO())
658 addLTOOptions(ToolChain: TC, Args, CmdArgs, Output, Inputs,
659 IsThinLTO: D.getLTOMode() == LTOK_Thin);
660
661 AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
662
663 if (TC.ShouldLinkCXXStdlib(Args)) {
664 bool OnlyLibstdcxxStatic = Args.hasArg(Ids: options::OPT_static_libstdcxx) &&
665 !Args.hasArg(Ids: options::OPT_static);
666 if (OnlyLibstdcxxStatic)
667 CmdArgs.push_back(Elt: "-Bstatic");
668 TC.AddCXXStdlibLibArgs(Args, CmdArgs);
669 if (OnlyLibstdcxxStatic)
670 CmdArgs.push_back(Elt: "-Bdynamic");
671 CmdArgs.push_back(Elt: "-lm");
672 }
673
674 if (!Args.hasArg(Ids: options::OPT_nostdlib, Ids: options::OPT_nodefaultlibs)) {
675 CmdArgs.push_back(Elt: "--start-group");
676 AddRunTimeLibs(TC, D, CmdArgs, Args);
677 CmdArgs.push_back(Elt: "-lc");
678 if (TC.hasValidGCCInstallation() || detectGCCToolchainAdjacent(D))
679 CmdArgs.push_back(Elt: "-lgloss");
680 CmdArgs.push_back(Elt: "--end-group");
681 }
682
683 if ((TC.hasValidGCCInstallation() || detectGCCToolchainAdjacent(D)) &&
684 NeedCRTs)
685 CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: CRTEnd)));
686
687 if (TC.getTriple().isRISCV())
688 CmdArgs.push_back(Elt: "-X");
689
690 // The R_ARM_TARGET2 relocation must be treated as R_ARM_REL32 on arm*-*-elf
691 // and arm*-*-eabi (the default is R_ARM_GOT_PREL, used on arm*-*-linux and
692 // arm*-*-*bsd).
693 if (arm::isARMEABIBareMetal(Triple: TC.getTriple()))
694 CmdArgs.push_back(Elt: "--target2=rel");
695
696 CmdArgs.push_back(Elt: "-o");
697 CmdArgs.push_back(Elt: Output.getFilename());
698
699 C.addCommand(C: std::make_unique<Command>(
700 args: JA, args: *this, args: ResponseFileSupport::AtFileCurCP(),
701 args: Args.MakeArgString(Str: TC.GetLinkerPath()), args&: CmdArgs, args: Inputs, args: Output));
702}
703
704// BareMetal toolchain allows all sanitizers where the compiler generates valid
705// code, ignoring all runtime library support issues on the assumption that
706// baremetal targets typically implement their own runtime support.
707SanitizerMask BareMetal::getSupportedSanitizers() const {
708 const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64;
709 const bool IsAArch64 = getTriple().getArch() == llvm::Triple::aarch64 ||
710 getTriple().getArch() == llvm::Triple::aarch64_be;
711 const bool IsRISCV64 = getTriple().getArch() == llvm::Triple::riscv64;
712 SanitizerMask Res = ToolChain::getSupportedSanitizers();
713 Res |= SanitizerKind::Address;
714 Res |= SanitizerKind::KernelAddress;
715 Res |= SanitizerKind::PointerCompare;
716 Res |= SanitizerKind::PointerSubtract;
717 Res |= SanitizerKind::Fuzzer;
718 Res |= SanitizerKind::FuzzerNoLink;
719 Res |= SanitizerKind::Vptr;
720 Res |= SanitizerKind::SafeStack;
721 Res |= SanitizerKind::Thread;
722 Res |= SanitizerKind::Scudo;
723 if (IsX86_64 || IsAArch64 || IsRISCV64) {
724 Res |= SanitizerKind::HWAddress;
725 Res |= SanitizerKind::KernelHWAddress;
726 }
727 return Res;
728}
729
730SmallVector<std::string>
731BareMetal::getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const {
732 return MultilibMacroDefines;
733}
734