Index: include/clang/Driver/ToolChain.h =================================================================== --- include/clang/Driver/ToolChain.h +++ include/clang/Driver/ToolChain.h @@ -53,6 +53,13 @@ RLT_Libgcc }; + enum RTTIMode { + RM_EnabledExplicitly, + RM_EnabledImplicitly, + RM_DisabledExplicitly, + RM_DisabledImplicitly + }; + private: const Driver &D; const llvm::Triple Triple; @@ -74,6 +81,7 @@ Tool *getLink() const; Tool *getClangAs() const; + const RTTIMode CachedRTTIMode; mutable std::unique_ptr SanitizerArguments; protected: @@ -134,6 +142,9 @@ const SanitizerArgs& getSanitizerArgs() const; + // 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,30 @@ } 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 & static_cast(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 { + llvm::opt::Arg *NoRTTIArg = + Args.getLastArg(options::OPT_mkernel, options::OPT_fapple_kext, + options::OPT_fno_rtti); + 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 +235,14 @@ } addAllOf(Sanitizers, Kinds); + // Warn that we're disabling the vptr sanitizer if -fno-rtti and + // -fsanitize=undefined were passed. + if (Sanitizers.has(SanitizerKind::Vptr) && + (RTTIMode == ToolChain::RM_DisabledImplicitly || + RTTIMode == ToolChain::RM_DisabledExplicitly)) { + 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,44 @@ using namespace clang; using namespace llvm::opt; +static ToolChain::RTTIMode calculateRTTIMode(const ArgList &Args, + const llvm::Triple &Triple) { + // Explicit rtti/no-rtti args + Arg *RTTIArg = Args.getLastArg(options::OPT_mkernel, options::OPT_fapple_kext, + options::OPT_fno_rtti, options::OPT_frtti); + if (RTTIArg) { + if (RTTIArg->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; + } else { + 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), + CachedRTTIMode(calculateRTTIMode(Args, Triple)) { 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 @@ -1932,16 +1932,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. @@ -1971,15 +1972,26 @@ 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 *A = Args.getLastArg( + options::OPT_fcxx_exceptions, options::OPT_fno_cxx_exceptions, + options::OPT_fexceptions, options::OPT_fno_exceptions); + if (A) CXXExceptionsEnabled = A->getOption().matches(options::OPT_fcxx_exceptions) || A->getOption().matches(options::OPT_fexceptions); if (CXXExceptionsEnabled) { + ToolChain::RTTIMode RTTIMode = TC.getRTTIMode(); + if (Triple.isPS4CPU()) { + assert(A && "On the PS4 exceptions should only be enabled if passing " + "an argument"); + if (RTTIMode == ToolChain::RM_DisabledExplicitly) + D.Diag(diag::err_drv_argument_not_allowed_with) + << "-fno-rtti" << A->getAsString(Args); + else if (RTTIMode == ToolChain::RM_EnabledImplicitly) + D.Diag(diag::warn_drv_enabling_rtti_with_exceptions); + } + CmdArgs.push_back("-fcxx-exceptions"); EH = true; @@ -4006,56 +4018,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, @@ -4233,7 +4200,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,7 +31,6 @@ // 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=address,thread -fno-rtti %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANA-SANT 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