diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -64,12 +64,22 @@ - For the ARM target, C-language intrinsics ```` 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 diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -3668,3 +3668,56 @@ 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``. diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -413,6 +413,11 @@ 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 getRuntimePath() const; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -955,22 +955,24 @@ 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"); } diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -390,8 +390,9 @@ 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 @@ 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()); } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -812,8 +812,8 @@ 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 @@ } 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 diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -350,6 +350,16 @@ 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)); diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -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 diff --git a/clang/test/Driver/sanitizer-ld.c b/clang/test/Driver/sanitizer-ld.c --- a/clang/test/Driver/sanitizer-ld.c +++ b/clang/test/Driver/sanitizer-ld.c @@ -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 \ diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -886,6 +886,27 @@ 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}\""