https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/188290
>From 5a5209507fff2d9c3aa4686ad782b0f764600c55 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Tue, 24 Mar 2026 11:14:15 -0500 Subject: [PATCH 1/2] [Clang] Correctly handle UBSan libraries for the GPU Summary: This PR adds the necessary clang driver plumbing to forward UBSan arguments on the GPU targets. These are currently only forwarded via the offloading languages if the user has the relevant library installed. Enables the support in https://github.com/llvm/llvm-project/pull/188289 --- clang/lib/Driver/ToolChains/AMDGPU.cpp | 1 + clang/lib/Driver/ToolChains/AMDGPU.h | 18 ++++++++++------ clang/lib/Driver/ToolChains/Clang.cpp | 20 +++++++++++++++++- clang/lib/Driver/ToolChains/CommonArgs.cpp | 2 +- clang/lib/Driver/ToolChains/Cuda.cpp | 1 + .../libclang_rt.ubsan_minimal.a | 0 .../libclang_rt.ubsan_minimal.a | 0 clang/test/Driver/amdgpu-toolchain.c | 7 +++++++ clang/test/Driver/cuda-cross-compiling.c | 7 +++++++ clang/test/Driver/openmp-offload-gpu.c | 21 +++++++++++++++++++ 10 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 clang/test/Driver/Inputs/resource_dir_with_per_target_subdir/lib/amdgcn-amd-amdhsa/libclang_rt.ubsan_minimal.a create mode 100644 clang/test/Driver/Inputs/resource_dir_with_per_target_subdir/lib/nvptx64-nvidia-cuda/libclang_rt.ubsan_minimal.a diff --git a/clang/lib/Driver/ToolChains/AMDGPU.cpp b/clang/lib/Driver/ToolChains/AMDGPU.cpp index 03bd88f0d4f47..af44448240455 100644 --- a/clang/lib/Driver/ToolChains/AMDGPU.cpp +++ b/clang/lib/Driver/ToolChains/AMDGPU.cpp @@ -633,6 +633,7 @@ void amdgpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, } getToolChain().addProfileRTLibs(Args, CmdArgs); + addSanitizerRuntimes(getToolChain(), Args, CmdArgs); if (Args.hasArg(options::OPT_stdlib)) CmdArgs.append({"-lc", "-lm"}); diff --git a/clang/lib/Driver/ToolChains/AMDGPU.h b/clang/lib/Driver/ToolChains/AMDGPU.h index 4dd8188842f83..93eb441385949 100644 --- a/clang/lib/Driver/ToolChains/AMDGPU.h +++ b/clang/lib/Driver/ToolChains/AMDGPU.h @@ -152,7 +152,8 @@ class LLVM_LIBRARY_VISIBILITY ROCMToolChain : public AMDGPUToolChain { Action::OffloadKind DeviceOffloadingKind) const; SanitizerMask getSupportedSanitizers() const override { - return SanitizerKind::Address; + return SanitizerKind::Address | SanitizerKind::Undefined | + SanitizerKind::UndefinedGroup; } bool diagnoseUnsupportedOption(const llvm::opt::Arg *A, @@ -202,12 +203,16 @@ class LLVM_LIBRARY_VISIBILITY ROCMToolChain : public AMDGPUToolChain { SmallVector<const char *, 4> SupportedSanitizers; SmallVector<const char *, 4> UnSupportedSanitizers; + SanitizerMask Supported = ROCMToolChain::getSupportedSanitizers(); + SanitizerMask SupportedMask; for (const char *Value : A->getValues()) { - SanitizerMask K = parseSanitizerValue(Value, /*Allow Groups*/ false); - if (K & ROCMToolChain::getSupportedSanitizers()) + SanitizerMask K = parseSanitizerValue(Value, /*Allow Groups*/ true); + if (K & Supported) { SupportedSanitizers.push_back(Value); - else + SupportedMask |= K; + } else { UnSupportedSanitizers.push_back(Value); + } } // If there are no supported sanitizers, drop the whole argument. @@ -221,8 +226,9 @@ class LLVM_LIBRARY_VISIBILITY ROCMToolChain : public AMDGPUToolChain { diagnoseUnsupportedOption(A, DAL, DriverArgs, Value); } } - // If we know the target arch, check if the sanitizer is supported for it. - if (shouldSkipSanitizeOption(TC, DriverArgs, TargetID, A)) + // The xnack+ feature is only required for ASan on AMDGPU. + if ((SupportedMask & SanitizerKind::Address) && + shouldSkipSanitizeOption(TC, DriverArgs, TargetID, A)) return true; // Add a new argument with only the supported sanitizers. diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 6416baf9126ff..d437939db67b5 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -9361,7 +9361,13 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, OPT_fprofile_generate, OPT_fprofile_generate_EQ, OPT_fprofile_instr_generate, - OPT_fprofile_instr_generate_EQ}; + OPT_fprofile_instr_generate_EQ, + OPT_fsanitize_EQ, + OPT_fno_sanitize_EQ, + OPT_fsanitize_minimal_runtime, + OPT_fno_sanitize_minimal_runtime, + OPT_fsanitize_trap_EQ, + OPT_fno_sanitize_trap_EQ}; const llvm::DenseSet<unsigned> LinkerOptions{OPT_mllvm, OPT_Zlinker_input}; auto ShouldForwardForToolChain = [&](Arg *A, const ToolChain &TC) { auto HasProfileRT = TC.getVFS().exists( @@ -9374,6 +9380,18 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, A->getOption().matches(OPT_fprofile_instr_generate) || A->getOption().matches(OPT_fprofile_instr_generate_EQ))) return false; + auto HasUBSanRT = TC.getVFS().exists( + TC.getCompilerRT(Args, "ubsan_minimal", ToolChain::FT_Static)); + // Don't forward sanitizer arguments if the toolchain doesn't support it. + // Without this check using it on the host would result in linker errors. + if (!HasUBSanRT && + (A->getOption().matches(OPT_fsanitize_EQ) || + A->getOption().matches(OPT_fno_sanitize_EQ) || + A->getOption().matches(OPT_fsanitize_minimal_runtime) || + A->getOption().matches(OPT_fno_sanitize_minimal_runtime) || + A->getOption().matches(OPT_fsanitize_trap_EQ) || + A->getOption().matches(OPT_fno_sanitize_trap_EQ))) + return false; // Don't forward -mllvm to toolchains that don't support LLVM. return TC.HasNativeLLVMSupport() || A->getOption().getID() != OPT_mllvm; }; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 9a17fa2546e68..d115a7b26fa06 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1789,7 +1789,7 @@ bool tools::addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, } // If there is a static runtime with no dynamic list, force all the symbols // to be dynamic to be sure we export sanitizer interface functions. - if (AddExportDynamic) + if (AddExportDynamic && !TC.getTriple().isNVPTX()) CmdArgs.push_back("--export-dynamic"); if (SanArgs.hasCrossDsoCfi() && !AddExportDynamic) diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp b/clang/lib/Driver/ToolChains/Cuda.cpp index dc94fca06db7d..2a968fcd88402 100644 --- a/clang/lib/Driver/ToolChains/Cuda.cpp +++ b/clang/lib/Driver/ToolChains/Cuda.cpp @@ -645,6 +645,7 @@ void NVPTX::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString(Twine("-L") + DefaultLibPath)); getToolChain().addProfileRTLibs(Args, CmdArgs); + addSanitizerRuntimes(getToolChain(), Args, CmdArgs); if (Args.hasArg(options::OPT_stdlib)) CmdArgs.append({"-lc", "-lm"}); diff --git a/clang/test/Driver/Inputs/resource_dir_with_per_target_subdir/lib/amdgcn-amd-amdhsa/libclang_rt.ubsan_minimal.a b/clang/test/Driver/Inputs/resource_dir_with_per_target_subdir/lib/amdgcn-amd-amdhsa/libclang_rt.ubsan_minimal.a new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/resource_dir_with_per_target_subdir/lib/nvptx64-nvidia-cuda/libclang_rt.ubsan_minimal.a b/clang/test/Driver/Inputs/resource_dir_with_per_target_subdir/lib/nvptx64-nvidia-cuda/libclang_rt.ubsan_minimal.a new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/amdgpu-toolchain.c b/clang/test/Driver/amdgpu-toolchain.c index 2a48ca6bb7670..54c9a5703c081 100644 --- a/clang/test/Driver/amdgpu-toolchain.c +++ b/clang/test/Driver/amdgpu-toolchain.c @@ -52,3 +52,10 @@ // RUN: -fprofile-generate %s 2>&1 | FileCheck -check-prefixes=PROFILE %s // PROFILE: ld.lld // PROFILE-SAME: "[[RESOURCE_DIR:.+]]{{/|\\\\}}lib{{/|\\\\}}amdgcn-amd-amdhsa{{/|\\\\}}libclang_rt.profile.a" + +// RUN: %clang -### --target=amdgcn-amd-amdhsa -mcpu=gfx906 -nogpulib \ +// RUN: -resource-dir=%S/Inputs/resource_dir_with_per_target_subdir \ +// RUN: -fsanitize=undefined -fsanitize-minimal-runtime %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=UBSAN %s +// UBSAN: ld.lld +// UBSAN-SAME: "[[RESOURCE_DIR:.+]]{{/|\\\\}}lib{{/|\\\\}}amdgcn-amd-amdhsa{{/|\\\\}}libclang_rt.ubsan_minimal.a" diff --git a/clang/test/Driver/cuda-cross-compiling.c b/clang/test/Driver/cuda-cross-compiling.c index 1dea9426f75ce..2bfe9f2a228ac 100644 --- a/clang/test/Driver/cuda-cross-compiling.c +++ b/clang/test/Driver/cuda-cross-compiling.c @@ -118,3 +118,10 @@ // RUN: -fprofile-generate %s 2>&1 | FileCheck -check-prefixes=PROFILE %s // PROFILE: clang-nvlink-wrapper // PROFILE-SAME: "[[RESOURCE_DIR:.+]]{{/|\\\\}}lib{{/|\\\\}}nvptx64-nvidia-cuda{{/|\\\\}}libclang_rt.profile.a" + +// RUN: %clang -### --target=nvptx64-nvidia-cuda -march=sm_89 -nogpulib \ +// RUN: -resource-dir=%S/Inputs/resource_dir_with_per_target_subdir \ +// RUN: -fsanitize=undefined -fsanitize-minimal-runtime %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=UBSAN %s +// UBSAN: clang-nvlink-wrapper +// UBSAN-SAME: "[[RESOURCE_DIR:.+]]{{/|\\\\}}lib{{/|\\\\}}nvptx64-nvidia-cuda{{/|\\\\}}libclang_rt.ubsan_minimal.a" diff --git a/clang/test/Driver/openmp-offload-gpu.c b/clang/test/Driver/openmp-offload-gpu.c index e057959d62044..9115e86a599bc 100644 --- a/clang/test/Driver/openmp-offload-gpu.c +++ b/clang/test/Driver/openmp-offload-gpu.c @@ -428,3 +428,24 @@ // RUN: | FileCheck --check-prefix=NO-PROFILE %s // // NO-PROFILE-NOT: --device-compiler=amdgcn-amd-amdhsa=-fprofile-generate + +// +// Check that `-fsanitize=` flags are forwarded to link in the runtime +// only if present in the resource directory. +// +// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fopenmp=libomp \ +// RUN: -resource-dir=%S/Inputs/resource_dir_with_per_target_subdir \ +// RUN: --offload-arch=gfx906 -fsanitize=undefined \ +// RUN: -fsanitize-minimal-runtime -nogpulib -nogpuinc %s 2>&1 \ +// RUN: | FileCheck --check-prefix=UBSAN %s +// +// UBSAN: clang-linker-wrapper{{.*}}--device-compiler=amdgcn-amd-amdhsa=-fsanitize=undefined +// UBSAN-SAME: --device-compiler=amdgcn-amd-amdhsa=-fsanitize-minimal-runtime +// +// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fopenmp=libomp \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: --offload-arch=gfx906 -fsanitize=undefined \ +// RUN: -fsanitize-minimal-runtime -nogpulib -nogpuinc %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO-UBSAN %s +// +// NO-UBSAN-NOT: --device-compiler=amdgcn-amd-amdhsa=-fsanitize=undefined >From 23b2208d6cd7c8e87be558598d4d5cfc42309560 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Wed, 25 Mar 2026 08:26:45 -0500 Subject: [PATCH 2/2] permit trap w/o runtime --- clang/lib/Driver/ToolChains/Clang.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index d437939db67b5..50ce8e9ce81bd 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -9388,9 +9388,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, (A->getOption().matches(OPT_fsanitize_EQ) || A->getOption().matches(OPT_fno_sanitize_EQ) || A->getOption().matches(OPT_fsanitize_minimal_runtime) || - A->getOption().matches(OPT_fno_sanitize_minimal_runtime) || - A->getOption().matches(OPT_fsanitize_trap_EQ) || - A->getOption().matches(OPT_fno_sanitize_trap_EQ))) + A->getOption().matches(OPT_fno_sanitize_minimal_runtime))) return false; // Don't forward -mllvm to toolchains that don't support LLVM. return TC.HasNativeLLVMSupport() || A->getOption().getID() != OPT_mllvm; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
