xref: /freebsd/contrib/llvm-project/clang/lib/Driver/ToolChains/BareMetal.cpp (revision 1db9f3b21e39176dd5b67cf8ac378633b172463e)
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