mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-14 03:45:33 +00:00
[Windows] Autolink with basenames and add libdir to libpath
Prior to this change, for a few compiler-rt libraries such as ubsan and the profile library, Clang would embed "-defaultlib:path/to/rt-arch.lib" into the .drective section of every object compiled with -finstr-profile-generate or -fsanitize=ubsan as appropriate. These paths assume that the link step will run from the same working directory as the compile step. There is also evidence that sometimes the paths become absolute, such as when clang is run from a different drive letter from the current working directory. This is fragile, and I'd like to get away from having paths embedded in the object if possible. Long ago it was suggested that we use this for ASan, and apparently I felt the same way back then: https://reviews.llvm.org/D4428#56536 This is also consistent with how all other autolinking usage works for PS4, Mac, and Windows: they all use basenames, not paths. To keep things working for people using the standard GCC driver workflow, the driver now adds the resource directory to the linker library search path when it calls the linker. This is enough to make check-ubsan pass, and seems like a generally good thing. Users that invoke the linker directly (most clang-cl users) will have to add clang's resource library directory to their linker search path in their build system. I'm not sure where I can document this. Ideally I'd also do it in the MSBuild files, but I can't figure out where they go. I'd like to start with this for now. Reviewed By: hans Differential Revision: https://reviews.llvm.org/D65543
This commit is contained in:
parent
59b9e6fe76
commit
b8000c0ce8
@ -64,12 +64,22 @@ Non-comprehensive list of changes in this release
|
||||
- For the ARM target, C-language intrinsics ``<arm_cde.h>`` for the CDE
|
||||
instruction set are now provided.
|
||||
|
||||
* clang adds support for a set of extended integer types (``_ExtInt(N)``) that
|
||||
- clang adds support for a set of extended integer types (``_ExtInt(N)``) that
|
||||
permit non-power of 2 integers, exposing the LLVM integer types. Since a major
|
||||
motivating use case for these types is to limit 'bit' usage, these types don't
|
||||
automatically promote to 'int' when operations are done between two ``ExtInt(N)``
|
||||
types, instead math occurs at the size of the largest ``ExtInt(N)`` type.
|
||||
|
||||
- Users of UBSan, PGO, and coverage on Windows will now need to add clang's
|
||||
library resource directory to their library search path. These features all
|
||||
use runtime libraries, and Clang provides these libraries in its resource
|
||||
directory. For example, if LLVM is installed in ``C:\Program Files\LLVM``,
|
||||
then the profile runtime library will appear at
|
||||
``C:\Program Files\LLVM\lib\clang\11.0.0\lib\windows\clang_rt.profile-x86_64.lib``.
|
||||
To ensure that the linker can find the appropriate library, users should pass
|
||||
``/LIBPATH:C:\Program Files\LLVM\lib\clang\11.0.0\lib\windows`` to the
|
||||
linker. If the user links the program with the ``clang`` or ``clang-cl``
|
||||
drivers, the driver will pass this flag for them.
|
||||
|
||||
|
||||
New Compiler Flags
|
||||
|
@ -3668,3 +3668,56 @@ This option is intended to be used as a temporary means to build projects where
|
||||
clang-cl cannot successfully compile all the files. clang-cl may fail to compile
|
||||
a file either because it cannot generate code for some C++ feature, or because
|
||||
it cannot parse some Microsoft language extension.
|
||||
|
||||
Finding Clang runtime libraries
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
clang-cl supports several features that require runtime library support:
|
||||
|
||||
- Address Sanitizer (ASan): ``-fsanitize=address``
|
||||
- Undefined Behavior Sanitizer (UBSan): ``-fsanitize=undefined``
|
||||
- Code coverage: ``-fprofile-instr-generate -fcoverage-mapping``
|
||||
- Profile Guided Optimization (PGO): ``-fprofile-instr-generate``
|
||||
- Certain math operations (int128 division) require the builtins library
|
||||
|
||||
In order to use these features, the user must link the right runtime libraries
|
||||
into their program. These libraries are distributed alongside Clang in the
|
||||
library resource directory. Clang searches for the resource directory by
|
||||
searching relative to the Clang executable. For example, if LLVM is installed
|
||||
in ``C:\Program Files\LLVM``, then the profile runtime library will be located
|
||||
at the path
|
||||
``C:\Program Files\LLVM\lib\clang\11.0.0\lib\windows\clang_rt.profile-x86_64.lib``.
|
||||
|
||||
For UBSan, PGO, and coverage, Clang will emit object files that auto-link the
|
||||
appropriate runtime library, but the user generally needs to help the linker
|
||||
(whether it is ``lld-link.exe`` or MSVC ``link.exe``) find the library resource
|
||||
directory. Using the example installation above, this would mean passing
|
||||
``/LIBPATH:C:\Program Files\LLVM\lib\clang\11.0.0\lib\windows`` to the linker.
|
||||
If the user links the program with the ``clang`` or ``clang-cl`` drivers, the
|
||||
driver will pass this flag for them.
|
||||
|
||||
If the linker cannot find the appropriate library, it will emit an error like
|
||||
this::
|
||||
|
||||
$ clang-cl -c -fsanitize=undefined t.cpp
|
||||
|
||||
$ lld-link t.obj -dll
|
||||
lld-link: error: could not open 'clang_rt.ubsan_standalone-x86_64.lib': no such file or directory
|
||||
lld-link: error: could not open 'clang_rt.ubsan_standalone_cxx-x86_64.lib': no such file or directory
|
||||
|
||||
$ link t.obj -dll -nologo
|
||||
LINK : fatal error LNK1104: cannot open file 'clang_rt.ubsan_standalone-x86_64.lib'
|
||||
|
||||
To fix the error, add the appropriate ``/libpath:`` flag to the link line.
|
||||
|
||||
For ASan, as of this writing, the user is also responsible for linking against
|
||||
the correct ASan libraries.
|
||||
|
||||
If the user is using the dynamic CRT (``/MD``), then they should add
|
||||
``clang_rt.asan_dynamic-x86_64.lib`` to the link line as a regular input. For
|
||||
other architectures, replace x86_64 with the appropriate name here and below.
|
||||
|
||||
If the user is using the static CRT (``/MT``), then different runtimes are used
|
||||
to produce DLLs and EXEs. To link a DLL, pass
|
||||
``clang_rt.asan_dll_thunk-x86_64.lib``. To link an EXE, pass
|
||||
``-wholearchive:clang_rt.asan-x86_64.lib``.
|
||||
|
@ -413,6 +413,11 @@ public:
|
||||
getCompilerRTArgString(const llvm::opt::ArgList &Args, StringRef Component,
|
||||
FileType Type = ToolChain::FT_Static) const;
|
||||
|
||||
std::string getCompilerRTBasename(const llvm::opt::ArgList &Args,
|
||||
StringRef Component,
|
||||
FileType Type = ToolChain::FT_Static,
|
||||
bool AddArch = true) const;
|
||||
|
||||
// Returns target specific runtime path if it exists.
|
||||
virtual Optional<std::string> getRuntimePath() const;
|
||||
|
||||
|
@ -955,22 +955,24 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
|
||||
if (TC.getTriple().isOSWindows() && needsUbsanRt()) {
|
||||
// Instruct the code generator to embed linker directives in the object file
|
||||
// that cause the required runtime libraries to be linked.
|
||||
CmdArgs.push_back(Args.MakeArgString(
|
||||
"--dependent-lib=" + TC.getCompilerRT(Args, "ubsan_standalone")));
|
||||
CmdArgs.push_back(
|
||||
Args.MakeArgString("--dependent-lib=" +
|
||||
TC.getCompilerRTBasename(Args, "ubsan_standalone")));
|
||||
if (types::isCXX(InputType))
|
||||
CmdArgs.push_back(Args.MakeArgString(
|
||||
"--dependent-lib=" + TC.getCompilerRT(Args, "ubsan_standalone_cxx")));
|
||||
"--dependent-lib=" +
|
||||
TC.getCompilerRTBasename(Args, "ubsan_standalone_cxx")));
|
||||
}
|
||||
if (TC.getTriple().isOSWindows() && needsStatsRt()) {
|
||||
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
|
||||
TC.getCompilerRT(Args, "stats_client")));
|
||||
CmdArgs.push_back(Args.MakeArgString(
|
||||
"--dependent-lib=" + TC.getCompilerRTBasename(Args, "stats_client")));
|
||||
|
||||
// The main executable must export the stats runtime.
|
||||
// FIXME: Only exporting from the main executable (e.g. based on whether the
|
||||
// translation unit defines main()) would save a little space, but having
|
||||
// multiple copies of the runtime shouldn't hurt.
|
||||
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
|
||||
TC.getCompilerRT(Args, "stats")));
|
||||
CmdArgs.push_back(Args.MakeArgString(
|
||||
"--dependent-lib=" + TC.getCompilerRTBasename(Args, "stats")));
|
||||
addIncludeLinkerOption(TC, Args, CmdArgs, "__sanitizer_stats_register");
|
||||
}
|
||||
|
||||
|
@ -390,8 +390,9 @@ std::string ToolChain::getCompilerRTPath() const {
|
||||
return std::string(Path.str());
|
||||
}
|
||||
|
||||
std::string ToolChain::getCompilerRT(const ArgList &Args, StringRef Component,
|
||||
FileType Type) const {
|
||||
std::string ToolChain::getCompilerRTBasename(const ArgList &Args,
|
||||
StringRef Component, FileType Type,
|
||||
bool AddArch) const {
|
||||
const llvm::Triple &TT = getTriple();
|
||||
bool IsITANMSVCWindows =
|
||||
TT.isWindowsMSVCEnvironment() || TT.isWindowsItaniumEnvironment();
|
||||
@ -413,18 +414,32 @@ std::string ToolChain::getCompilerRT(const ArgList &Args, StringRef Component,
|
||||
break;
|
||||
}
|
||||
|
||||
std::string ArchAndEnv;
|
||||
if (AddArch) {
|
||||
StringRef Arch = getArchNameForCompilerRTLib(*this, Args);
|
||||
const char *Env = TT.isAndroid() ? "-android" : "";
|
||||
ArchAndEnv = ("-" + Arch + Env).str();
|
||||
}
|
||||
return (Prefix + Twine("clang_rt.") + Component + ArchAndEnv + Suffix).str();
|
||||
}
|
||||
|
||||
std::string ToolChain::getCompilerRT(const ArgList &Args, StringRef Component,
|
||||
FileType Type) const {
|
||||
// Check for runtime files in the new layout without the architecture first.
|
||||
std::string CRTBasename =
|
||||
getCompilerRTBasename(Args, Component, Type, /*AddArch=*/false);
|
||||
for (const auto &LibPath : getLibraryPaths()) {
|
||||
SmallString<128> P(LibPath);
|
||||
llvm::sys::path::append(P, Prefix + Twine("clang_rt.") + Component + Suffix);
|
||||
llvm::sys::path::append(P, CRTBasename);
|
||||
if (getVFS().exists(P))
|
||||
return std::string(P.str());
|
||||
}
|
||||
|
||||
StringRef Arch = getArchNameForCompilerRTLib(*this, Args);
|
||||
const char *Env = TT.isAndroid() ? "-android" : "";
|
||||
// Fall back to the old expected compiler-rt name if the new one does not
|
||||
// exist.
|
||||
CRTBasename = getCompilerRTBasename(Args, Component, Type, /*AddArch=*/true);
|
||||
SmallString<128> Path(getCompilerRTPath());
|
||||
llvm::sys::path::append(Path, Prefix + Twine("clang_rt.") + Component + "-" +
|
||||
Arch + Env + Suffix);
|
||||
llvm::sys::path::append(Path, CRTBasename);
|
||||
return std::string(Path.str());
|
||||
}
|
||||
|
||||
|
@ -812,8 +812,8 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C,
|
||||
CmdArgs.push_back("-fprofile-instrument=clang");
|
||||
if (TC.getTriple().isWindowsMSVCEnvironment()) {
|
||||
// Add dependent lib for clang_rt.profile
|
||||
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
|
||||
TC.getCompilerRT(Args, "profile")));
|
||||
CmdArgs.push_back(Args.MakeArgString(
|
||||
"--dependent-lib=" + TC.getCompilerRTBasename(Args, "profile")));
|
||||
}
|
||||
}
|
||||
|
||||
@ -830,8 +830,9 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C,
|
||||
}
|
||||
if (PGOGenArg) {
|
||||
if (TC.getTriple().isWindowsMSVCEnvironment()) {
|
||||
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
|
||||
TC.getCompilerRT(Args, "profile")));
|
||||
// Add dependent lib for clang_rt.profile
|
||||
CmdArgs.push_back(Args.MakeArgString(
|
||||
"--dependent-lib=" + TC.getCompilerRTBasename(Args, "profile")));
|
||||
}
|
||||
if (PGOGenArg->getOption().matches(
|
||||
PGOGenerateArg ? options::OPT_fprofile_generate_EQ
|
||||
|
@ -350,6 +350,16 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
||||
Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath));
|
||||
}
|
||||
|
||||
// Add the compiler-rt library directories to libpath if they exist to help
|
||||
// the linker find the various sanitizer, builtin, and profiling runtimes.
|
||||
for (const auto &LibPath : TC.getLibraryPaths()) {
|
||||
if (TC.getVFS().exists(LibPath))
|
||||
CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));
|
||||
}
|
||||
auto CRTPath = TC.getCompilerRTPath();
|
||||
if (TC.getVFS().exists(CRTPath))
|
||||
CmdArgs.push_back(Args.MakeArgString("-libpath:" + CRTPath));
|
||||
|
||||
if (!C.getDriver().IsCLMode() && Args.hasArg(options::OPT_L))
|
||||
for (const auto &LibPath : Args.getAllArgValues(options::OPT_L))
|
||||
CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath));
|
||||
|
@ -67,11 +67,11 @@
|
||||
|
||||
// RUN: %clang_cl -### /FA -fprofile-instr-generate -- %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-INSTR-GENERATE %s
|
||||
// RUN: %clang_cl -### /FA -fprofile-instr-generate=/tmp/somefile.profraw -- %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-INSTR-GENERATE-FILE %s
|
||||
// CHECK-PROFILE-INSTR-GENERATE: "-fprofile-instrument=clang" "--dependent-lib={{[^"]*}}clang_rt.profile-{{[^"]*}}.lib"
|
||||
// CHECK-PROFILE-INSTR-GENERATE: "-fprofile-instrument=clang" "--dependent-lib=clang_rt.profile-{{[^"]*}}.lib"
|
||||
// CHECK-PROFILE-INSTR-GENERATE-FILE: "-fprofile-instrument-path=/tmp/somefile.profraw"
|
||||
|
||||
// RUN: %clang_cl -### /FA -fprofile-generate -- %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE %s
|
||||
// CHECK-PROFILE-GENERATE: "-fprofile-instrument=llvm" "--dependent-lib={{[^"]*}}clang_rt.profile-{{[^"]*}}.lib"
|
||||
// CHECK-PROFILE-GENERATE: "-fprofile-instrument=llvm" "--dependent-lib=clang_rt.profile-{{[^"]*}}.lib"
|
||||
|
||||
// RUN: %clang_cl -### /FA -fprofile-instr-generate -fprofile-instr-use -- %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
|
||||
// RUN: %clang_cl -### /FA -fprofile-instr-generate -fprofile-instr-use=file -- %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
|
||||
|
@ -656,16 +656,16 @@
|
||||
// RUN: -target x86_64-pc-windows \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-WIN64 %s
|
||||
// CHECK-CFI-STATS-WIN64: "--dependent-lib={{[^"]*}}clang_rt.stats_client-x86_64.lib"
|
||||
// CHECK-CFI-STATS-WIN64: "--dependent-lib={{[^"]*}}clang_rt.stats-x86_64.lib"
|
||||
// CHECK-CFI-STATS-WIN64: "--dependent-lib=clang_rt.stats_client-x86_64.lib"
|
||||
// CHECK-CFI-STATS-WIN64: "--dependent-lib=clang_rt.stats-x86_64.lib"
|
||||
// CHECK-CFI-STATS-WIN64: "--linker-option=/include:__sanitizer_stats_register"
|
||||
|
||||
// RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \
|
||||
// RUN: -target i686-pc-windows \
|
||||
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
|
||||
// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-WIN32 %s
|
||||
// CHECK-CFI-STATS-WIN32: "--dependent-lib={{[^"]*}}clang_rt.stats_client-i386.lib"
|
||||
// CHECK-CFI-STATS-WIN32: "--dependent-lib={{[^"]*}}clang_rt.stats-i386.lib"
|
||||
// CHECK-CFI-STATS-WIN32: "--dependent-lib=clang_rt.stats_client-i386.lib"
|
||||
// CHECK-CFI-STATS-WIN32: "--dependent-lib=clang_rt.stats-i386.lib"
|
||||
// CHECK-CFI-STATS-WIN32: "--linker-option=/include:___sanitizer_stats_register"
|
||||
|
||||
// RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
|
||||
|
@ -886,6 +886,27 @@ if (LLVM_BUILD_INSTRUMENTED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# When using clang-cl with an instrumentation-based tool, add clang's library
|
||||
# resource directory to the library search path. Because cmake invokes the
|
||||
# linker directly, it isn't sufficient to pass -fsanitize=* to the linker.
|
||||
if (CLANG_CL AND (LLVM_BUILD_INSTRUMENTED OR LLVM_USE_SANITIZER))
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_CXX_COMPILER} /clang:-print-resource-dir
|
||||
OUTPUT_VARIABLE clang_resource_dir
|
||||
ERROR_VARIABLE clang_cl_stderr
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE clang_cl_exit_code)
|
||||
if (NOT "${clang_cl_exit_code}" STREQUAL "0")
|
||||
message(FATAL_ERROR
|
||||
"Unable to invoke clang-cl to find resource dir: ${clang_cl_stderr}")
|
||||
endif()
|
||||
file(TO_CMAKE_PATH "${clang_resource_dir}" clang_resource_dir)
|
||||
append("/libpath:${clang_resource_dir}/lib/windows"
|
||||
CMAKE_EXE_LINKER_FLAGS
|
||||
CMAKE_SHARED_LINKER_FLAGS)
|
||||
endif()
|
||||
|
||||
if(LLVM_PROFDATA_FILE AND EXISTS ${LLVM_PROFDATA_FILE})
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
|
||||
append("-fprofile-instr-use=\"${LLVM_PROFDATA_FILE}\""
|
||||
|
Loading…
x
Reference in New Issue
Block a user