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/RISCV.h"
16 #include "clang/Driver/Compilation.h"
17 #include "clang/Driver/Driver.h"
18 #include "clang/Driver/DriverDiagnostic.h"
19 #include "clang/Driver/Options.h"
20 #include "llvm/Option/ArgList.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/VirtualFileSystem.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 using namespace llvm::opt;
26 using namespace clang;
27 using namespace clang::driver;
28 using namespace clang::driver::tools;
29 using namespace clang::driver::toolchains;
30 
31 static Multilib makeMultilib(StringRef commonSuffix) {
32   return Multilib(commonSuffix, commonSuffix, commonSuffix);
33 }
34 
35 static bool findRISCVMultilibs(const Driver &D,
36                                const llvm::Triple &TargetTriple,
37                                const ArgList &Args, DetectedMultilibs &Result) {
38   Multilib::flags_list Flags;
39   StringRef Arch = riscv::getRISCVArch(Args, TargetTriple);
40   StringRef Abi = tools::riscv::getRISCVABI(Args, TargetTriple);
41 
42   if (TargetTriple.isRISCV64()) {
43     Multilib Imac = makeMultilib("").flag("+march=rv64imac").flag("+mabi=lp64");
44     Multilib Imafdc = makeMultilib("/rv64imafdc/lp64d")
45                           .flag("+march=rv64imafdc")
46                           .flag("+mabi=lp64d");
47 
48     // Multilib reuse
49     bool UseImafdc =
50         (Arch == "rv64imafdc") || (Arch == "rv64gc"); // gc => imafdc
51 
52     addMultilibFlag((Arch == "rv64imac"), "march=rv64imac", Flags);
53     addMultilibFlag(UseImafdc, "march=rv64imafdc", Flags);
54     addMultilibFlag(Abi == "lp64", "mabi=lp64", Flags);
55     addMultilibFlag(Abi == "lp64d", "mabi=lp64d", Flags);
56 
57     Result.Multilibs = MultilibSet().Either(Imac, Imafdc);
58     return Result.Multilibs.select(Flags, Result.SelectedMultilib);
59   }
60   if (TargetTriple.isRISCV32()) {
61     Multilib Imac =
62         makeMultilib("").flag("+march=rv32imac").flag("+mabi=ilp32");
63     Multilib I =
64         makeMultilib("/rv32i/ilp32").flag("+march=rv32i").flag("+mabi=ilp32");
65     Multilib Im =
66         makeMultilib("/rv32im/ilp32").flag("+march=rv32im").flag("+mabi=ilp32");
67     Multilib Iac = makeMultilib("/rv32iac/ilp32")
68                        .flag("+march=rv32iac")
69                        .flag("+mabi=ilp32");
70     Multilib Imafc = makeMultilib("/rv32imafc/ilp32f")
71                          .flag("+march=rv32imafc")
72                          .flag("+mabi=ilp32f");
73 
74     // Multilib reuse
75     bool UseI = (Arch == "rv32i") || (Arch == "rv32ic");    // ic => i
76     bool UseIm = (Arch == "rv32im") || (Arch == "rv32imc"); // imc => im
77     bool UseImafc = (Arch == "rv32imafc") || (Arch == "rv32imafdc") ||
78                     (Arch == "rv32gc"); // imafdc,gc => imafc
79 
80     addMultilibFlag(UseI, "march=rv32i", Flags);
81     addMultilibFlag(UseIm, "march=rv32im", Flags);
82     addMultilibFlag((Arch == "rv32iac"), "march=rv32iac", Flags);
83     addMultilibFlag((Arch == "rv32imac"), "march=rv32imac", Flags);
84     addMultilibFlag(UseImafc, "march=rv32imafc", Flags);
85     addMultilibFlag(Abi == "ilp32", "mabi=ilp32", Flags);
86     addMultilibFlag(Abi == "ilp32f", "mabi=ilp32f", Flags);
87 
88     Result.Multilibs = MultilibSet().Either(I, Im, Iac, Imac, Imafc);
89     return Result.Multilibs.select(Flags, Result.SelectedMultilib);
90   }
91   return false;
92 }
93 
94 BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
95                      const ArgList &Args)
96     : ToolChain(D, Triple, Args) {
97   getProgramPaths().push_back(getDriver().getInstalledDir());
98   if (getDriver().getInstalledDir() != getDriver().Dir)
99     getProgramPaths().push_back(getDriver().Dir);
100 
101   findMultilibs(D, Triple, Args);
102   SmallString<128> SysRoot(computeSysRoot());
103   if (!SysRoot.empty()) {
104     llvm::sys::path::append(SysRoot, "lib");
105     getFilePaths().push_back(std::string(SysRoot));
106     getLibraryPaths().push_back(std::string(SysRoot));
107   }
108 }
109 
110 /// Is the triple {arm,thumb}-none-none-{eabi,eabihf} ?
111 static bool isARMBareMetal(const llvm::Triple &Triple) {
112   if (Triple.getArch() != llvm::Triple::arm &&
113       Triple.getArch() != llvm::Triple::thumb)
114     return false;
115 
116   if (Triple.getVendor() != llvm::Triple::UnknownVendor)
117     return false;
118 
119   if (Triple.getOS() != llvm::Triple::UnknownOS)
120     return false;
121 
122   if (Triple.getEnvironment() != llvm::Triple::EABI &&
123       Triple.getEnvironment() != llvm::Triple::EABIHF)
124     return false;
125 
126   return true;
127 }
128 
129 /// Is the triple aarch64-none-elf?
130 static bool isAArch64BareMetal(const llvm::Triple &Triple) {
131   if (Triple.getArch() != llvm::Triple::aarch64)
132     return false;
133 
134   if (Triple.getVendor() != llvm::Triple::UnknownVendor)
135     return false;
136 
137   if (Triple.getOS() != llvm::Triple::UnknownOS)
138     return false;
139 
140   return Triple.getEnvironmentName() == "elf";
141 }
142 
143 static bool isRISCVBareMetal(const llvm::Triple &Triple) {
144   if (!Triple.isRISCV())
145     return false;
146 
147   if (Triple.getVendor() != llvm::Triple::UnknownVendor)
148     return false;
149 
150   if (Triple.getOS() != llvm::Triple::UnknownOS)
151     return false;
152 
153   return Triple.getEnvironmentName() == "elf";
154 }
155 
156 void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,
157                               const ArgList &Args) {
158   DetectedMultilibs Result;
159   if (isRISCVBareMetal(Triple)) {
160     if (findRISCVMultilibs(D, Triple, Args, Result)) {
161       SelectedMultilib = Result.SelectedMultilib;
162       Multilibs = Result.Multilibs;
163     }
164   }
165 }
166 
167 bool BareMetal::handlesTarget(const llvm::Triple &Triple) {
168   return isARMBareMetal(Triple) || isAArch64BareMetal(Triple) ||
169          isRISCVBareMetal(Triple);
170 }
171 
172 Tool *BareMetal::buildLinker() const {
173   return new tools::baremetal::Linker(*this);
174 }
175 
176 std::string BareMetal::computeSysRoot() const {
177   if (!getDriver().SysRoot.empty())
178     return getDriver().SysRoot + SelectedMultilib.osSuffix();
179 
180   SmallString<128> SysRootDir;
181   llvm::sys::path::append(SysRootDir, getDriver().Dir, "../lib/clang-runtimes",
182                           getDriver().getTargetTriple());
183 
184   SysRootDir += SelectedMultilib.osSuffix();
185   return std::string(SysRootDir);
186 }
187 
188 void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
189                                           ArgStringList &CC1Args) const {
190   if (DriverArgs.hasArg(options::OPT_nostdinc))
191     return;
192 
193   if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
194     SmallString<128> Dir(getDriver().ResourceDir);
195     llvm::sys::path::append(Dir, "include");
196     addSystemInclude(DriverArgs, CC1Args, Dir.str());
197   }
198 
199   if (!DriverArgs.hasArg(options::OPT_nostdlibinc)) {
200     SmallString<128> Dir(computeSysRoot());
201     if (!Dir.empty()) {
202       llvm::sys::path::append(Dir, "include");
203       addSystemInclude(DriverArgs, CC1Args, Dir.str());
204     }
205   }
206 }
207 
208 void BareMetal::addClangTargetOptions(const ArgList &DriverArgs,
209                                       ArgStringList &CC1Args,
210                                       Action::OffloadKind) const {
211   CC1Args.push_back("-nostdsysteminc");
212 }
213 
214 void BareMetal::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
215                                              ArgStringList &CC1Args) const {
216   if (DriverArgs.hasArg(options::OPT_nostdinc) ||
217       DriverArgs.hasArg(options::OPT_nostdlibinc) ||
218       DriverArgs.hasArg(options::OPT_nostdincxx))
219     return;
220 
221   const Driver &D = getDriver();
222   std::string SysRoot(computeSysRoot());
223   if (SysRoot.empty())
224     return;
225 
226   switch (GetCXXStdlibType(DriverArgs)) {
227   case ToolChain::CST_Libcxx: {
228     // First check sysroot/usr/include/c++/v1 if it exists.
229     SmallString<128> TargetDir(SysRoot);
230     llvm::sys::path::append(TargetDir, "usr", "include", "c++", "v1");
231     if (D.getVFS().exists(TargetDir)) {
232       addSystemInclude(DriverArgs, CC1Args, TargetDir.str());
233       break;
234     }
235     // Add generic path if nothing else succeeded so far.
236     SmallString<128> Dir(SysRoot);
237     llvm::sys::path::append(Dir, "include", "c++", "v1");
238     addSystemInclude(DriverArgs, CC1Args, Dir.str());
239     break;
240   }
241   case ToolChain::CST_Libstdcxx: {
242     SmallString<128> Dir(SysRoot);
243     llvm::sys::path::append(Dir, "include", "c++");
244     std::error_code EC;
245     Generic_GCC::GCCVersion Version = {"", -1, -1, -1, "", "", ""};
246     // Walk the subdirs, and find the one with the newest gcc version:
247     for (llvm::vfs::directory_iterator LI = D.getVFS().dir_begin(Dir.str(), EC),
248                                        LE;
249          !EC && LI != LE; LI = LI.increment(EC)) {
250       StringRef VersionText = llvm::sys::path::filename(LI->path());
251       auto CandidateVersion = Generic_GCC::GCCVersion::Parse(VersionText);
252       if (CandidateVersion.Major == -1)
253         continue;
254       if (CandidateVersion <= Version)
255         continue;
256       Version = CandidateVersion;
257     }
258     if (Version.Major == -1)
259       return;
260     llvm::sys::path::append(Dir, Version.Text);
261     addSystemInclude(DriverArgs, CC1Args, Dir.str());
262     break;
263   }
264   }
265 }
266 
267 void BareMetal::AddCXXStdlibLibArgs(const ArgList &Args,
268                                     ArgStringList &CmdArgs) const {
269   switch (GetCXXStdlibType(Args)) {
270   case ToolChain::CST_Libcxx:
271     CmdArgs.push_back("-lc++");
272     if (Args.hasArg(options::OPT_fexperimental_library))
273       CmdArgs.push_back("-lc++experimental");
274     CmdArgs.push_back("-lc++abi");
275     break;
276   case ToolChain::CST_Libstdcxx:
277     CmdArgs.push_back("-lstdc++");
278     CmdArgs.push_back("-lsupc++");
279     break;
280   }
281   CmdArgs.push_back("-lunwind");
282 }
283 
284 void BareMetal::AddLinkRuntimeLib(const ArgList &Args,
285                                   ArgStringList &CmdArgs) const {
286   ToolChain::RuntimeLibType RLT = GetRuntimeLibType(Args);
287   switch (RLT) {
288   case ToolChain::RLT_CompilerRT: {
289     const std::string FileName = getCompilerRT(Args, "builtins");
290     llvm::StringRef BaseName = llvm::sys::path::filename(FileName);
291     BaseName.consume_front("lib");
292     BaseName.consume_back(".a");
293     CmdArgs.push_back(Args.MakeArgString("-l" + BaseName));
294     return;
295   }
296   case ToolChain::RLT_Libgcc:
297     CmdArgs.push_back("-lgcc");
298     return;
299   }
300   llvm_unreachable("Unhandled RuntimeLibType.");
301 }
302 
303 void baremetal::Linker::ConstructJob(Compilation &C, const JobAction &JA,
304                                      const InputInfo &Output,
305                                      const InputInfoList &Inputs,
306                                      const ArgList &Args,
307                                      const char *LinkingOutput) const {
308   ArgStringList CmdArgs;
309 
310   auto &TC = static_cast<const toolchains::BareMetal &>(getToolChain());
311 
312   AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
313 
314   CmdArgs.push_back("-Bstatic");
315 
316   Args.AddAllArgs(CmdArgs, {options::OPT_L, options::OPT_T_Group,
317                             options::OPT_e, options::OPT_s, options::OPT_t,
318                             options::OPT_Z_Flag, options::OPT_r});
319 
320   TC.AddFilePathLibArgs(Args, CmdArgs);
321 
322   for (const auto &LibPath : TC.getLibraryPaths())
323     CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-L", LibPath)));
324 
325   const std::string FileName = TC.getCompilerRT(Args, "builtins");
326   llvm::SmallString<128> PathBuf{FileName};
327   llvm::sys::path::remove_filename(PathBuf);
328   CmdArgs.push_back(Args.MakeArgString("-L" + PathBuf));
329 
330   if (TC.ShouldLinkCXXStdlib(Args))
331     TC.AddCXXStdlibLibArgs(Args, CmdArgs);
332 
333   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
334     CmdArgs.push_back("-lc");
335     CmdArgs.push_back("-lm");
336 
337     TC.AddLinkRuntimeLib(Args, CmdArgs);
338   }
339 
340   CmdArgs.push_back("-o");
341   CmdArgs.push_back(Output.getFilename());
342 
343   C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
344                                          Args.MakeArgString(TC.GetLinkerPath()),
345                                          CmdArgs, Inputs, Output));
346 }
347