Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1043,6 +1043,10 @@ Group; def fno_sanitize_minimal_runtime : Flag<["-"], "fno-sanitize-minimal-runtime">, Group; +def fsanitize_no_runtime : Flag<["-"], "fsanitize-no-runtime">, + Group; +def fno_sanitize_no_runtime : Flag<["-"], "fno-sanitize-no-runtime">, + Group; def fsanitize_link_cxx_runtime : Flag<["-"], "fsanitize-link-c++-runtime">, Group; def fsanitize_cfi_cross_dso : Flag<["-"], "fsanitize-cfi-cross-dso">, Index: clang/include/clang/Driver/SanitizerArgs.h =================================================================== --- clang/include/clang/Driver/SanitizerArgs.h +++ clang/include/clang/Driver/SanitizerArgs.h @@ -21,6 +21,17 @@ class ToolChain; class SanitizerArgs { + enum SanitizerRuntimeKind { + /// Don't use any sanitizer runtime. + SRT_None, + + /// Use minimal sanitizer runtime. + SRT_Minimal, + + /// Regular sanitizer runtime. + SRT_Regular, + }; + SanitizerSet Sanitizers; SanitizerSet RecoverableSanitizers; SanitizerSet TrapSanitizers; @@ -48,7 +59,7 @@ bool TsanMemoryAccess = true; bool TsanFuncEntryExit = true; bool TsanAtomics = true; - bool MinimalRuntime = false; + SanitizerRuntimeKind RT = SRT_Regular; // True if cross-dso CFI support if provided by the system (i.e. Android). bool ImplicitCfiRuntime = false; @@ -58,24 +69,39 @@ bool needsSharedRt() const { return SharedRuntime; } - bool needsAsanRt() const { return Sanitizers.has(SanitizerKind::Address); } - bool needsHwasanRt() const { return Sanitizers.has(SanitizerKind::HWAddress); } - bool needsTsanRt() const { return Sanitizers.has(SanitizerKind::Thread); } - bool needsMsanRt() const { return Sanitizers.has(SanitizerKind::Memory); } - bool needsFuzzer() const { return Sanitizers.has(SanitizerKind::Fuzzer); } + bool needsAsanRt() const { + return Sanitizers.has(SanitizerKind::Address) && RT != SRT_None; + } + bool needsHwasanRt() const { + return Sanitizers.has(SanitizerKind::HWAddress) && RT != SRT_None; + } + bool needsTsanRt() const { + return Sanitizers.has(SanitizerKind::Thread) && RT != SRT_None; + } + bool needsMsanRt() const { + return Sanitizers.has(SanitizerKind::Memory) && RT != SRT_None; + } + bool needsFuzzer() const { + return Sanitizers.has(SanitizerKind::Fuzzer) && RT != SRT_None; + } bool needsLsanRt() const { return Sanitizers.has(SanitizerKind::Leak) && !Sanitizers.has(SanitizerKind::Address) && - !Sanitizers.has(SanitizerKind::HWAddress); + !Sanitizers.has(SanitizerKind::HWAddress) && RT != SRT_None; } bool needsUbsanRt() const; - bool requiresMinimalRuntime() const { return MinimalRuntime; } - bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); } - bool needsSafeStackRt() const { return SafeStackRuntime; } + bool requiresRuntime() const { return RT != SRT_None; } + bool requiresMinimalRuntime() const { return RT == SRT_Minimal; } + bool needsDfsanRt() const { + return Sanitizers.has(SanitizerKind::DataFlow) && RT != SRT_None; + } + bool needsSafeStackRt() const { return SafeStackRuntime && RT != SRT_None; } bool needsCfiRt() const; bool needsCfiDiagRt() const; - bool needsStatsRt() const { return Stats; } - bool needsScudoRt() const { return Sanitizers.has(SanitizerKind::Scudo); } + bool needsStatsRt() const { return Stats && RT != SRT_None; } + bool needsScudoRt() const { + return Sanitizers.has(SanitizerKind::Scudo) && RT != SRT_None; + } bool requiresPIE() const; bool needsUnwindTables() const; Index: clang/lib/Driver/SanitizerArgs.cpp =================================================================== --- clang/lib/Driver/SanitizerArgs.cpp +++ clang/lib/Driver/SanitizerArgs.cpp @@ -208,7 +208,7 @@ // All of these include ubsan. if (needsAsanRt() || needsMsanRt() || needsHwasanRt() || needsTsanRt() || needsDfsanRt() || needsLsanRt() || needsCfiDiagRt() || - (needsScudoRt() && !requiresMinimalRuntime())) + (needsScudoRt() && !requiresMinimalRuntime()) || RT == SRT_None) return false; return (Sanitizers.Mask & NeedsUbsanRt & ~TrapSanitizers.Mask) || @@ -217,12 +217,12 @@ bool SanitizerArgs::needsCfiRt() const { return !(Sanitizers.Mask & SanitizerKind::CFI & ~TrapSanitizers.Mask) && - CfiCrossDso && !ImplicitCfiRuntime; + CfiCrossDso && !ImplicitCfiRuntime && RT != SRT_None; } bool SanitizerArgs::needsCfiDiagRt() const { return (Sanitizers.Mask & SanitizerKind::CFI & ~TrapSanitizers.Mask) && - CfiCrossDso && !ImplicitCfiRuntime; + CfiCrossDso && !ImplicitCfiRuntime && RT != SRT_None; } bool SanitizerArgs::requiresPIE() const { @@ -261,9 +261,21 @@ SanitizerMask TrappingKinds = parseSanitizeTrapArgs(D, Args); SanitizerMask InvalidTrappingKinds = TrappingKinds & NotAllowedWithTrap; - MinimalRuntime = - Args.hasFlag(options::OPT_fsanitize_minimal_runtime, - options::OPT_fno_sanitize_minimal_runtime, MinimalRuntime); + auto *RuntimeArg = Args.getLastArg( + options::OPT_fsanitize_no_runtime, options::OPT_fno_sanitize_no_runtime, + options::OPT_fsanitize_minimal_runtime, options::OPT_fno_sanitize_minimal_runtime); + + if (RuntimeArg) { + Option O = RuntimeArg->getOption(); + if (O.matches(options::OPT_fsanitize_no_runtime)) + RT = SRT_None; + if (O.matches(options::OPT_fno_sanitize_no_runtime)) + RT = SRT_Regular; + if (O.matches(options::OPT_fsanitize_minimal_runtime)) + RT = SRT_Minimal; + if (O.matches(options::OPT_fno_sanitize_minimal_runtime)) + RT = SRT_Regular; + } // The object size sanitizer should not be enabled at -O0. Arg *OptLevel = Args.getLastArg(options::OPT_O_Group); @@ -303,7 +315,7 @@ } Add &= ~InvalidTrappingKinds; - if (MinimalRuntime) { + if (requiresMinimalRuntime()) { if (SanitizerMask KindsToDiagnose = Add & NotAllowedWithMinimalRuntime & ~DiagnosedKinds) { std::string Desc = describeSanitizeArg(*I, KindsToDiagnose); @@ -367,7 +379,7 @@ // Silently discard any unsupported sanitizers implicitly enabled through // group expansion. Add &= ~InvalidTrappingKinds; - if (MinimalRuntime) { + if (requiresMinimalRuntime()) { Add &= ~NotAllowedWithMinimalRuntime; } if (CfiCrossDso) @@ -640,7 +652,7 @@ Stats = Args.hasFlag(options::OPT_fsanitize_stats, options::OPT_fno_sanitize_stats, false); - if (MinimalRuntime) { + if (requiresMinimalRuntime()) { SanitizerMask IncompatibleMask = Kinds & ~setGroupBits(CompatibleWithMinimalRuntime); if (IncompatibleMask) @@ -972,7 +984,9 @@ if (Stats) CmdArgs.push_back("-fsanitize-stats"); - if (MinimalRuntime) + if (RT == SRT_None) + CmdArgs.push_back("-fsanitize-no-runtime"); + else if (RT == SRT_Minimal) CmdArgs.push_back("-fsanitize-minimal-runtime"); if (AsanFieldPadding) Index: clang/test/Driver/sanitizer-ld.c =================================================================== --- clang/test/Driver/sanitizer-ld.c +++ clang/test/Driver/sanitizer-ld.c @@ -28,6 +28,14 @@ // RUN: --sysroot=%S/Inputs/basic_linux_tree \ // RUN: | FileCheck --check-prefix=CHECK-SHARED-ASAN-LINUX %s +// RUN: %clang -fsanitize=address -fsanitize-no-runtime %s -### -o %t.o 2>&1 \ +// RUN: -target x86_64-unknown-linux -fuse-ld=ld \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-ASAN-NO-RUNTIME-LINUX %s +// +// CHECK-ASAN-NO-RUNTIME-LINUX-NOT: libclang_rt.asan-x86_64.a" + // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target i386-unknown-linux -fuse-ld=ld -fsanitize=address \ // RUN: -shared-libsan -static-libsan -shared-libasan \