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