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