1 | //===--- MinGW.cpp - MinGWToolChain Implementation ------------------------===// |
---|---|
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 "MinGW.h" |
10 | #include "clang/Config/config.h" |
11 | #include "clang/Driver/CommonArgs.h" |
12 | #include "clang/Driver/Compilation.h" |
13 | #include "clang/Driver/Driver.h" |
14 | #include "clang/Driver/InputInfo.h" |
15 | #include "clang/Driver/Options.h" |
16 | #include "clang/Driver/SanitizerArgs.h" |
17 | #include "llvm/Config/llvm-config.h" // for LLVM_HOST_TRIPLE |
18 | #include "llvm/Option/ArgList.h" |
19 | #include "llvm/Support/FileSystem.h" |
20 | #include "llvm/Support/Path.h" |
21 | #include "llvm/Support/VirtualFileSystem.h" |
22 | #include <system_error> |
23 | |
24 | using namespace clang::diag; |
25 | using namespace clang::driver; |
26 | using namespace clang; |
27 | using namespace llvm::opt; |
28 | |
29 | /// MinGW Tools |
30 | void tools::MinGW::Assembler::ConstructJob(Compilation &C, const JobAction &JA, |
31 | const InputInfo &Output, |
32 | const InputInfoList &Inputs, |
33 | const ArgList &Args, |
34 | const char *LinkingOutput) const { |
35 | claimNoWarnArgs(Args); |
36 | ArgStringList CmdArgs; |
37 | |
38 | if (getToolChain().getArch() == llvm::Triple::x86) { |
39 | CmdArgs.push_back(Elt: "--32"); |
40 | } else if (getToolChain().getArch() == llvm::Triple::x86_64) { |
41 | CmdArgs.push_back(Elt: "--64"); |
42 | } |
43 | |
44 | Args.AddAllArgValues(Output&: CmdArgs, options::Id0: OPT_Wa_COMMA, options::Id1: OPT_Xassembler); |
45 | |
46 | CmdArgs.push_back(Elt: "-o"); |
47 | CmdArgs.push_back(Elt: Output.getFilename()); |
48 | |
49 | for (const auto &II : Inputs) |
50 | CmdArgs.push_back(Elt: II.getFilename()); |
51 | |
52 | const char *Exec = Args.MakeArgString(Str: getToolChain().GetProgramPath(Name: "as")); |
53 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(), |
54 | args&: Exec, args&: CmdArgs, args: Inputs, args: Output)); |
55 | |
56 | if (Args.hasArg(options::OPT_gsplit_dwarf)) |
57 | SplitDebugInfo(TC: getToolChain(), C, T: *this, JA, Args, Output, |
58 | OutFile: SplitDebugName(JA, Args, Input: Inputs[0], Output)); |
59 | } |
60 | |
61 | void tools::MinGW::Linker::AddLibGCC(const ArgList &Args, |
62 | ArgStringList &CmdArgs) const { |
63 | if (Args.hasArg(options::OPT_mthreads)) |
64 | CmdArgs.push_back(Elt: "-lmingwthrd"); |
65 | CmdArgs.push_back(Elt: "-lmingw32"); |
66 | |
67 | // Make use of compiler-rt if --rtlib option is used |
68 | ToolChain::RuntimeLibType RLT = getToolChain().GetRuntimeLibType(Args); |
69 | if (RLT == ToolChain::RLT_Libgcc) { |
70 | bool Static = Args.hasArg(options::OPT_static_libgcc) || |
71 | Args.hasArg(options::OPT_static); |
72 | bool Shared = Args.hasArg(options::OPT_shared); |
73 | bool CXX = getToolChain().getDriver().CCCIsCXX(); |
74 | |
75 | if (Static || (!CXX && !Shared)) { |
76 | CmdArgs.push_back(Elt: "-lgcc"); |
77 | CmdArgs.push_back(Elt: "-lgcc_eh"); |
78 | } else { |
79 | CmdArgs.push_back(Elt: "-lgcc_s"); |
80 | CmdArgs.push_back(Elt: "-lgcc"); |
81 | } |
82 | } else { |
83 | AddRunTimeLibs(TC: getToolChain(), D: getToolChain().getDriver(), CmdArgs, Args); |
84 | } |
85 | |
86 | CmdArgs.push_back(Elt: "-lmoldname"); |
87 | CmdArgs.push_back(Elt: "-lmingwex"); |
88 | for (auto Lib : Args.getAllArgValues(options::OPT_l)) |
89 | if (StringRef(Lib).starts_with("msvcr") || |
90 | StringRef(Lib).starts_with("ucrt") || |
91 | StringRef(Lib).starts_with("crtdll")) |
92 | return; |
93 | CmdArgs.push_back(Elt: "-lmsvcrt"); |
94 | } |
95 | |
96 | void tools::MinGW::Linker::ConstructJob(Compilation &C, const JobAction &JA, |
97 | const InputInfo &Output, |
98 | const InputInfoList &Inputs, |
99 | const ArgList &Args, |
100 | const char *LinkingOutput) const { |
101 | const ToolChain &TC = getToolChain(); |
102 | const Driver &D = TC.getDriver(); |
103 | const SanitizerArgs &Sanitize = TC.getSanitizerArgs(JobArgs: Args); |
104 | |
105 | ArgStringList CmdArgs; |
106 | |
107 | // Silence warning for "clang -g foo.o -o foo" |
108 | Args.ClaimAllArgs(options::OPT_g_Group); |
109 | // and "clang -emit-llvm foo.o -o foo" |
110 | Args.ClaimAllArgs(options::OPT_emit_llvm); |
111 | // and for "clang -w foo.o -o foo". Other warning options are already |
112 | // handled somewhere else. |
113 | Args.ClaimAllArgs(options::OPT_w); |
114 | |
115 | if (!D.SysRoot.empty()) |
116 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "--sysroot="+ D.SysRoot)); |
117 | |
118 | if (Args.hasArg(options::OPT_s)) |
119 | CmdArgs.push_back(Elt: "-s"); |
120 | |
121 | CmdArgs.push_back(Elt: "-m"); |
122 | switch (TC.getArch()) { |
123 | case llvm::Triple::x86: |
124 | CmdArgs.push_back(Elt: "i386pe"); |
125 | break; |
126 | case llvm::Triple::x86_64: |
127 | CmdArgs.push_back(Elt: "i386pep"); |
128 | break; |
129 | case llvm::Triple::arm: |
130 | case llvm::Triple::thumb: |
131 | // FIXME: this is incorrect for WinCE |
132 | CmdArgs.push_back(Elt: "thumb2pe"); |
133 | break; |
134 | case llvm::Triple::aarch64: |
135 | if (TC.getEffectiveTriple().isWindowsArm64EC()) |
136 | CmdArgs.push_back(Elt: "arm64ecpe"); |
137 | else |
138 | CmdArgs.push_back(Elt: "arm64pe"); |
139 | break; |
140 | case llvm::Triple::mipsel: |
141 | CmdArgs.push_back(Elt: "mipspe"); |
142 | break; |
143 | default: |
144 | D.Diag(diag::DiagID: err_target_unknown_triple) << TC.getEffectiveTriple().str(); |
145 | } |
146 | |
147 | Arg *SubsysArg = |
148 | Args.getLastArg(options::OPT_mwindows, options::OPT_mconsole); |
149 | if (SubsysArg && SubsysArg->getOption().matches(options::ID: OPT_mwindows)) { |
150 | CmdArgs.push_back(Elt: "--subsystem"); |
151 | CmdArgs.push_back(Elt: "windows"); |
152 | } else if (SubsysArg && |
153 | SubsysArg->getOption().matches(options::ID: OPT_mconsole)) { |
154 | CmdArgs.push_back(Elt: "--subsystem"); |
155 | CmdArgs.push_back(Elt: "console"); |
156 | } |
157 | |
158 | if (Args.hasArg(options::OPT_mdll)) |
159 | CmdArgs.push_back(Elt: "--dll"); |
160 | else if (Args.hasArg(options::OPT_shared)) |
161 | CmdArgs.push_back(Elt: "--shared"); |
162 | if (Args.hasArg(options::OPT_static)) |
163 | CmdArgs.push_back(Elt: "-Bstatic"); |
164 | else |
165 | CmdArgs.push_back(Elt: "-Bdynamic"); |
166 | if (Args.hasArg(options::OPT_mdll) || Args.hasArg(options::OPT_shared)) { |
167 | CmdArgs.push_back(Elt: "-e"); |
168 | if (TC.getArch() == llvm::Triple::x86) |
169 | CmdArgs.push_back(Elt: "_DllMainCRTStartup@12"); |
170 | else |
171 | CmdArgs.push_back(Elt: "DllMainCRTStartup"); |
172 | CmdArgs.push_back(Elt: "--enable-auto-image-base"); |
173 | } |
174 | |
175 | if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) |
176 | CmdArgs.push_back(Elt: "--no-demangle"); |
177 | |
178 | if (!Args.hasFlag(options::OPT_fauto_import, options::OPT_fno_auto_import, |
179 | true)) |
180 | CmdArgs.push_back(Elt: "--disable-auto-import"); |
181 | |
182 | if (Arg *A = Args.getLastArg(options::OPT_mguard_EQ)) { |
183 | StringRef GuardArgs = A->getValue(); |
184 | if (GuardArgs == "none") |
185 | CmdArgs.push_back(Elt: "--no-guard-cf"); |
186 | else if (GuardArgs == "cf"|| GuardArgs == "cf-nochecks") |
187 | CmdArgs.push_back(Elt: "--guard-cf"); |
188 | else |
189 | D.Diag(diag::DiagID: err_drv_unsupported_option_argument) |
190 | << A->getSpelling() << GuardArgs; |
191 | } |
192 | |
193 | if (Args.hasArg(options::OPT_fms_hotpatch)) |
194 | CmdArgs.push_back(Elt: "--functionpadmin"); |
195 | |
196 | CmdArgs.push_back(Elt: "-o"); |
197 | const char *OutputFile = Output.getFilename(); |
198 | // GCC implicitly adds an .exe extension if it is given an output file name |
199 | // that lacks an extension. |
200 | // GCC used to do this only when the compiler itself runs on windows, but |
201 | // since GCC 8 it does the same when cross compiling as well. |
202 | if (!llvm::sys::path::has_extension(path: OutputFile)) { |
203 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Twine(OutputFile) + ".exe")); |
204 | OutputFile = CmdArgs.back(); |
205 | } else |
206 | CmdArgs.push_back(Elt: OutputFile); |
207 | |
208 | // FIXME: add -N, -n flags |
209 | Args.AddLastArg(CmdArgs, options::OPT_r); |
210 | Args.AddLastArg(CmdArgs, options::OPT_s); |
211 | Args.AddLastArg(CmdArgs, options::OPT_t); |
212 | Args.AddAllArgs(Output&: CmdArgs, options::Id0: OPT_u_Group); |
213 | |
214 | // Add asan_dynamic as the first import lib before other libs. This allows |
215 | // asan to be initialized as early as possible to increase its instrumentation |
216 | // coverage to include other user DLLs which has not been built with asan. |
217 | if (Sanitize.needsAsanRt() && !Args.hasArg(options::OPT_nostdlib) && |
218 | !Args.hasArg(options::OPT_nodefaultlibs)) { |
219 | // MinGW always links against a shared MSVCRT. |
220 | CmdArgs.push_back( |
221 | Elt: TC.getCompilerRTArgString(Args, Component: "asan_dynamic", Type: ToolChain::FT_Shared)); |
222 | } |
223 | |
224 | if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { |
225 | if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_mdll)) { |
226 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "dllcrt2.o"))); |
227 | } else { |
228 | if (Args.hasArg(options::OPT_municode)) |
229 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "crt2u.o"))); |
230 | else |
231 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "crt2.o"))); |
232 | } |
233 | if (Args.hasArg(options::OPT_pg)) |
234 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "gcrt2.o"))); |
235 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "crtbegin.o"))); |
236 | } |
237 | |
238 | Args.AddAllArgs(Output&: CmdArgs, options::Id0: OPT_L); |
239 | TC.AddFilePathLibArgs(Args, CmdArgs); |
240 | |
241 | // Add the compiler-rt library directories if they exist to help |
242 | // the linker find the various sanitizer, builtin, and profiling runtimes. |
243 | for (const auto &LibPath : TC.getLibraryPaths()) { |
244 | if (TC.getVFS().exists(Path: LibPath)) |
245 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-L"+ LibPath)); |
246 | } |
247 | auto CRTPath = TC.getCompilerRTPath(); |
248 | if (TC.getVFS().exists(Path: CRTPath)) |
249 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-L"+ CRTPath)); |
250 | |
251 | AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); |
252 | |
253 | if (D.isUsingLTO()) |
254 | addLTOOptions(ToolChain: TC, Args, CmdArgs, Output, Inputs, |
255 | IsThinLTO: D.getLTOMode() == LTOK_Thin); |
256 | |
257 | if (C.getDriver().IsFlangMode() && |
258 | !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { |
259 | TC.addFortranRuntimeLibraryPath(Args, CmdArgs); |
260 | TC.addFortranRuntimeLibs(Args, CmdArgs); |
261 | } |
262 | |
263 | // TODO: Add profile stuff here |
264 | |
265 | if (TC.ShouldLinkCXXStdlib(Args)) { |
266 | bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) && |
267 | !Args.hasArg(options::OPT_static); |
268 | if (OnlyLibstdcxxStatic) |
269 | CmdArgs.push_back(Elt: "-Bstatic"); |
270 | TC.AddCXXStdlibLibArgs(Args, CmdArgs); |
271 | if (OnlyLibstdcxxStatic) |
272 | CmdArgs.push_back(Elt: "-Bdynamic"); |
273 | } |
274 | |
275 | bool HasWindowsApp = false; |
276 | for (auto Lib : Args.getAllArgValues(options::OPT_l)) { |
277 | if (Lib == "windowsapp") { |
278 | HasWindowsApp = true; |
279 | break; |
280 | } |
281 | } |
282 | |
283 | if (!Args.hasArg(options::OPT_nostdlib)) { |
284 | if (!Args.hasArg(options::OPT_nodefaultlibs)) { |
285 | if (Args.hasArg(options::OPT_static)) |
286 | CmdArgs.push_back(Elt: "--start-group"); |
287 | |
288 | if (Args.hasArg(options::OPT_fstack_protector) || |
289 | Args.hasArg(options::OPT_fstack_protector_strong) || |
290 | Args.hasArg(options::OPT_fstack_protector_all)) { |
291 | CmdArgs.push_back(Elt: "-lssp_nonshared"); |
292 | CmdArgs.push_back(Elt: "-lssp"); |
293 | } |
294 | |
295 | if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, |
296 | options::OPT_fno_openmp, false)) { |
297 | switch (TC.getDriver().getOpenMPRuntime(Args)) { |
298 | case Driver::OMPRT_OMP: |
299 | CmdArgs.push_back(Elt: "-lomp"); |
300 | break; |
301 | case Driver::OMPRT_IOMP5: |
302 | CmdArgs.push_back(Elt: "-liomp5md"); |
303 | break; |
304 | case Driver::OMPRT_GOMP: |
305 | CmdArgs.push_back(Elt: "-lgomp"); |
306 | break; |
307 | case Driver::OMPRT_Unknown: |
308 | // Already diagnosed. |
309 | break; |
310 | } |
311 | } |
312 | |
313 | AddLibGCC(Args, CmdArgs); |
314 | |
315 | if (Args.hasArg(options::OPT_pg)) |
316 | CmdArgs.push_back(Elt: "-lgmon"); |
317 | |
318 | if (Args.hasArg(options::OPT_pthread)) |
319 | CmdArgs.push_back(Elt: "-lpthread"); |
320 | |
321 | if (Sanitize.needsAsanRt()) { |
322 | // MinGW always links against a shared MSVCRT. |
323 | CmdArgs.push_back(Elt: TC.getCompilerRTArgString(Args, Component: "asan_dynamic", |
324 | Type: ToolChain::FT_Shared)); |
325 | CmdArgs.push_back( |
326 | Elt: TC.getCompilerRTArgString(Args, Component: "asan_dynamic_runtime_thunk")); |
327 | CmdArgs.push_back(Elt: "--require-defined"); |
328 | CmdArgs.push_back(Elt: TC.getArch() == llvm::Triple::x86 |
329 | ? "___asan_seh_interceptor" |
330 | : "__asan_seh_interceptor"); |
331 | // Make sure the linker consider all object files from the dynamic |
332 | // runtime thunk. |
333 | CmdArgs.push_back(Elt: "--whole-archive"); |
334 | CmdArgs.push_back( |
335 | Elt: TC.getCompilerRTArgString(Args, Component: "asan_dynamic_runtime_thunk")); |
336 | CmdArgs.push_back(Elt: "--no-whole-archive"); |
337 | } |
338 | |
339 | TC.addProfileRTLibs(Args, CmdArgs); |
340 | |
341 | if (!HasWindowsApp) { |
342 | // Add system libraries. If linking to libwindowsapp.a, that import |
343 | // library replaces all these and we shouldn't accidentally try to |
344 | // link to the normal desktop mode dlls. |
345 | if (Args.hasArg(options::OPT_mwindows)) { |
346 | CmdArgs.push_back(Elt: "-lgdi32"); |
347 | CmdArgs.push_back(Elt: "-lcomdlg32"); |
348 | } |
349 | CmdArgs.push_back(Elt: "-ladvapi32"); |
350 | CmdArgs.push_back(Elt: "-lshell32"); |
351 | CmdArgs.push_back(Elt: "-luser32"); |
352 | CmdArgs.push_back(Elt: "-lkernel32"); |
353 | } |
354 | |
355 | if (Args.hasArg(options::OPT_static)) { |
356 | CmdArgs.push_back(Elt: "--end-group"); |
357 | } else { |
358 | AddLibGCC(Args, CmdArgs); |
359 | if (!HasWindowsApp) |
360 | CmdArgs.push_back(Elt: "-lkernel32"); |
361 | } |
362 | } |
363 | |
364 | if (!Args.hasArg(options::OPT_nostartfiles)) { |
365 | // Add crtfastmath.o if available and fast math is enabled. |
366 | TC.addFastMathRuntimeIfAvailable(Args, CmdArgs); |
367 | |
368 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: TC.GetFilePath(Name: "crtend.o"))); |
369 | } |
370 | } |
371 | const char *Exec = Args.MakeArgString(Str: TC.GetLinkerPath()); |
372 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, |
373 | args: ResponseFileSupport::AtFileUTF8(), |
374 | args&: Exec, args&: CmdArgs, args: Inputs, args: Output)); |
375 | } |
376 | |
377 | static bool isCrossCompiling(const llvm::Triple &T, bool RequireArchMatch) { |
378 | llvm::Triple HostTriple(llvm::Triple::normalize(LLVM_HOST_TRIPLE)); |
379 | if (HostTriple.getOS() != llvm::Triple::Win32) |
380 | return true; |
381 | if (RequireArchMatch && HostTriple.getArch() != T.getArch()) |
382 | return true; |
383 | return false; |
384 | } |
385 | |
386 | // Simplified from Generic_GCC::GCCInstallationDetector::ScanLibDirForGCCTriple. |
387 | static bool findGccVersion(StringRef LibDir, std::string &GccLibDir, |
388 | std::string &Ver, |
389 | toolchains::Generic_GCC::GCCVersion &Version) { |
390 | Version = toolchains::Generic_GCC::GCCVersion::Parse(VersionText: "0.0.0"); |
391 | std::error_code EC; |
392 | for (llvm::sys::fs::directory_iterator LI(LibDir, EC), LE; !EC && LI != LE; |
393 | LI = LI.increment(ec&: EC)) { |
394 | StringRef VersionText = llvm::sys::path::filename(path: LI->path()); |
395 | auto CandidateVersion = |
396 | toolchains::Generic_GCC::GCCVersion::Parse(VersionText); |
397 | if (CandidateVersion.Major == -1) |
398 | continue; |
399 | if (CandidateVersion <= Version) |
400 | continue; |
401 | Version = CandidateVersion; |
402 | Ver = std::string(VersionText); |
403 | GccLibDir = LI->path(); |
404 | } |
405 | return Ver.size(); |
406 | } |
407 | |
408 | static llvm::Triple getLiteralTriple(const Driver &D, const llvm::Triple &T) { |
409 | llvm::Triple LiteralTriple(D.getTargetTriple()); |
410 | // The arch portion of the triple may be overridden by -m32/-m64. |
411 | LiteralTriple.setArchName(T.getArchName()); |
412 | return LiteralTriple; |
413 | } |
414 | |
415 | void toolchains::MinGW::findGccLibDir(const llvm::Triple &LiteralTriple) { |
416 | llvm::SmallVector<llvm::SmallString<32>, 5> SubdirNames; |
417 | SubdirNames.emplace_back(Args: LiteralTriple.str()); |
418 | SubdirNames.emplace_back(Args: getTriple().str()); |
419 | SubdirNames.emplace_back(Args: getTriple().getArchName()); |
420 | SubdirNames.back() += "-w64-mingw32"; |
421 | SubdirNames.emplace_back(Args: getTriple().getArchName()); |
422 | SubdirNames.back() += "-w64-mingw32ucrt"; |
423 | SubdirNames.emplace_back(Args: "mingw32"); |
424 | if (SubdirName.empty()) { |
425 | SubdirName = getTriple().getArchName(); |
426 | SubdirName += "-w64-mingw32"; |
427 | } |
428 | // lib: Arch Linux, Ubuntu, Windows |
429 | // lib64: openSUSE Linux |
430 | for (StringRef CandidateLib : {"lib", "lib64"}) { |
431 | for (StringRef CandidateSysroot : SubdirNames) { |
432 | llvm::SmallString<1024> LibDir(Base); |
433 | llvm::sys::path::append(path&: LibDir, a: CandidateLib, b: "gcc", c: CandidateSysroot); |
434 | if (findGccVersion(LibDir, GccLibDir, Ver, Version&: GccVer)) { |
435 | SubdirName = std::string(CandidateSysroot); |
436 | return; |
437 | } |
438 | } |
439 | } |
440 | } |
441 | |
442 | static llvm::ErrorOr<std::string> findGcc(const llvm::Triple &LiteralTriple, |
443 | const llvm::Triple &T) { |
444 | llvm::SmallVector<llvm::SmallString<32>, 5> Gccs; |
445 | Gccs.emplace_back(Args: LiteralTriple.str()); |
446 | Gccs.back() += "-gcc"; |
447 | Gccs.emplace_back(Args: T.str()); |
448 | Gccs.back() += "-gcc"; |
449 | Gccs.emplace_back(Args: T.getArchName()); |
450 | Gccs.back() += "-w64-mingw32-gcc"; |
451 | Gccs.emplace_back(Args: T.getArchName()); |
452 | Gccs.back() += "-w64-mingw32ucrt-gcc"; |
453 | Gccs.emplace_back(Args: "mingw32-gcc"); |
454 | // Please do not add "gcc" here |
455 | for (StringRef CandidateGcc : Gccs) |
456 | if (llvm::ErrorOr<std::string> GPPName = llvm::sys::findProgramByName(Name: CandidateGcc)) |
457 | return GPPName; |
458 | return make_error_code(e: std::errc::no_such_file_or_directory); |
459 | } |
460 | |
461 | static llvm::ErrorOr<std::string> |
462 | findClangRelativeSysroot(const Driver &D, const llvm::Triple &LiteralTriple, |
463 | const llvm::Triple &T, std::string &SubdirName) { |
464 | llvm::SmallVector<llvm::SmallString<32>, 4> Subdirs; |
465 | Subdirs.emplace_back(Args: LiteralTriple.str()); |
466 | Subdirs.emplace_back(Args: T.str()); |
467 | Subdirs.emplace_back(Args: T.getArchName()); |
468 | Subdirs.back() += "-w64-mingw32"; |
469 | Subdirs.emplace_back(Args: T.getArchName()); |
470 | Subdirs.back() += "-w64-mingw32ucrt"; |
471 | StringRef ClangRoot = llvm::sys::path::parent_path(path: D.Dir); |
472 | StringRef Sep = llvm::sys::path::get_separator(); |
473 | for (StringRef CandidateSubdir : Subdirs) { |
474 | if (llvm::sys::fs::is_directory(Path: ClangRoot + Sep + CandidateSubdir)) { |
475 | SubdirName = std::string(CandidateSubdir); |
476 | return (ClangRoot + Sep + CandidateSubdir).str(); |
477 | } |
478 | } |
479 | return make_error_code(e: std::errc::no_such_file_or_directory); |
480 | } |
481 | |
482 | static bool looksLikeMinGWSysroot(const std::string &Directory) { |
483 | StringRef Sep = llvm::sys::path::get_separator(); |
484 | if (!llvm::sys::fs::exists(Path: Directory + Sep + "include"+ Sep + "_mingw.h")) |
485 | return false; |
486 | if (!llvm::sys::fs::exists(Path: Directory + Sep + "lib"+ Sep + "libkernel32.a")) |
487 | return false; |
488 | return true; |
489 | } |
490 | |
491 | toolchains::MinGW::MinGW(const Driver &D, const llvm::Triple &Triple, |
492 | const ArgList &Args) |
493 | : ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args), |
494 | RocmInstallation(D, Triple, Args) { |
495 | getProgramPaths().push_back(Elt: getDriver().Dir); |
496 | |
497 | std::string InstallBase = |
498 | std::string(llvm::sys::path::parent_path(path: getDriver().Dir)); |
499 | // The sequence for detecting a sysroot here should be kept in sync with |
500 | // the testTriple function below. |
501 | llvm::Triple LiteralTriple = getLiteralTriple(D, T: getTriple()); |
502 | if (getDriver().SysRoot.size()) |
503 | Base = getDriver().SysRoot; |
504 | // Look for <clang-bin>/../<triplet>; if found, use <clang-bin>/.. as the |
505 | // base as it could still be a base for a gcc setup with libgcc. |
506 | else if (llvm::ErrorOr<std::string> TargetSubdir = findClangRelativeSysroot( |
507 | D: getDriver(), LiteralTriple, T: getTriple(), SubdirName)) |
508 | Base = std::string(llvm::sys::path::parent_path(path: TargetSubdir.get())); |
509 | // If the install base of Clang seems to have mingw sysroot files directly |
510 | // in the toplevel include and lib directories, use this as base instead of |
511 | // looking for a triple prefixed GCC in the path. |
512 | else if (looksLikeMinGWSysroot(Directory: InstallBase)) |
513 | Base = InstallBase; |
514 | else if (llvm::ErrorOr<std::string> GPPName = |
515 | findGcc(LiteralTriple, T: getTriple())) |
516 | Base = std::string(llvm::sys::path::parent_path( |
517 | path: llvm::sys::path::parent_path(path: GPPName.get()))); |
518 | else |
519 | Base = InstallBase; |
520 | |
521 | Base += llvm::sys::path::get_separator(); |
522 | findGccLibDir(LiteralTriple); |
523 | TripleDirName = SubdirName; |
524 | // GccLibDir must precede Base/lib so that the |
525 | // correct crtbegin.o ,cetend.o would be found. |
526 | getFilePaths().push_back(Elt: GccLibDir); |
527 | |
528 | // openSUSE/Fedora |
529 | std::string CandidateSubdir = SubdirName + "/sys-root/mingw"; |
530 | if (getDriver().getVFS().exists(Path: Base + CandidateSubdir)) |
531 | SubdirName = CandidateSubdir; |
532 | |
533 | getFilePaths().push_back( |
534 | Elt: (Base + SubdirName + llvm::sys::path::get_separator() + "lib").str()); |
535 | |
536 | // Gentoo |
537 | getFilePaths().push_back( |
538 | Elt: (Base + SubdirName + llvm::sys::path::get_separator() + "mingw/lib").str()); |
539 | |
540 | // Only include <base>/lib if we're not cross compiling (not even for |
541 | // windows->windows to a different arch), or if the sysroot has been set |
542 | // (where we presume the user has pointed it at an arch specific |
543 | // subdirectory). |
544 | if (!::isCrossCompiling(T: getTriple(), /*RequireArchMatch=*/true) || |
545 | getDriver().SysRoot.size()) |
546 | getFilePaths().push_back(Elt: Base + "lib"); |
547 | |
548 | NativeLLVMSupport = |
549 | Args.getLastArgValue(options::OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER) |
550 | .equals_insensitive("lld"); |
551 | } |
552 | |
553 | Tool *toolchains::MinGW::getTool(Action::ActionClass AC) const { |
554 | switch (AC) { |
555 | case Action::PreprocessJobClass: |
556 | if (!Preprocessor) |
557 | Preprocessor.reset(p: new tools::gcc::Preprocessor(*this)); |
558 | return Preprocessor.get(); |
559 | case Action::CompileJobClass: |
560 | if (!Compiler) |
561 | Compiler.reset(p: new tools::gcc::Compiler(*this)); |
562 | return Compiler.get(); |
563 | default: |
564 | return ToolChain::getTool(AC); |
565 | } |
566 | } |
567 | |
568 | Tool *toolchains::MinGW::buildAssembler() const { |
569 | return new tools::MinGW::Assembler(*this); |
570 | } |
571 | |
572 | Tool *toolchains::MinGW::buildLinker() const { |
573 | return new tools::MinGW::Linker(*this); |
574 | } |
575 | |
576 | bool toolchains::MinGW::HasNativeLLVMSupport() const { |
577 | return NativeLLVMSupport; |
578 | } |
579 | |
580 | ToolChain::UnwindTableLevel |
581 | toolchains::MinGW::getDefaultUnwindTableLevel(const ArgList &Args) const { |
582 | Arg *ExceptionArg = Args.getLastArg(options::OPT_fsjlj_exceptions, |
583 | options::OPT_fseh_exceptions, |
584 | options::OPT_fdwarf_exceptions); |
585 | if (ExceptionArg && |
586 | ExceptionArg->getOption().matches(options::OPT_fseh_exceptions)) |
587 | return UnwindTableLevel::Asynchronous; |
588 | |
589 | if (getArch() == llvm::Triple::x86_64 || getArch() == llvm::Triple::arm || |
590 | getArch() == llvm::Triple::thumb || getArch() == llvm::Triple::aarch64) |
591 | return UnwindTableLevel::Asynchronous; |
592 | return UnwindTableLevel::None; |
593 | } |
594 | |
595 | bool toolchains::MinGW::isPICDefault() const { |
596 | return getArch() == llvm::Triple::x86_64 || |
597 | getArch() == llvm::Triple::aarch64; |
598 | } |
599 | |
600 | bool toolchains::MinGW::isPIEDefault(const llvm::opt::ArgList &Args) const { |
601 | return false; |
602 | } |
603 | |
604 | bool toolchains::MinGW::isPICDefaultForced() const { return true; } |
605 | |
606 | llvm::ExceptionHandling |
607 | toolchains::MinGW::GetExceptionModel(const ArgList &Args) const { |
608 | if (getArch() == llvm::Triple::x86_64 || getArch() == llvm::Triple::aarch64 || |
609 | getArch() == llvm::Triple::arm || getArch() == llvm::Triple::thumb) |
610 | return llvm::ExceptionHandling::WinEH; |
611 | return llvm::ExceptionHandling::DwarfCFI; |
612 | } |
613 | |
614 | SanitizerMask toolchains::MinGW::getSupportedSanitizers() const { |
615 | SanitizerMask Res = ToolChain::getSupportedSanitizers(); |
616 | Res |= SanitizerKind::Address; |
617 | Res |= SanitizerKind::PointerCompare; |
618 | Res |= SanitizerKind::PointerSubtract; |
619 | Res |= SanitizerKind::Vptr; |
620 | return Res; |
621 | } |
622 | |
623 | void toolchains::MinGW::AddCudaIncludeArgs(const ArgList &DriverArgs, |
624 | ArgStringList &CC1Args) const { |
625 | CudaInstallation->AddCudaIncludeArgs(DriverArgs, CC1Args); |
626 | } |
627 | |
628 | void toolchains::MinGW::AddHIPIncludeArgs(const ArgList &DriverArgs, |
629 | ArgStringList &CC1Args) const { |
630 | RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args); |
631 | } |
632 | |
633 | void toolchains::MinGW::printVerboseInfo(raw_ostream &OS) const { |
634 | CudaInstallation->print(OS); |
635 | RocmInstallation->print(OS); |
636 | } |
637 | |
638 | // Include directories for various hosts: |
639 | |
640 | // Windows, mingw.org |
641 | // c:\mingw\lib\gcc\mingw32\4.8.1\include\c++ |
642 | // c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\mingw32 |
643 | // c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\backward |
644 | // c:\mingw\include |
645 | // c:\mingw\mingw32\include |
646 | |
647 | // Windows, mingw-w64 mingw-builds |
648 | // c:\mingw32\i686-w64-mingw32\include |
649 | // c:\mingw32\i686-w64-mingw32\include\c++ |
650 | // c:\mingw32\i686-w64-mingw32\include\c++\i686-w64-mingw32 |
651 | // c:\mingw32\i686-w64-mingw32\include\c++\backward |
652 | |
653 | // Windows, mingw-w64 msys2 |
654 | // c:\msys64\mingw32\include |
655 | // c:\msys64\mingw32\i686-w64-mingw32\include |
656 | // c:\msys64\mingw32\include\c++\4.9.2 |
657 | // c:\msys64\mingw32\include\c++\4.9.2\i686-w64-mingw32 |
658 | // c:\msys64\mingw32\include\c++\4.9.2\backward |
659 | |
660 | // openSUSE |
661 | // /usr/lib64/gcc/x86_64-w64-mingw32/5.1.0/include/c++ |
662 | // /usr/lib64/gcc/x86_64-w64-mingw32/5.1.0/include/c++/x86_64-w64-mingw32 |
663 | // /usr/lib64/gcc/x86_64-w64-mingw32/5.1.0/include/c++/backward |
664 | // /usr/x86_64-w64-mingw32/sys-root/mingw/include |
665 | |
666 | // Arch Linux |
667 | // /usr/i686-w64-mingw32/include/c++/5.1.0 |
668 | // /usr/i686-w64-mingw32/include/c++/5.1.0/i686-w64-mingw32 |
669 | // /usr/i686-w64-mingw32/include/c++/5.1.0/backward |
670 | // /usr/i686-w64-mingw32/include |
671 | |
672 | // Ubuntu |
673 | // /usr/include/c++/4.8 |
674 | // /usr/include/c++/4.8/x86_64-w64-mingw32 |
675 | // /usr/include/c++/4.8/backward |
676 | // /usr/x86_64-w64-mingw32/include |
677 | |
678 | // Fedora |
679 | // /usr/x86_64-w64-mingw32ucrt/sys-root/mingw/include/c++/x86_64-w64-mingw32ucrt |
680 | // /usr/x86_64-w64-mingw32ucrt/sys-root/mingw/include/c++/backward |
681 | // /usr/x86_64-w64-mingw32ucrt/sys-root/mingw/include |
682 | // /usr/lib/gcc/x86_64-w64-mingw32ucrt/12.2.1/include-fixed |
683 | |
684 | void toolchains::MinGW::AddClangSystemIncludeArgs(const ArgList &DriverArgs, |
685 | ArgStringList &CC1Args) const { |
686 | if (DriverArgs.hasArg(options::OPT_nostdinc)) |
687 | return; |
688 | |
689 | if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { |
690 | SmallString<1024> P(getDriver().ResourceDir); |
691 | llvm::sys::path::append(path&: P, a: "include"); |
692 | addSystemInclude(DriverArgs, CC1Args, Path: P.str()); |
693 | } |
694 | |
695 | if (DriverArgs.hasArg(options::OPT_nostdlibinc)) |
696 | return; |
697 | |
698 | addSystemInclude(DriverArgs, CC1Args, |
699 | Path: Base + SubdirName + llvm::sys::path::get_separator() + |
700 | "include"); |
701 | |
702 | // Gentoo |
703 | addSystemInclude(DriverArgs, CC1Args, |
704 | Path: Base + SubdirName + llvm::sys::path::get_separator() + "usr/include"); |
705 | |
706 | // Only include <base>/include if we're not cross compiling (but do allow it |
707 | // if we're on Windows and building for Windows on another architecture), |
708 | // or if the sysroot has been set (where we presume the user has pointed it |
709 | // at an arch specific subdirectory). |
710 | if (!::isCrossCompiling(T: getTriple(), /*RequireArchMatch=*/false) || |
711 | getDriver().SysRoot.size()) |
712 | addSystemInclude(DriverArgs, CC1Args, Path: Base + "include"); |
713 | } |
714 | |
715 | void toolchains::MinGW::addClangTargetOptions( |
716 | const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, |
717 | Action::OffloadKind DeviceOffloadKind) const { |
718 | if (Arg *A = DriverArgs.getLastArg(options::OPT_mguard_EQ)) { |
719 | StringRef GuardArgs = A->getValue(); |
720 | if (GuardArgs == "none") { |
721 | // Do nothing. |
722 | } else if (GuardArgs == "cf") { |
723 | // Emit CFG instrumentation and the table of address-taken functions. |
724 | CC1Args.push_back(Elt: "-cfguard"); |
725 | } else if (GuardArgs == "cf-nochecks") { |
726 | // Emit only the table of address-taken functions. |
727 | CC1Args.push_back(Elt: "-cfguard-no-checks"); |
728 | } else { |
729 | getDriver().Diag(diag::err_drv_unsupported_option_argument) |
730 | << A->getSpelling() << GuardArgs; |
731 | } |
732 | } |
733 | |
734 | // Default to not enabling sized deallocation, but let user provided options |
735 | // override it. |
736 | // |
737 | // If using sized deallocation, user code that invokes delete will end up |
738 | // calling delete(void*,size_t). If the user wanted to override the |
739 | // operator delete(void*), there may be a fallback operator |
740 | // delete(void*,size_t) which calls the regular operator delete(void*). |
741 | // |
742 | // However, if the C++ standard library is linked in the form of a DLL, |
743 | // and the fallback operator delete(void*,size_t) is within this DLL (which is |
744 | // the case for libc++ at least) it will only redirect towards the library's |
745 | // default operator delete(void*), not towards the user's provided operator |
746 | // delete(void*). |
747 | // |
748 | // This issue can be avoided, if the fallback operators are linked statically |
749 | // into the callers, even if the C++ standard library is linked as a DLL. |
750 | // |
751 | // This is meant as a temporary workaround until libc++ implements this |
752 | // technique, which is tracked in |
753 | // https://github.com/llvm/llvm-project/issues/96899. |
754 | if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation, |
755 | options::OPT_fno_sized_deallocation)) |
756 | CC1Args.push_back(Elt: "-fno-sized-deallocation"); |
757 | |
758 | CC1Args.push_back(Elt: "-fno-use-init-array"); |
759 | |
760 | for (auto Opt : {options::OPT_mthreads, options::OPT_mwindows, |
761 | options::OPT_mconsole, options::OPT_mdll}) { |
762 | if (Arg *A = DriverArgs.getLastArgNoClaim(Opt)) |
763 | A->ignoreTargetSpecific(); |
764 | } |
765 | } |
766 | |
767 | void toolchains::MinGW::AddClangCXXStdlibIncludeArgs( |
768 | const ArgList &DriverArgs, ArgStringList &CC1Args) const { |
769 | if (DriverArgs.hasArg(options::OPT_nostdinc, options::OPT_nostdlibinc, |
770 | options::OPT_nostdincxx)) |
771 | return; |
772 | |
773 | StringRef Slash = llvm::sys::path::get_separator(); |
774 | |
775 | switch (GetCXXStdlibType(Args: DriverArgs)) { |
776 | case ToolChain::CST_Libcxx: { |
777 | std::string TargetDir = (Base + "include"+ Slash + getTripleString() + |
778 | Slash + "c++"+ Slash + "v1") |
779 | .str(); |
780 | if (getDriver().getVFS().exists(Path: TargetDir)) |
781 | addSystemInclude(DriverArgs, CC1Args, Path: TargetDir); |
782 | addSystemInclude(DriverArgs, CC1Args, |
783 | Path: Base + SubdirName + Slash + "include"+ Slash + "c++"+ |
784 | Slash + "v1"); |
785 | addSystemInclude(DriverArgs, CC1Args, |
786 | Path: Base + "include"+ Slash + "c++"+ Slash + "v1"); |
787 | break; |
788 | } |
789 | |
790 | case ToolChain::CST_Libstdcxx: |
791 | llvm::SmallVector<llvm::SmallString<1024>, 7> CppIncludeBases; |
792 | CppIncludeBases.emplace_back(Args: Base); |
793 | llvm::sys::path::append(path&: CppIncludeBases[0], a: SubdirName, b: "include", c: "c++"); |
794 | CppIncludeBases.emplace_back(Args: Base); |
795 | llvm::sys::path::append(path&: CppIncludeBases[1], a: SubdirName, b: "include", c: "c++", |
796 | d: Ver); |
797 | CppIncludeBases.emplace_back(Args: Base); |
798 | llvm::sys::path::append(path&: CppIncludeBases[2], a: "include", b: "c++", c: Ver); |
799 | CppIncludeBases.emplace_back(Args: GccLibDir); |
800 | llvm::sys::path::append(path&: CppIncludeBases[3], a: "include", b: "c++"); |
801 | CppIncludeBases.emplace_back(Args: GccLibDir); |
802 | llvm::sys::path::append(path&: CppIncludeBases[4], a: "include", |
803 | b: "g++-v"+ GccVer.Text); |
804 | CppIncludeBases.emplace_back(Args: GccLibDir); |
805 | llvm::sys::path::append(path&: CppIncludeBases[5], a: "include", |
806 | b: "g++-v"+ GccVer.MajorStr + "."+ GccVer.MinorStr); |
807 | CppIncludeBases.emplace_back(Args: GccLibDir); |
808 | llvm::sys::path::append(path&: CppIncludeBases[6], a: "include", |
809 | b: "g++-v"+ GccVer.MajorStr); |
810 | for (auto &CppIncludeBase : CppIncludeBases) { |
811 | addSystemInclude(DriverArgs, CC1Args, Path: CppIncludeBase); |
812 | CppIncludeBase += Slash; |
813 | addSystemInclude(DriverArgs, CC1Args, Path: CppIncludeBase + TripleDirName); |
814 | addSystemInclude(DriverArgs, CC1Args, Path: CppIncludeBase + "backward"); |
815 | } |
816 | break; |
817 | } |
818 | } |
819 | |
820 | static bool testTriple(const Driver &D, const llvm::Triple &Triple, |
821 | const ArgList &Args) { |
822 | // If an explicit sysroot is set, that will be used and we shouldn't try to |
823 | // detect anything else. |
824 | std::string SubdirName; |
825 | if (D.SysRoot.size()) |
826 | return true; |
827 | llvm::Triple LiteralTriple = getLiteralTriple(D, T: Triple); |
828 | std::string InstallBase = std::string(llvm::sys::path::parent_path(path: D.Dir)); |
829 | if (llvm::ErrorOr<std::string> TargetSubdir = |
830 | findClangRelativeSysroot(D, LiteralTriple, T: Triple, SubdirName)) |
831 | return true; |
832 | // If the install base itself looks like a mingw sysroot, we'll use that |
833 | // - don't use any potentially unrelated gcc to influence what triple to use. |
834 | if (looksLikeMinGWSysroot(Directory: InstallBase)) |
835 | return false; |
836 | if (llvm::ErrorOr<std::string> GPPName = findGcc(LiteralTriple, T: Triple)) |
837 | return true; |
838 | // If we neither found a colocated sysroot or a matching gcc executable, |
839 | // conclude that we can't know if this is the correct spelling of the triple. |
840 | return false; |
841 | } |
842 | |
843 | static llvm::Triple adjustTriple(const Driver &D, const llvm::Triple &Triple, |
844 | const ArgList &Args) { |
845 | // First test if the original triple can find a sysroot with the triple |
846 | // name. |
847 | if (testTriple(D, Triple, Args)) |
848 | return Triple; |
849 | llvm::SmallVector<llvm::StringRef, 3> Archs; |
850 | // If not, test a couple other possible arch names that might be what was |
851 | // intended. |
852 | if (Triple.getArch() == llvm::Triple::x86) { |
853 | Archs.emplace_back(Args: "i386"); |
854 | Archs.emplace_back(Args: "i586"); |
855 | Archs.emplace_back(Args: "i686"); |
856 | } else if (Triple.getArch() == llvm::Triple::arm || |
857 | Triple.getArch() == llvm::Triple::thumb) { |
858 | Archs.emplace_back(Args: "armv7"); |
859 | } |
860 | for (auto A : Archs) { |
861 | llvm::Triple TestTriple(Triple); |
862 | TestTriple.setArchName(A); |
863 | if (testTriple(D, Triple: TestTriple, Args)) |
864 | return TestTriple; |
865 | } |
866 | // If none was found, just proceed with the original value. |
867 | return Triple; |
868 | } |
869 | |
870 | void toolchains::MinGW::fixTripleArch(const Driver &D, llvm::Triple &Triple, |
871 | const ArgList &Args) { |
872 | if (Triple.getArch() == llvm::Triple::x86 || |
873 | Triple.getArch() == llvm::Triple::arm || |
874 | Triple.getArch() == llvm::Triple::thumb) |
875 | Triple = adjustTriple(D, Triple, Args); |
876 | } |
877 |
Definitions
- ConstructJob
- AddLibGCC
- ConstructJob
- isCrossCompiling
- findGccVersion
- getLiteralTriple
- findGccLibDir
- findGcc
- findClangRelativeSysroot
- looksLikeMinGWSysroot
- MinGW
- getTool
- buildAssembler
- buildLinker
- HasNativeLLVMSupport
- getDefaultUnwindTableLevel
- isPICDefault
- isPIEDefault
- isPICDefaultForced
- GetExceptionModel
- getSupportedSanitizers
- AddCudaIncludeArgs
- AddHIPIncludeArgs
- printVerboseInfo
- AddClangSystemIncludeArgs
- addClangTargetOptions
- AddClangCXXStdlibIncludeArgs
- testTriple
- adjustTriple
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more