- 
                Notifications
    You must be signed in to change notification settings 
- Fork 15k
[Clang][Driver] Revise Cygwin ToolChain to call linker directly #147960
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -9,6 +9,7 @@ | |
| #include "Cygwin.h" | ||
| #include "clang/Config/config.h" | ||
| #include "clang/Driver/CommonArgs.h" | ||
| #include "clang/Driver/Compilation.h" | ||
| #include "clang/Driver/Driver.h" | ||
| #include "clang/Driver/Options.h" | ||
| #include "llvm/Support/Path.h" | ||
|  | @@ -30,6 +31,8 @@ Cygwin::Cygwin(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) | |
| Generic_GCC::PushPPaths(PPaths); | ||
|  | ||
| path_list &Paths = getFilePaths(); | ||
| if (GCCInstallation.isValid()) | ||
| Paths.push_back(GCCInstallation.getInstallPath().str()); | ||
|  | ||
| Generic_GCC::AddMultiarchPaths(D, SysRoot, "lib", Paths); | ||
|  | ||
|  | @@ -107,3 +110,300 @@ void Cygwin::AddClangSystemIncludeArgs(const ArgList &DriverArgs, | |
| addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/include"); | ||
| addExternCSystemInclude(DriverArgs, CC1Args, SysRoot + "/usr/include/w32api"); | ||
| } | ||
|  | ||
| void cygwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, | ||
| const InputInfo &Output, | ||
| const InputInfoList &Inputs, | ||
| const ArgList &Args, | ||
| const char *LinkingOutput) const { | ||
| const auto &ToolChain = getToolChain(); | ||
| const Driver &D = ToolChain.getDriver(); | ||
|  | ||
| const bool IsStatic = Args.hasArg(options::OPT_static); | ||
|  | ||
| ArgStringList CmdArgs; | ||
|  | ||
| // Silence warning for "clang -g foo.o -o foo" | ||
| Args.ClaimAllArgs(options::OPT_g_Group); | ||
| // and "clang -emit-llvm foo.o -o foo" | ||
| Args.ClaimAllArgs(options::OPT_emit_llvm); | ||
| // and for "clang -w foo.o -o foo". Other warning options are already | ||
| // handled somewhere else. | ||
| Args.ClaimAllArgs(options::OPT_w); | ||
|  | ||
| if (!D.SysRoot.empty()) | ||
| CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); | ||
|  | ||
| if (Args.hasArg(options::OPT_s)) | ||
| CmdArgs.push_back("-s"); | ||
|  | ||
| CmdArgs.push_back("-m"); | ||
| switch (ToolChain.getArch()) { | ||
| case llvm::Triple::x86: | ||
| CmdArgs.push_back("i386pe"); | ||
| break; | ||
| case llvm::Triple::x86_64: | ||
| CmdArgs.push_back("i386pep"); | ||
| break; | ||
| case llvm::Triple::arm: | ||
| case llvm::Triple::thumb: | ||
| // FIXME: this is incorrect for WinCE | ||
| CmdArgs.push_back("thumb2pe"); | ||
| break; | ||
| case llvm::Triple::aarch64: | ||
| if (Args.hasArg(options::OPT_marm64x)) | ||
| CmdArgs.push_back("arm64xpe"); | ||
| else if (ToolChain.getEffectiveTriple().isWindowsArm64EC()) | ||
| CmdArgs.push_back("arm64ecpe"); | ||
| else | ||
| CmdArgs.push_back("arm64pe"); | ||
| break; | ||
| case llvm::Triple::mipsel: | ||
| CmdArgs.push_back("mipspe"); | ||
| break; | ||
| default: | ||
| D.Diag(diag::err_target_unknown_triple) | ||
| << ToolChain.getEffectiveTriple().str(); | ||
| } | ||
|  | ||
| Arg *SubsysArg = | ||
| Args.getLastArg(options::OPT_mwindows, options::OPT_mconsole); | ||
| if (SubsysArg && SubsysArg->getOption().matches(options::OPT_mwindows)) { | ||
| CmdArgs.push_back("--subsystem"); | ||
| CmdArgs.push_back("windows"); | ||
| } else if (SubsysArg && | ||
| SubsysArg->getOption().matches(options::OPT_mconsole)) { | ||
| CmdArgs.push_back("--subsystem"); | ||
| CmdArgs.push_back("console"); | ||
| } | ||
|  | ||
| if (ToolChain.getTriple().isArch32Bit()) { | ||
| CmdArgs.push_back("--wrap=_Znwj"); | ||
| CmdArgs.push_back("--wrap=_Znaj"); | ||
| CmdArgs.push_back("--wrap=_ZdlPv"); | ||
| CmdArgs.push_back("--wrap=_ZdaPv"); | ||
| CmdArgs.push_back("--wrap=_ZnwjRKSt9nothrow_t"); | ||
| CmdArgs.push_back("--wrap=_ZnajRKSt9nothrow_t"); | ||
| CmdArgs.push_back("--wrap=_ZdlPvRKSt9nothrow_t"); | ||
| CmdArgs.push_back("--wrap=_ZdaPvRKSt9nothrow_t"); | ||
| } else { | ||
| CmdArgs.push_back("--wrap=_Znwm"); | ||
| CmdArgs.push_back("--wrap=_Znam"); | ||
| CmdArgs.push_back("--wrap=_ZdlPv"); | ||
| CmdArgs.push_back("--wrap=_ZdaPv"); | ||
| CmdArgs.push_back("--wrap=_ZnwmRKSt9nothrow_t"); | ||
| CmdArgs.push_back("--wrap=_ZnamRKSt9nothrow_t"); | ||
| CmdArgs.push_back("--wrap=_ZdlPvRKSt9nothrow_t"); | ||
| CmdArgs.push_back("--wrap=_ZdaPvRKSt9nothrow_t"); | ||
| } | ||
|  | ||
| if (Args.hasArg(options::OPT_mdll)) | ||
| CmdArgs.push_back("--dll"); | ||
| else if (Args.hasArg(options::OPT_shared)) | ||
| CmdArgs.push_back("--shared"); | ||
| if (Args.hasArg(options::OPT_static)) | ||
| CmdArgs.push_back("-Bstatic"); | ||
| else | ||
| CmdArgs.push_back("-Bdynamic"); | ||
|  | ||
| CmdArgs.push_back("--dll-search-prefix=cyg"); | ||
| if (Args.hasArg(options::OPT_rdynamic)) | ||
| CmdArgs.push_back("--export-all-symbols"); | ||
| if (!Args.hasArg(options::OPT_shared) && !Args.hasArg(options::OPT_mdll)) { | ||
| if (ToolChain.getTriple().isArch32Bit()) | ||
| CmdArgs.push_back("--large-address-aware"); | ||
| CmdArgs.push_back("--tsaware"); | ||
| } | ||
|  | ||
| CmdArgs.push_back("-o"); | ||
| const char *OutputFile = Output.getFilename(); | ||
| // GCC implicitly adds an .exe extension if it is given an output file name | ||
| // that lacks an extension. | ||
| // GCC used to do this only when the compiler itself runs on windows, but | ||
| // since GCC 8 it does the same when cross compiling as well. | ||
| if (!llvm::sys::path::has_extension(OutputFile)) { | ||
| CmdArgs.push_back(Args.MakeArgString(Twine(OutputFile) + ".exe")); | ||
| OutputFile = CmdArgs.back(); | ||
| } else | ||
| CmdArgs.push_back(OutputFile); | ||
|  | ||
| if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles, | ||
| options::OPT_r)) { | ||
| const bool IsShared = Args.hasArg(options::OPT_shared); | ||
| if (Args.hasArg(options::OPT_mdll) || IsShared) { | ||
| CmdArgs.push_back("-e"); | ||
| CmdArgs.push_back(ToolChain.getArch() == llvm::Triple::x86 | ||
| ? "__cygwin_dll_entry@12" | ||
| : "_cygwin_dll_entry"); | ||
| CmdArgs.push_back("--enable-auto-image-base"); | ||
| } | ||
|  | ||
| if (!Args.hasArg(options::OPT_mdll) && !IsShared) | ||
| CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt0.o"))); | ||
| if (ToolChain.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT) { | ||
| std::string crtbegin = | ||
| ToolChain.getCompilerRT(Args, "crtbegin", ToolChain::FT_Object); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From which toolchain file is this bit copied? We don't have these bits in the mingw toolchain. Has someone tested and used cygwin with  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have local patches to build compiler-rt (@tyan0 might have another work), and succeeded to compile a program with  | ||
| if (ToolChain.getVFS().exists(crtbegin)) { | ||
| std::string P; | ||
| P = crtbegin; | ||
| CmdArgs.push_back(Args.MakeArgString(P)); | ||
| } | ||
| } | ||
| if (IsShared) | ||
| CmdArgs.push_back( | ||
| Args.MakeArgString(ToolChain.GetFilePath("crtbeginS.o"))); | ||
| else | ||
| CmdArgs.push_back( | ||
| Args.MakeArgString(ToolChain.GetFilePath("crtbegin.o"))); | ||
|  | ||
| // Add crtfastmath.o if available and fast math is enabled. | ||
| ToolChain.addFastMathRuntimeIfAvailable(Args, CmdArgs); | ||
| } | ||
|  | ||
| Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_u}); | ||
|  | ||
| ToolChain.AddFilePathLibArgs(Args, CmdArgs); | ||
|  | ||
| if (D.isUsingLTO()) | ||
| tools::addLTOOptions(ToolChain, Args, CmdArgs, Output, Inputs, | ||
| D.getLTOMode() == LTOK_Thin); | ||
|  | ||
| if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) | ||
| CmdArgs.push_back("--no-demangle"); | ||
|  | ||
| bool NeedsSanitizerDeps = | ||
| tools::addSanitizerRuntimes(ToolChain, Args, CmdArgs); | ||
| bool NeedsXRayDeps = tools::addXRayRuntime(ToolChain, Args, CmdArgs); | ||
| tools::addLinkerCompressDebugSectionsOption(ToolChain, Args, CmdArgs); | ||
| tools::AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); | ||
|  | ||
| bool saw_high_entropy_va = false; | ||
| bool saw_nxcompat = false; | ||
| for (const char *Arg : CmdArgs) { | ||
| if (StringRef(Arg) == "-high-entropy-va" || | ||
| StringRef(Arg) == "--high-entropy-va" || | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if there's a more clever way to express this, without having to repeat every constant twice, for checking for one or two leading dashes? And the  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thinking about this more, these are last-wins options, so couldn't we just add our intended defaults before the AddLinkerInputs and be done with it? Or am I forgetting the reason why that wouldn't work? | ||
| StringRef(Arg) == "-disable-high-entropy-va" || | ||
| StringRef(Arg) == "--disable-high-entropy-va") | ||
| saw_high_entropy_va = true; | ||
| if (StringRef(Arg) == "-nxcompat" || StringRef(Arg) == "--nxcompat" || | ||
| StringRef(Arg) == "-disable-nxcompat" || | ||
| StringRef(Arg) == "--disable-nxcompat") | ||
| saw_nxcompat = true; | ||
| } | ||
| if (!saw_high_entropy_va && ToolChain.getTriple().isArch64Bit()) | ||
| CmdArgs.push_back("--disable-high-entropy-va"); | ||
| if (!saw_nxcompat) | ||
| CmdArgs.push_back("--disable-nxcompat"); | ||
|  | ||
| tools::addHIPRuntimeLibArgs(ToolChain, C, Args, CmdArgs); | ||
|  | ||
| // The profile runtime also needs access to system libraries. | ||
| getToolChain().addProfileRTLibs(Args, CmdArgs); | ||
|  | ||
| if (D.CCCIsCXX() && | ||
| !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs, | ||
| options::OPT_r)) { | ||
| if (ToolChain.ShouldLinkCXXStdlib(Args)) { | ||
| bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) && | ||
| !Args.hasArg(options::OPT_static); | ||
| if (OnlyLibstdcxxStatic) | ||
| CmdArgs.push_back("-Bstatic"); | ||
| ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); | ||
| if (OnlyLibstdcxxStatic) | ||
| CmdArgs.push_back("-Bdynamic"); | ||
| } | ||
| CmdArgs.push_back("-lm"); | ||
| } | ||
|  | ||
| // Silence warnings when linking C code with a C++ '-stdlib' argument. | ||
| Args.ClaimAllArgs(options::OPT_stdlib_EQ); | ||
|  | ||
| // Additional linker set-up and flags for Fortran. This is required in order | ||
| // to generate executables. As Fortran runtime depends on the C runtime, | ||
| // these dependencies need to be listed before the C runtime below (i.e. | ||
| // AddRunTimeLibs). | ||
| if (D.IsFlangMode() && | ||
| !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { | ||
| ToolChain.addFortranRuntimeLibraryPath(Args, CmdArgs); | ||
| ToolChain.addFortranRuntimeLibs(Args, CmdArgs); | ||
| CmdArgs.push_back("-lm"); | ||
| } | ||
|  | ||
| if (!Args.hasArg(options::OPT_nostdlib, options::OPT_r)) { | ||
| if (!Args.hasArg(options::OPT_nodefaultlibs)) { | ||
| if (IsStatic) | ||
| CmdArgs.push_back("--start-group"); | ||
|  | ||
| if (NeedsSanitizerDeps) | ||
| tools::linkSanitizerRuntimeDeps(ToolChain, Args, CmdArgs); | ||
|  | ||
| if (NeedsXRayDeps) | ||
| tools::linkXRayRuntimeDeps(ToolChain, Args, CmdArgs); | ||
|  | ||
| bool WantPthread = Args.hasArg(options::OPT_pthread) || | ||
| Args.hasArg(options::OPT_pthreads); | ||
|  | ||
| // Use the static OpenMP runtime with -static-openmp | ||
| bool StaticOpenMP = Args.hasArg(options::OPT_static_openmp) && | ||
| !Args.hasArg(options::OPT_static); | ||
|  | ||
| // FIXME: Only pass GompNeedsRT = true for platforms with libgomp that | ||
| // require librt. Most modern Linux platforms do, but some may not. | ||
| if (tools::addOpenMPRuntime(C, CmdArgs, ToolChain, Args, StaticOpenMP, | ||
| JA.isHostOffloading(Action::OFK_OpenMP), | ||
| /* GompNeedsRT= */ true)) | ||
| // OpenMP runtimes implies pthreads when using the GNU toolchain. | ||
| // FIXME: Does this really make sense for all GNU toolchains? | ||
| WantPthread = true; | ||
|  | ||
| tools::AddRunTimeLibs(ToolChain, D, CmdArgs, Args); | ||
|  | ||
| if (WantPthread) | ||
| CmdArgs.push_back("-lpthread"); | ||
|  | ||
| if (Args.hasArg(options::OPT_fsplit_stack)) | ||
| CmdArgs.push_back("--wrap=pthread_create"); | ||
|  | ||
| if (!Args.hasArg(options::OPT_nolibc)) | ||
| CmdArgs.push_back("-lc"); | ||
|  | ||
| // Cygwin specific | ||
| CmdArgs.push_back("-lcygwin"); | ||
| if (Args.hasArg(options::OPT_mwindows)) { | ||
| CmdArgs.push_back("-lgdi32"); | ||
| CmdArgs.push_back("-lcomdlg32"); | ||
| } | ||
| CmdArgs.push_back("-ladvapi32"); | ||
| CmdArgs.push_back("-lshell32"); | ||
| CmdArgs.push_back("-luser32"); | ||
| CmdArgs.push_back("-lkernel32"); | ||
|  | ||
| if (IsStatic) | ||
| CmdArgs.push_back("--end-group"); | ||
| else | ||
| tools::AddRunTimeLibs(ToolChain, D, CmdArgs, Args); | ||
| } | ||
|  | ||
| if (!Args.hasArg(options::OPT_nostartfiles)) { | ||
| if (ToolChain.GetRuntimeLibType(Args) == ToolChain::RLT_CompilerRT) { | ||
| std::string crtend = | ||
| ToolChain.getCompilerRT(Args, "crtend", ToolChain::FT_Object); | ||
| if (ToolChain.getVFS().exists(crtend)) { | ||
| std::string P; | ||
| P = crtend; | ||
| CmdArgs.push_back(Args.MakeArgString(P)); | ||
| } | ||
| } | ||
| CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtend.o"))); | ||
| } | ||
| } | ||
|  | ||
| Args.addAllArgs(CmdArgs, {options::OPT_T, options::OPT_t}); | ||
|  | ||
| const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath()); | ||
| C.addCommand(std::make_unique<Command>(JA, *this, | ||
| ResponseFileSupport::AtFileCurCP(), | ||
| Exec, CmdArgs, Inputs, Output)); | ||
| } | ||
|  | ||
| auto Cygwin::buildLinker() const -> Tool * { return new cygwin::Linker(*this); } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the fallback (
getOS()which defaults toTriple.getOSName()) return here, compared with this new value?And I'm a little unsure how this change fits in with the rest of this change; isn't this change orthogonal to the rest? The rest of the change is about directly calling the linker (while still using the same libgcc etc as otherwise?), while this change is needed for e.g. getting the right directory for compiler-rt builtins and similar? I.e. this change might be ok in itself, but it feels to me like it's a separate change for a separate PR?