Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -49,7 +49,6 @@ bool needsUbsanRt() const; bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); } - bool sanitizesVptr() const { return Sanitizers.has(SanitizerKind::Vptr); } bool requiresPIE() const; bool needsUnwindTables() const; bool linkCXXRuntimes() const { return LinkCXXRuntimes; } Index: include/clang/Driver/ToolChain.h =================================================================== --- include/clang/Driver/ToolChain.h +++ include/clang/Driver/ToolChain.h @@ -53,10 +53,20 @@ RLT_Libgcc }; + enum RTTIMode { + RM_EnabledExplicitly, + RM_EnabledImplicitly, + RM_DisabledExplicitly, + RM_DisabledImplicitly + }; + private: const Driver &D; const llvm::Triple Triple; const llvm::opt::ArgList &Args; + // We need to initialize CachedRTTIArg before CachedRTTIMode + const llvm::opt::Arg *CachedRTTIArg; + const RTTIMode CachedRTTIMode; /// The list of toolchain specific path prefixes to search for /// files. @@ -134,6 +144,12 @@ const SanitizerArgs& getSanitizerArgs() const; + // Returns the Arg * that explicitly turned on/off rtti, or nullptr. + const llvm::opt::Arg *getRTTIArg() const { return CachedRTTIArg; } + + // Returns the RTTIMode for the toolchain with the current arguments. + RTTIMode getRTTIMode() const { return CachedRTTIMode; } + // Tool access. /// TranslateArgs - Create a new derived argument list for any argument Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -171,6 +171,8 @@ // Used to deduplicate diagnostics. unsigned Kinds = 0; unsigned NotSupported = getToolchainUnsupportedKinds(TC); + ToolChain::RTTIMode RTTIMode = TC.getRTTIMode(); + const Driver &D = TC.getDriver(); for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend(); I != E; ++I) { @@ -193,6 +195,28 @@ } Add &= ~NotSupported; + // Test for -fno-rtti + explicit -fsanitizer=vptr before expanding groups + // so we don't error out if -fno-rtti and -fsanitize=undefined were + // passed. + if (Add & SanitizeKind::Vptr && + (RTTIMode == ToolChain::RM_DisabledImplicitly || + RTTIMode == ToolChain::RM_DisabledExplicitly)) { + if (RTTIMode == ToolChain::RM_DisabledImplicitly) + // Warn about not having rtti enabled if the vptr sanitizer is + // explicitly enabled + D.Diag(diag::warn_drv_disabling_vptr_no_rtti_default); + else { + const llvm::opt::Arg *NoRTTIArg = TC.getRTTIArg(); + assert(NoRTTIArg && + "RTTI disabled explicitly but we have no argument!"); + D.Diag(diag::err_drv_argument_not_allowed_with) + << "-fsanitize=vptr" << NoRTTIArg->getAsString(Args); + } + + // Take out the Vptr sanitizer from the enabled sanitizers + AllRemove |= static_cast(SanitizeKind::Vptr); + } + Add = expandGroups(Add); // Group expansion may have enabled a sanitizer which is disabled later. Add &= ~AllRemove; @@ -209,6 +233,16 @@ } addAllOf(Sanitizers, Kinds); + // We disable the vptr sanitizer if rtti is disabled and -fsanitize=undefined + // was passed. + if (Sanitizers.has(SanitizerKind::Vptr) && + (RTTIMode == ToolChain::RM_DisabledImplicitly || + RTTIMode == ToolChain::RM_DisabledExplicitly)) { + // Make sure Kinds is up to date too. + Kinds &= ~SanitizeKind::Vptr; + Sanitizers.set(SanitizerKind::Vptr, 0); + } + // Parse -f(no-)?sanitize-recover flags. unsigned RecoverableKinds = RecoverableByDefault; unsigned DiagnosedUnrecoverableKinds = 0; Index: lib/Driver/ToolChain.cpp =================================================================== --- lib/Driver/ToolChain.cpp +++ lib/Driver/ToolChain.cpp @@ -26,14 +26,47 @@ using namespace clang; using namespace llvm::opt; +static llvm::opt::Arg *GetRTTIArgument(const ArgList &Args) { + return Args.getLastArg(options::OPT_mkernel, options::OPT_fapple_kext, + options::OPT_fno_rtti, options::OPT_frtti); +} + +static ToolChain::RTTIMode CalculateRTTIMode(const ArgList &Args, + const llvm::Triple &Triple, + const Arg *CachedRTTIArg) { + // Explicit rtti/no-rtti args + if (CachedRTTIArg) { + if (CachedRTTIArg->getOption().matches(options::OPT_frtti)) + return ToolChain::RM_EnabledExplicitly; + else + return ToolChain::RM_DisabledExplicitly; + } + + // -frtti is default, except for the PS4 CPU. + if (!Triple.isPS4CPU()) + return ToolChain::RM_EnabledImplicitly; + + // On the PS4, turning on c++ exceptions turns on rtti. + // We're assuming that, if we see -fexceptions, rtti gets turned on. + Arg *Exceptions = Args.getLastArg( + options::OPT_fcxx_exceptions, options::OPT_fno_cxx_exceptions, + options::OPT_fexceptions, options::OPT_fno_exceptions); + if (Exceptions && + (Exceptions->getOption().matches(options::OPT_fexceptions) || + Exceptions->getOption().matches(options::OPT_fcxx_exceptions))) + return ToolChain::RM_EnabledImplicitly; + + return ToolChain::RM_DisabledImplicitly; +} + ToolChain::ToolChain(const Driver &D, const llvm::Triple &T, const ArgList &Args) - : D(D), Triple(T), Args(Args) { + : D(D), Triple(T), Args(Args), CachedRTTIArg(GetRTTIArgument(Args)), + CachedRTTIMode(CalculateRTTIMode(Args, Triple, CachedRTTIArg)) { if (Arg *A = Args.getLastArg(options::OPT_mthread_model)) if (!isThreadModelSupported(A->getValue())) D.Diag(diag::err_drv_invalid_thread_model_for_target) - << A->getValue() - << A->getAsString(Args); + << A->getValue() << A->getAsString(Args); } ToolChain::~ToolChain() { Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -1931,16 +1931,17 @@ return false; } -/// addExceptionArgs - Adds exception related arguments to the driver command -/// arguments. There's a master flag, -fexceptions and also language specific -/// flags to enable/disable C++ and Objective-C exceptions. -/// This makes it possible to for example disable C++ exceptions but enable -/// Objective-C exceptions. +/// Adds exception related arguments to the driver command arguments. There's a +/// master flag, -fexceptions and also language specific flags to enable/disable +/// C++ and Objective-C exceptions. This makes it possible to for example +/// disable C++ exceptions but enable Objective-C exceptions. static void addExceptionArgs(const ArgList &Args, types::ID InputType, - const llvm::Triple &Triple, - bool KernelOrKext, + const ToolChain &TC, bool KernelOrKext, const ObjCRuntime &objcRuntime, ArgStringList &CmdArgs) { + const Driver &D = TC.getDriver(); + const llvm::Triple &Triple = TC.getTriple(); + if (KernelOrKext) { // -mkernel and -fapple-kext imply no exceptions, so claim exception related // arguments now to avoid warnings about unused arguments. @@ -1970,15 +1971,29 @@ if (types::isCXX(InputType)) { bool CXXExceptionsEnabled = Triple.getArch() != llvm::Triple::xcore && !Triple.isPS4CPU(); - if (Arg *A = Args.getLastArg(options::OPT_fcxx_exceptions, - options::OPT_fno_cxx_exceptions, - options::OPT_fexceptions, - options::OPT_fno_exceptions)) + Arg *ExceptionArg = Args.getLastArg( + options::OPT_fcxx_exceptions, options::OPT_fno_cxx_exceptions, + options::OPT_fexceptions, options::OPT_fno_exceptions); + if (ExceptionArg) CXXExceptionsEnabled = - A->getOption().matches(options::OPT_fcxx_exceptions) || - A->getOption().matches(options::OPT_fexceptions); + ExceptionArg->getOption().matches(options::OPT_fcxx_exceptions) || + ExceptionArg->getOption().matches(options::OPT_fexceptions); if (CXXExceptionsEnabled) { + if (Triple.isPS4CPU()) { + ToolChain::RTTIMode RTTIMode = TC.getRTTIMode(); + assert(ExceptionArg && + "On the PS4 exceptions should only be enabled if passing " + "an argument"); + if (RTTIMode == ToolChain::RM_DisabledExplicitly) { + const Arg *RTTIArg = TC.getRTTIArg(); + assert(RTTIArg && "RTTI disabled explicitly but no RTTIArg!"); + D.Diag(diag::err_drv_argument_not_allowed_with) + << RTTIArg->getAsString(Args) << ExceptionArg->getAsString(Args); + } else if (RTTIMode == ToolChain::RM_EnabledImplicitly) + D.Diag(diag::warn_drv_enabling_rtti_with_exceptions); + } + CmdArgs.push_back("-fcxx-exceptions"); EH = true; @@ -4004,56 +4019,11 @@ false)) CmdArgs.push_back("-fno-elide-constructors"); - // -frtti is default, except for the PS4 CPU. - if (!Args.hasFlag(options::OPT_frtti, options::OPT_fno_rtti, - !Triple.isPS4CPU()) || - KernelOrKext) { - bool IsCXX = types::isCXX(InputType); - bool RTTIEnabled = false; - Arg *NoRTTIArg = Args.getLastArg( - options::OPT_mkernel, options::OPT_fapple_kext, options::OPT_fno_rtti); - - // PS4 requires rtti when exceptions are enabled. If -fno-rtti was - // explicitly passed, error out. Otherwise enable rtti and emit a - // warning. - Arg *Exceptions = Args.getLastArg( - options::OPT_fcxx_exceptions, options::OPT_fno_cxx_exceptions, - options::OPT_fexceptions, options::OPT_fno_exceptions); - if (Triple.isPS4CPU() && Exceptions) { - bool CXXExceptions = - (IsCXX && - Exceptions->getOption().matches(options::OPT_fexceptions)) || - Exceptions->getOption().matches(options::OPT_fcxx_exceptions); - if (CXXExceptions) { - if (NoRTTIArg) - D.Diag(diag::err_drv_argument_not_allowed_with) - << NoRTTIArg->getAsString(Args) << Exceptions->getAsString(Args); - else { - RTTIEnabled = true; - D.Diag(diag::warn_drv_enabling_rtti_with_exceptions); - } - } - } + ToolChain::RTTIMode RTTIMode = getToolChain().getRTTIMode(); - // -fno-rtti cannot usefully be combined with -fsanitize=vptr. - if (Sanitize.sanitizesVptr()) { - // If rtti was explicitly disabled and the vptr sanitizer is on, error - // out. Otherwise, warn that vptr will be disabled unless -frtti is - // passed. - if (NoRTTIArg) { - D.Diag(diag::err_drv_argument_not_allowed_with) - << "-fsanitize=vptr" << NoRTTIArg->getAsString(Args); - } else { - D.Diag(diag::warn_drv_disabling_vptr_no_rtti_default); - // All sanitizer switches have been pushed. This -fno-sanitize - // will override any -fsanitize={vptr,undefined} passed before it. - CmdArgs.push_back("-fno-sanitize=vptr"); - } - } - - if (!RTTIEnabled) - CmdArgs.push_back("-fno-rtti"); - } + if (RTTIMode == ToolChain::RM_DisabledExplicitly || + RTTIMode == ToolChain::RM_DisabledImplicitly) + CmdArgs.push_back("-fno-rtti"); // -fshort-enums=0 is default for all architectures except Hexagon. if (Args.hasFlag(options::OPT_fshort_enums, @@ -4231,7 +4201,7 @@ // Handle GCC-style exception args. if (!C.getDriver().IsCLMode()) - addExceptionArgs(Args, InputType, getToolChain().getTriple(), KernelOrKext, + addExceptionArgs(Args, InputType, getToolChain(), KernelOrKext, objcRuntime, CmdArgs); if (getToolChain().UseSjLjExceptions()) Index: test/Driver/fsanitize.c =================================================================== --- test/Driver/fsanitize.c +++ test/Driver/fsanitize.c @@ -31,9 +31,11 @@ // CHECK-UNDEFINED-TRAP-ON-ERROR-VPTR: '-fsanitize=vptr' not allowed with '-fsanitize-undefined-trap-on-error' // RUN: %clang -target x86_64-linux-gnu -fsanitize=vptr -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-NO-RTTI -// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-NO-RTTI // CHECK-VPTR-NO-RTTI: '-fsanitize=vptr' not allowed with '-fno-rtti' +// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-NO-RTTI +// CHECK-UNDEFINED-NO-RTTI-NOT: vptr + // RUN: %clang -target x86_64-linux-gnu -fsanitize=address,thread -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANA-SANT // CHECK-SANA-SANT: '-fsanitize=address' not allowed with '-fsanitize=thread' Index: test/Driver/rtti-options.cpp =================================================================== --- test/Driver/rtti-options.cpp +++ test/Driver/rtti-options.cpp @@ -3,19 +3,23 @@ // No warnings/errors should be emitted for unknown, except if combining // the vptr sanitizer with -fno-rtti +// RUN: %clang -### -c -fno-rtti -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-RTTI %s +// RUN: %clang -### -c -frtti -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-NO-RTTI %s + // -fsanitize=vptr +// Make sure we only error/warn once, when trying to enable vptr and +// undefined and have -fno-rtti +// RUN: %clang -### -c -fsanitize=undefined -fsanitize=vptr -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-ERROR -check-prefix=CHECK-OK %s + // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=vptr %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-WARN %s // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=vptr -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=vptr -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-ERROR %s -// RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=undefined %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-WARN %s // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=undefined -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s -// RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=undefined -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-ERROR %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=vptr %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=vptr -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=vptr -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-ERROR %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=undefined %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=undefined -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s -// RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=undefined -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-ERROR %s // Exceptions + no/default rtti // RUN: %clang -### -c -target x86_64-scei-ps4 -fcxx-exceptions -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-EXC-ERROR-CXX %s