mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-29 22:50:53 +03:00

This happens in an environment where MSVC was set up first (so env var VSINSTALLDIR defined), and later prepending a dir such as `C:\Program Files\Git\usr\bin` to PATH, so that the first link.exe on PATH isn't the Microsoft one.
306 lines
9.3 KiB
C++
306 lines
9.3 KiB
C++
//===-- linker-msvc.cpp ---------------------------------------------------===//
|
|
//
|
|
// LDC - the LLVM D compiler
|
|
//
|
|
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
|
// file for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "dmd/errors.h"
|
|
#include "driver/args.h"
|
|
#include "driver/cl_options.h"
|
|
#include "driver/cl_options_instrumentation.h"
|
|
#include "driver/cl_options_sanitizers.h"
|
|
#include "driver/configfile.h"
|
|
#include "driver/exe_path.h"
|
|
#include "driver/linker.h"
|
|
#include "driver/tool.h"
|
|
#include "gen/logger.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#if LDC_WITH_LLD
|
|
#include "lld/Common/Driver.h"
|
|
#if LDC_LLVM_VER >= 1700
|
|
LLD_HAS_DRIVER(coff)
|
|
#endif
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace {
|
|
|
|
void addMscrtLibs(bool useInternalToolchain, std::vector<std::string> &args) {
|
|
const auto mscrtlibName = getMscrtLibName(&useInternalToolchain);
|
|
|
|
args.push_back(("/DEFAULTLIB:" + mscrtlibName).str());
|
|
|
|
// We need the vcruntime lib for druntime's exception handling (ldc.eh_msvc).
|
|
// Pick one of the 4 variants matching the selected main UCRT lib.
|
|
|
|
#if LDC_LLVM_VER >= 1700
|
|
#define contains_lower contains_insensitive
|
|
#define endswith_lower ends_with_insensitive
|
|
#else
|
|
#define contains_lower contains_insensitive
|
|
#define endswith_lower endswith_insensitive
|
|
#endif
|
|
if (useInternalToolchain) {
|
|
assert(mscrtlibName.contains_lower("vcruntime"));
|
|
return;
|
|
}
|
|
|
|
const bool isStatic = mscrtlibName.contains_lower("libcmt");
|
|
|
|
const bool isDebug =
|
|
mscrtlibName.endswith_lower("d") || mscrtlibName.endswith_lower("d.lib");
|
|
|
|
const llvm::StringRef prefix = isStatic ? "lib" : "";
|
|
const llvm::StringRef suffix = isDebug ? "d" : "";
|
|
|
|
args.push_back(("/DEFAULTLIB:" + prefix + "vcruntime" + suffix).str());
|
|
}
|
|
|
|
void addLibIfFound(std::vector<std::string> &args, const llvm::Twine &name) {
|
|
for (const char *dir : ConfigFile::instance.libDirs()) {
|
|
llvm::SmallString<128> candidate(dir);
|
|
llvm::sys::path::append(candidate, name);
|
|
if (llvm::sys::fs::exists(candidate)) {
|
|
args.emplace_back(candidate.data(), candidate.size());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void addSanitizerLibs(std::vector<std::string> &args) {
|
|
if (opts::isSanitizerEnabled(opts::AddressSanitizer)) {
|
|
args.push_back("ldc_rt.asan.lib");
|
|
} else if (opts::isSanitizerEnabled(opts::LeakSanitizer)) {
|
|
// If ASan is enabled, it includes LSan. So only add LSan link flags if ASan is _not_ enabled already.
|
|
args.push_back("ldc_rt.lsan.lib");
|
|
}
|
|
if (opts::isSanitizerEnabled(opts::FuzzSanitizer)) {
|
|
args.push_back("ldc_rt.fuzzer.lib");
|
|
args.push_back("/SUBSYSTEM:CONSOLE"); // pull main() from fuzzer lib
|
|
}
|
|
|
|
// TODO: remaining sanitizers
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
int linkObjToBinaryMSVC(llvm::StringRef outputPath,
|
|
const std::vector<std::string> &defaultLibNames) {
|
|
#ifdef _WIN32
|
|
windows::MsvcEnvironmentScope msvcEnv;
|
|
|
|
const bool forceMSVC = env::has(L"LDC_VSDIR_FORCE");
|
|
const bool useInternalToolchain =
|
|
(!forceMSVC && getExplicitMscrtLibName().contains_lower("vcruntime")) ||
|
|
!msvcEnv.setup();
|
|
|
|
if (forceMSVC && useInternalToolchain) {
|
|
warning(Loc(), "no Visual C++ installation found for linking, falling back "
|
|
"to MinGW-based libraries");
|
|
}
|
|
#else
|
|
const bool useInternalToolchain = true;
|
|
#endif
|
|
|
|
// build arguments
|
|
std::vector<std::string> args;
|
|
|
|
args.push_back("/NOLOGO");
|
|
|
|
// specify that the image will contain a table of safe exception handlers
|
|
// and can handle addresses >2GB (32bit only)
|
|
if (global.params.targetTriple->isArch32Bit()) {
|
|
args.push_back("/SAFESEH");
|
|
args.push_back("/LARGEADDRESSAWARE");
|
|
}
|
|
|
|
// output debug information
|
|
if (global.params.symdebug) {
|
|
args.push_back("/DEBUG");
|
|
}
|
|
|
|
// remove dead code and fold identical COMDATs
|
|
if (opts::disableLinkerStripDead) {
|
|
args.push_back("/OPT:NOREF");
|
|
} else {
|
|
args.push_back("/OPT:REF");
|
|
// don't fold identical COMDATs (e.g., functions) if debuginfos are enabled,
|
|
// otherwise breakpoints may not be hit
|
|
args.push_back(global.params.symdebug ? "/OPT:NOICF" : "/OPT:ICF");
|
|
}
|
|
|
|
const bool willLinkAgainstSharedDefaultLibs =
|
|
!defaultLibNames.empty() && linkAgainstSharedDefaultLibs();
|
|
if (willLinkAgainstSharedDefaultLibs) {
|
|
// Suppress linker warning LNK4217 wrt. 'importing locally defined symbol'
|
|
// (dllimport of symbol dllexported from the same binary), because there
|
|
// might be *many* of those (=> instantiated globals) if compiled with
|
|
// -dllimport=all (and without -linkonce-templates).
|
|
args.push_back("/IGNORE:4217");
|
|
}
|
|
|
|
// add C runtime libs
|
|
addMscrtLibs(useInternalToolchain, args);
|
|
|
|
// specify creation of DLL
|
|
if (global.params.dll) {
|
|
args.push_back("/DLL");
|
|
}
|
|
|
|
args.push_back(("/OUT:" + outputPath).str());
|
|
|
|
// object files
|
|
for (auto objfile : global.params.objfiles) {
|
|
args.push_back(objfile);
|
|
}
|
|
|
|
// add precompiled rt.dso object file (in lib directory) when linking
|
|
// against shared druntime
|
|
const auto &libDirs = ConfigFile::instance.libDirs();
|
|
if (willLinkAgainstSharedDefaultLibs && !libDirs.empty()) {
|
|
args.push_back((llvm::Twine(libDirs[0]) + "/ldc_rt.dso.obj").str());
|
|
}
|
|
|
|
// .res/.def files
|
|
if (global.params.resfile.length)
|
|
args.push_back(global.params.resfile.ptr);
|
|
if (global.params.deffile.length)
|
|
args.push_back(std::string("/DEF:") + global.params.deffile.ptr);
|
|
|
|
if (opts::enableDynamicCompile) {
|
|
args.push_back("ldc-jit-rt.lib");
|
|
args.push_back("ldc-jit.lib");
|
|
}
|
|
|
|
// user libs
|
|
for (auto libfile : global.params.libfiles) {
|
|
args.push_back(libfile);
|
|
}
|
|
|
|
// LLVM compiler-rt libs
|
|
addLibIfFound(args, "ldc_rt.builtins.lib");
|
|
addSanitizerLibs(args);
|
|
if (opts::isInstrumentingForPGO()) {
|
|
args.push_back("ldc_rt.profile.lib");
|
|
// it depends on ws2_32 for symbol `gethostname`
|
|
args.push_back("ws2_32.lib");
|
|
}
|
|
|
|
// additional linker switches
|
|
auto addSwitch = [&](std::string str) {
|
|
if (str.length() > 2) {
|
|
// rewrite common -L and -l switches
|
|
if (str[0] == '-' && str[1] == 'L') {
|
|
str = "/LIBPATH:" + str.substr(2);
|
|
} else if (str[0] == '-' && str[1] == 'l') {
|
|
str = str.substr(2) + ".lib";
|
|
}
|
|
}
|
|
args.push_back(str);
|
|
};
|
|
|
|
for (const auto &str : opts::linkerSwitches) {
|
|
addSwitch(str);
|
|
}
|
|
|
|
// lib dirs
|
|
for (const char *dir_c : libDirs) {
|
|
const llvm::StringRef dir(dir_c);
|
|
if (!dir.empty())
|
|
args.push_back(("/LIBPATH:" + dir).str());
|
|
}
|
|
|
|
if (useInternalToolchain && !libDirs.empty()) {
|
|
args.push_back((llvm::Twine("/LIBPATH:") + libDirs[0] + "/mingw").str());
|
|
}
|
|
|
|
// default libs
|
|
for (const auto &name : defaultLibNames) {
|
|
args.push_back(name + ".lib");
|
|
}
|
|
|
|
// libs added via pragma(lib, libname) - should be empty due to embedded
|
|
// references in object file
|
|
for (auto ls : global.params.linkswitches) {
|
|
addSwitch(ls);
|
|
}
|
|
|
|
auto explicitPlatformLibs = getExplicitPlatformLibs();
|
|
#if LDC_LLVM_VER >= 1600
|
|
if (explicitPlatformLibs.has_value()) {
|
|
for (auto &lib : explicitPlatformLibs.value()) {
|
|
args.push_back(llvm::sys::path::has_extension(lib) ? std::move(lib)
|
|
: lib + ".lib");
|
|
}
|
|
}
|
|
#else
|
|
if (explicitPlatformLibs.hasValue()) {
|
|
for (auto &lib : explicitPlatformLibs.getValue()) {
|
|
args.push_back(llvm::sys::path::has_extension(lib) ? std::move(lib)
|
|
: lib + ".lib");
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
// default platform libs
|
|
// TODO check which libaries are necessary
|
|
args.insert(args.end(),
|
|
{"kernel32.lib", "user32.lib", "gdi32.lib", "winspool.lib",
|
|
"shell32.lib", // required for dmain2.d
|
|
"ole32.lib", "oleaut32.lib", "uuid.lib", "comdlg32.lib",
|
|
"advapi32.lib",
|
|
// these get pulled in by druntime (rt/msvc.c); include
|
|
// explicitly for -betterC convenience (issue #3035)
|
|
"oldnames.lib", "legacy_stdio_definitions.lib"});
|
|
}
|
|
|
|
Logger::println("Linking with: ");
|
|
Stream logstr = Logger::cout();
|
|
for (const auto &arg : args) {
|
|
if (!arg.empty()) {
|
|
logstr << "'" << arg << "' ";
|
|
}
|
|
}
|
|
logstr << "\n"; // FIXME where's flush ?
|
|
|
|
#if LDC_WITH_LLD
|
|
if (useInternalLLDForLinking() ||
|
|
(useInternalToolchain && opts::linker.empty())) {
|
|
const auto fullArgs =
|
|
getFullArgs("lld-link", args, global.params.v.verbose);
|
|
|
|
const bool canExitEarly = false;
|
|
const bool success = lld::coff::link(fullArgs, llvm::outs(), llvm::errs(),
|
|
canExitEarly, false);
|
|
|
|
if (!success)
|
|
error(Loc(), "linking with LLD failed");
|
|
|
|
return success ? 0 : 1;
|
|
}
|
|
#endif
|
|
|
|
// try to call linker
|
|
std::string linker = opts::linker;
|
|
if (linker.empty()) {
|
|
#ifdef _WIN32
|
|
// Default to lld-link.exe for LTO, otherwise Microsoft's link.exe.
|
|
// Try not to accidentally use a GNU link.exe (if in PATH before the MSVC
|
|
// bin dir).
|
|
linker = opts::isUsingLTO() ? "lld-link.exe"
|
|
: msvcEnv.tryResolveToolPath("link.exe");
|
|
#else
|
|
linker = "lld-link";
|
|
#endif
|
|
}
|
|
|
|
return executeToolAndWait(Loc(), linker, args, global.params.v.verbose);
|
|
}
|