Skip to content

[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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,8 @@ static StringRef getArchNameForCompilerRTLib(const ToolChain &TC,
StringRef ToolChain::getOSLibName() const {
if (Triple.isOSDarwin())
return "darwin";
if (Triple.isWindowsCygwinEnvironment())
return "cygwin";

switch (Triple.getOS()) {
case llvm::Triple::FreeBSD:
Expand Down
272 changes: 272 additions & 0 deletions clang/lib/Driver/ToolChains/Cygwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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);

Expand Down Expand Up @@ -107,3 +110,272 @@ 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");
}

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");

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);
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;
for (const char *Arg : CmdArgs)
if (StringRef(Arg) == "--high-entropy-va" ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this check needed? Users want to override can use -Wl,--high-entropy-va. If you really want to handle this here, options::OPT_fhigh_entropy_va should be added.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is looking at the linker args, so already having the -Wl, removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean as this:

if (!Args.hasFlag(options::OPT_fauto_import, options::OPT_fno_auto_import,
true))
CmdArgs.push_back("--disable-auto-import");

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you define options::OPT_fauto_import? My concern is how you handle prepended -Wl, in Args.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the linkers adopt last-flag-wins rule, simply calling CmdArgs.push_back("--disable-high-entropy-va") before calling AddLinkerInputs is enough to work. -Wl, is not needed to be handled explicitly.

StringRef(Arg) == "--disable-high-entropy-va")
saw_high_entropy_va = true;
if (!saw_high_entropy_va)
CmdArgs.push_back("--disable-high-entropy-va");

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); }
18 changes: 18 additions & 0 deletions clang/lib/Driver/ToolChains/Cygwin.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,25 @@ class LLVM_LIBRARY_VISIBILITY Cygwin : public Generic_GCC {
void
AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const override;

protected:
Tool *buildLinker() const override;
};

namespace cygwin {
class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
public:
Linker(const ToolChain &TC) : Tool("cygwin::Linker", "linker", TC) {}

bool hasIntegratedCPP() const override { return false; }
bool isLinkJob() const override { return true; }

void ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output, const InputInfoList &Inputs,
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const override;
};
} // end namespace cygwin

} // end namespace toolchains
} // end namespace driver
Expand Down
Empty file.
Loading