1 //===-- BareMetal.cpp - Bare Metal ToolChain --------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
11 #include "CommonArgs.h"
13 #include "clang/Driver/InputInfo.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"
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
;
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
);
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")
52 (Arch
== "rv64imafdc") || (Arch
== "rv64gc"); // gc => imafdc
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
);
60 MultilibSetBuilder().Either(Imac
, Imafdc
).makeMultilibSet();
61 return Result
.Multilibs
.select(Flags
, Result
.SelectedMultilibs
);
63 if (TargetTriple
.isRISCV32()) {
64 MultilibBuilder Imac
=
65 MultilibBuilder().flag("-march=rv32imac").flag("-mabi=ilp32");
66 MultilibBuilder I
= MultilibBuilder("/rv32i/ilp32")
69 MultilibBuilder Im
= MultilibBuilder("/rv32im/ilp32")
70 .flag("-march=rv32im")
72 MultilibBuilder Iac
= MultilibBuilder("/rv32iac/ilp32")
73 .flag("-march=rv32iac")
75 MultilibBuilder Imafc
= MultilibBuilder("/rv32imafc/ilp32f")
76 .flag("-march=rv32imafc")
77 .flag("-mabi=ilp32f");
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
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
);
94 MultilibSetBuilder().Either(I
, Im
, Iac
, Imac
, Imafc
).makeMultilibSet();
95 return Result
.Multilibs
.select(Flags
, Result
.SelectedMultilibs
);
100 BareMetal::BareMetal(const Driver
&D
, const llvm::Triple
&Triple
,
102 : ToolChain(D
, Triple
, Args
) {
103 getProgramPaths().push_back(getDriver().getInstalledDir());
104 if (getDriver().getInstalledDir() != getDriver().Dir
)
105 getProgramPaths().push_back(getDriver().Dir
);
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
));
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
)
127 if (Triple
.getVendor() != llvm::Triple::UnknownVendor
)
130 if (Triple
.getOS() != llvm::Triple::UnknownOS
)
133 if (Triple
.getEnvironment() != llvm::Triple::EABI
&&
134 Triple
.getEnvironment() != llvm::Triple::EABIHF
)
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
)
146 if (Triple
.getVendor() != llvm::Triple::UnknownVendor
)
149 if (Triple
.getOS() != llvm::Triple::UnknownOS
)
152 return Triple
.getEnvironmentName() == "elf";
155 static bool isRISCVBareMetal(const llvm::Triple
&Triple
) {
156 if (!Triple
.isRISCV())
159 if (Triple
.getVendor() != llvm::Triple::UnknownVendor
)
162 if (Triple
.getOS() != llvm::Triple::UnknownOS
)
165 return Triple
.getEnvironmentName() == "elf";
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
;
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
);
181 Multilib::flags_list Flags
= TC
.getMultilibFlags(Args
);
182 llvm::ErrorOr
<MultilibSet
> ErrorOrMultilibSet
=
183 MultilibSet::parseYaml(*MB
.get());
184 if (ErrorOrMultilibSet
.getError())
186 Result
.Multilibs
= ErrorOrMultilibSet
.get();
187 if (Result
.Multilibs
.select(Flags
, Result
.SelectedMultilibs
))
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();
196 static constexpr llvm::StringLiteral MultilibFilename
= "multilib.yaml";
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())
204 SmallString
<128> SysRootDir(D
.Dir
);
205 llvm::sys::path::append(SysRootDir
, "..", "lib", "clang-runtimes");
207 SmallString
<128> MultilibPath(SysRootDir
);
208 llvm::sys::path::append(MultilibPath
, MultilibFilename
);
210 // New behaviour: if multilib.yaml is found then use clang-runtimes as the
212 if (D
.getVFS().exists(MultilibPath
))
213 return std::string(SysRootDir
);
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
);
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
;
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
;
237 bool BareMetal::handlesTarget(const llvm::Triple
&Triple
) {
238 return isARMBareMetal(Triple
) || isAArch64BareMetal(Triple
) ||
239 isRISCVBareMetal(Triple
) || isPPCBareMetal(Triple
);
242 Tool
*BareMetal::buildLinker() const {
243 return new tools::baremetal::Linker(*this);
246 Tool
*BareMetal::buildStaticLibTool() const {
247 return new tools::baremetal::StaticLibTool(*this);
250 std::string
BareMetal::computeSysRoot() const {
251 return computeBaseSysRoot(getDriver(), getTriple());
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
);
259 // No multilibs selected so return a single default multilib.
260 static const llvm::SmallVector
<Multilib
> Default
= {Multilib()};
261 return llvm::reverse(Default
);
264 void BareMetal::AddClangSystemIncludeArgs(const ArgList
&DriverArgs
,
265 ArgStringList
&CC1Args
) const {
266 if (DriverArgs
.hasArg(options::OPT_nostdinc
))
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());
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());
288 void BareMetal::addClangTargetOptions(const ArgList
&DriverArgs
,
289 ArgStringList
&CC1Args
,
290 Action::OffloadKind
) const {
291 CC1Args
.push_back("-nostdsysteminc");
294 void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList
&DriverArgs
,
295 ArgStringList
&CC1Args
) const {
296 if (DriverArgs
.hasArg(options::OPT_nostdinc
) ||
297 DriverArgs
.hasArg(options::OPT_nostdlibinc
) ||
298 DriverArgs
.hasArg(options::OPT_nostdincxx
))
301 const Driver
&D
= getDriver();
302 std::string
SysRoot(computeSysRoot());
306 for (const Multilib
&M
: getOrderedMultilibs()) {
307 SmallString
<128> Dir(SysRoot
);
308 llvm::sys::path::append(Dir
, M
.gccSuffix());
309 switch (GetCXXStdlibType(DriverArgs
)) {
310 case ToolChain::CST_Libcxx
: {
311 // First check sysroot/usr/include/c++/v1 if it exists.
312 SmallString
<128> TargetDir(Dir
);
313 llvm::sys::path::append(TargetDir
, "usr", "include", "c++", "v1");
314 if (D
.getVFS().exists(TargetDir
)) {
315 addSystemInclude(DriverArgs
, CC1Args
, TargetDir
.str());
318 // Add generic path if nothing else succeeded so far.
319 llvm::sys::path::append(Dir
, "include", "c++", "v1");
320 addSystemInclude(DriverArgs
, CC1Args
, Dir
.str());
323 case ToolChain::CST_Libstdcxx
: {
324 llvm::sys::path::append(Dir
, "include", "c++");
326 Generic_GCC::GCCVersion Version
= {"", -1, -1, -1, "", "", ""};
327 // Walk the subdirs, and find the one with the newest gcc version:
328 for (llvm::vfs::directory_iterator
329 LI
= D
.getVFS().dir_begin(Dir
.str(), EC
),
331 !EC
&& LI
!= LE
; LI
= LI
.increment(EC
)) {
332 StringRef VersionText
= llvm::sys::path::filename(LI
->path());
333 auto CandidateVersion
= Generic_GCC::GCCVersion::Parse(VersionText
);
334 if (CandidateVersion
.Major
== -1)
336 if (CandidateVersion
<= Version
)
338 Version
= CandidateVersion
;
340 if (Version
.Major
!= -1) {
341 llvm::sys::path::append(Dir
, Version
.Text
);
342 addSystemInclude(DriverArgs
, CC1Args
, Dir
.str());
350 void BareMetal::AddCXXStdlibLibArgs(const ArgList
&Args
,
351 ArgStringList
&CmdArgs
) const {
352 switch (GetCXXStdlibType(Args
)) {
353 case ToolChain::CST_Libcxx
:
354 CmdArgs
.push_back("-lc++");
355 if (Args
.hasArg(options::OPT_fexperimental_library
))
356 CmdArgs
.push_back("-lc++experimental");
357 CmdArgs
.push_back("-lc++abi");
359 case ToolChain::CST_Libstdcxx
:
360 CmdArgs
.push_back("-lstdc++");
361 CmdArgs
.push_back("-lsupc++");
364 CmdArgs
.push_back("-lunwind");
367 void BareMetal::AddLinkRuntimeLib(const ArgList
&Args
,
368 ArgStringList
&CmdArgs
) const {
369 ToolChain::RuntimeLibType RLT
= GetRuntimeLibType(Args
);
371 case ToolChain::RLT_CompilerRT
: {
372 const std::string FileName
= getCompilerRT(Args
, "builtins");
373 llvm::StringRef BaseName
= llvm::sys::path::filename(FileName
);
374 BaseName
.consume_front("lib");
375 BaseName
.consume_back(".a");
376 CmdArgs
.push_back(Args
.MakeArgString("-l" + BaseName
));
379 case ToolChain::RLT_Libgcc
:
380 CmdArgs
.push_back("-lgcc");
383 llvm_unreachable("Unhandled RuntimeLibType.");
386 void baremetal::StaticLibTool::ConstructJob(Compilation
&C
, const JobAction
&JA
,
387 const InputInfo
&Output
,
388 const InputInfoList
&Inputs
,
390 const char *LinkingOutput
) const {
391 const Driver
&D
= getToolChain().getDriver();
393 // Silence warning for "clang -g foo.o -o foo"
394 Args
.ClaimAllArgs(options::OPT_g_Group
);
395 // and "clang -emit-llvm foo.o -o foo"
396 Args
.ClaimAllArgs(options::OPT_emit_llvm
);
397 // and for "clang -w foo.o -o foo". Other warning options are already
398 // handled somewhere else.
399 Args
.ClaimAllArgs(options::OPT_w
);
400 // Silence warnings when linking C code with a C++ '-stdlib' argument.
401 Args
.ClaimAllArgs(options::OPT_stdlib_EQ
);
403 // ar tool command "llvm-ar <options> <output_file> <input_files>".
404 ArgStringList CmdArgs
;
405 // Create and insert file members with a deterministic index.
406 CmdArgs
.push_back("rcsD");
407 CmdArgs
.push_back(Output
.getFilename());
409 for (const auto &II
: Inputs
) {
410 if (II
.isFilename()) {
411 CmdArgs
.push_back(II
.getFilename());
415 // Delete old output archive file if it already exists before generating a new
417 const char *OutputFileName
= Output
.getFilename();
418 if (Output
.isFilename() && llvm::sys::fs::exists(OutputFileName
)) {
419 if (std::error_code EC
= llvm::sys::fs::remove(OutputFileName
)) {
420 D
.Diag(diag::err_drv_unable_to_remove_file
) << EC
.message();
425 const char *Exec
= Args
.MakeArgString(getToolChain().GetStaticLibToolPath());
426 C
.addCommand(std::make_unique
<Command
>(JA
, *this,
427 ResponseFileSupport::AtFileCurCP(),
428 Exec
, CmdArgs
, Inputs
, Output
));
431 void baremetal::Linker::ConstructJob(Compilation
&C
, const JobAction
&JA
,
432 const InputInfo
&Output
,
433 const InputInfoList
&Inputs
,
435 const char *LinkingOutput
) const {
436 ArgStringList CmdArgs
;
438 auto &TC
= static_cast<const toolchains::BareMetal
&>(getToolChain());
439 const llvm::Triple::ArchType Arch
= TC
.getArch();
440 const llvm::Triple
&Triple
= getToolChain().getEffectiveTriple();
442 AddLinkerInputs(TC
, Inputs
, Args
, CmdArgs
, JA
);
444 CmdArgs
.push_back("-Bstatic");
446 if (Triple
.isARM() || Triple
.isThumb()) {
447 bool IsBigEndian
= arm::isARMBigEndian(Triple
, Args
);
449 arm::appendBE8LinkFlag(Args
, CmdArgs
, Triple
);
450 CmdArgs
.push_back(IsBigEndian
? "-EB" : "-EL");
451 } else if (Triple
.isAArch64()) {
452 CmdArgs
.push_back(Arch
== llvm::Triple::aarch64_be
? "-EB" : "-EL");
455 Args
.addAllArgs(CmdArgs
, {options::OPT_L
, options::OPT_T_Group
,
456 options::OPT_s
, options::OPT_t
, options::OPT_r
});
458 TC
.AddFilePathLibArgs(Args
, CmdArgs
);
460 for (const auto &LibPath
: TC
.getLibraryPaths())
461 CmdArgs
.push_back(Args
.MakeArgString(llvm::Twine("-L", LibPath
)));
463 const std::string FileName
= TC
.getCompilerRT(Args
, "builtins");
464 llvm::SmallString
<128> PathBuf
{FileName
};
465 llvm::sys::path::remove_filename(PathBuf
);
466 CmdArgs
.push_back(Args
.MakeArgString("-L" + PathBuf
));
468 if (TC
.ShouldLinkCXXStdlib(Args
))
469 TC
.AddCXXStdlibLibArgs(Args
, CmdArgs
);
471 if (!Args
.hasArg(options::OPT_nostdlib
, options::OPT_nodefaultlibs
)) {
472 CmdArgs
.push_back("-lc");
473 CmdArgs
.push_back("-lm");
475 TC
.AddLinkRuntimeLib(Args
, CmdArgs
);
478 if (TC
.getTriple().isRISCV())
479 CmdArgs
.push_back("-X");
481 // The R_ARM_TARGET2 relocation must be treated as R_ARM_REL32 on arm*-*-elf
482 // and arm*-*-eabi (the default is R_ARM_GOT_PREL, used on arm*-*-linux and
484 if (isARMBareMetal(TC
.getTriple()))
485 CmdArgs
.push_back("--target2=rel");
487 CmdArgs
.push_back("-o");
488 CmdArgs
.push_back(Output
.getFilename());
490 C
.addCommand(std::make_unique
<Command
>(
491 JA
, *this, ResponseFileSupport::AtFileCurCP(),
492 Args
.MakeArgString(TC
.GetLinkerPath()), CmdArgs
, Inputs
, Output
));