include(CheckCXXCompilerFlag) include(CheckLinkerFlag) message(STATUS "Checking available compiler and linker flags...") if(NOT MSVC) message(STATUS "-- Linker options:") # Linker flags. if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") check_linker_flag(CXX -Wl,-fatal_warnings LINKER_HAS_FATAL_WARN_APPLE) else() # for some reason, Clang 17 blocks --fatal-warnings if using mold linker, even if it supports the flag. check_linker_flag(CXX -Wl,--fatal-warnings LINKER_HAS_FATAL_WARN) endif() check_linker_flag(CXX -Wl,--as-needed LINKER_HAS_AS_NEEDED) message(STATUS "-- Compiler options:") # Compiler option flags. Common to both compilers, but older versions might not support the following. #check_cxx_compiler_flag("" COMP_HAS_) # Compiler option flags. Expected to work on GCC but not Clang, at the moment. check_cxx_compiler_flag("-fno-expensive-optimizations" COMP_HAS_FNO_EXPENSIVE_OPTIMIZATIONS) # Compiler option flags. Expected to work on Clang but not GCC, at the moment. # check_cxx_compiler_flag(CXX "-fvirtual-function-elimination" COMP_HAS_VFUNC_ELIMINATION) # only for release build? is it stable? # check_cxx_compiler_flag("-fforce-emit-vtables" COMP_HAS_F_FORCE_EMIT_VTABLES) # -fwhole-program-vtables # -fstrict-vtable-pointers if(${USE_ASAN}) # Compiler option flags. Sanitizers related (ASAN). message(STATUS "-- Compiler options (Address Sanitizer):") set(CMAKE_REQUIRED_LIBRARIES -fsanitize=address) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=address" COMP_HAS_ASAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_ASAN) message(FATAL_ERROR "This compiler does not support Address Sanitizer. Disable it.") endif() check_cxx_compiler_flag("-fsanitize-cfi" COMP_HAS_FSAN_CFI) # cfi: control flow integrity check_cxx_compiler_flag("-fsanitize-address-use-after-scope" COMP_HAS_FSAN_ADDRESS_USE_AFTER_SCOPE) check_cxx_compiler_flag("-fsanitize=pointer-compare" COMP_HAS_FSAN_PTR_COMP) check_cxx_compiler_flag("-fsanitize=pointer-subtract" COMP_HAS_FSAN_PTR_SUB) check_cxx_compiler_flag("-fsanitize-trap=all" COMP_HAS_FSAN_TRAP_ALL) endif() if(${USE_UBSAN}) # Compiler option flags. Sanitizers related (UBSAN). message(STATUS "-- Compiler options (Undefined Behavior Sanitizer):") set(CMAKE_REQUIRED_LIBRARIES -fsanitize=undefined) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=undefined" COMP_HAS_UBSAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_UBSAN) message(FATAL_ERROR "This compiler does not support Undefined Behavior Sanitizer. Disable it.") endif() check_cxx_compiler_flag("-fsanitize=float-divide-by-zero" COMP_HAS_FSAN_FLOAT_DIVIDE_BY_ZERO) #check_cxx_compiler_flag("-fsanitize=unsigned-integer-overflow" COMP_HAS_FSAN_UNSIGNED_INTEGER_OVERFLOW) #Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. #check_cxx_compiler_flag("-fsanitize=implicit-conversion" COMP_HAS_FSAN_IMPLICIT_CONVERSION) #check_cxx_compiler_flag("-fsanitize=local-bounds" COMP_HAS_FSAN_LOCAL_BOUNDS) check_cxx_compiler_flag("-fno-sanitize=enum" COMP_HAS_FSAN_NO_ENUM) endif() if(${USE_LSAN}) # Compiler option flags. Sanitizers related (LSAN). message(STATUS "-- Compiler options (Leak Sanitizer):") set(CMAKE_REQUIRED_LIBRARIES -fsanitize=leak) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=leak" COMP_HAS_LSAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_LSAN) message(FATAL_ERROR "This compiler does not support Leak Sanitizer. Disable it.") endif() endif() if(${USE_MSAN}) # Compiler option flags. Sanitizers related (UBSAN). message(STATUS "-- Compiler options (Memory Behavior Sanitizer):") set(CMAKE_REQUIRED_LIBRARIES -fsanitize=memory) # link against sanitizer lib, being required by the following compiler flag check_cxx_compiler_flag("-fsanitize=memory" COMP_HAS_MSAN) unset(CMAKE_REQUIRED_LIBRARIES) if(NOT COMP_HAS_MSAN) message(FATAL_ERROR "This compiler does not support Memory Sanitizer. Disable it.") endif() # MemorySanitizer: it doesn't work out of the box. It needs to be linked to an MSAN-instrumented build of libc++ and libc++abi. # This means: one should build them from LLVM source... # https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo #IF (${USE_MSAN}) # SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") #ENDIF() # Use "-stdlib=libstdc++" to link against GCC c/c++ libs (this is done by default) # To use LLVM libc++ use "-stdlib=libc++", but you need to install it separately message( WARNING "You have enabled MSAN. Make sure you do know what you are doing. It doesn't work out of the box. \ See comments in the toolchain and: https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo" ) check_cxx_compiler_flag("-fsanitize=memory-track-origins" COMP_HAS_F_SANITIZE_MEMORY_TRACK_ORIGINS) check_cxx_compiler_flag("-fPIE" COMP_HAS_F_PIE) endif() if(ENABLED_SANITIZER) if(RUNTIME_STATIC_LINK) check_cxx_compiler_flag("-fstatic-libasan" COMP_HAS_F_STATIC_LIBASAN) if(NOT COMP_HAS_F_STATIC_LIBASAN) message(FATAL_ERROR "This compiler doesn't support statically linking libasan. Turn off RUNTIME_STATIC_LINK?") endif() endif() else() # icf = identical code folding: # - not supported by mold # - error when linking with lld and asan or other sanitizers check_linker_flag(CXX -Wl,--icf=safe LINKER_HAS_ICF) endif() if(USE_COMPILER_HARDENING_OPTIONS) message(STATUS "-- Compiler options (code hardening):") # Compiler option flags. Other sanitizers or code hardening. if(NOT USE_ASAN) check_cxx_compiler_flag("-fsanitize=safe-stack" COMP_HAS_F_SANITIZE_SAFE_STACK) # Can't be used with asan! endif() check_cxx_compiler_flag("-fcf-protection=full" COMP_HAS_FCF_PROTECTION_FULL) check_cxx_compiler_flag("-fstack-check" COMP_HAS_F_STACK_CHECK) check_cxx_compiler_flag("-fstack-protector-all" COMP_HAS_F_STACK_PROTECTOR_ALL) check_cxx_compiler_flag("-fstack-protector-strong" COMP_HAS_F_STACK_PROTECTOR_STRONG) check_cxx_compiler_flag("-fstack-protector" COMP_HAS_F_STACK_PROTECTOR) check_cxx_compiler_flag("-fstack-clash-protection" COMP_HAS_F_STACK_CLASH_PROTECTION) #Flags GCC specific? check_cxx_compiler_flag("-fvtable-verify=preinit" COMP_HAS_F_VTABLE_VERIFY_PREINIT) check_cxx_compiler_flag("-fharden-control-flow-redundancy" COMP_HAS_F_HARDEN_CFR) check_cxx_compiler_flag("-fhardcfr-check-exceptions" COMP_HAS_F_HARDCFR_CHECK_EXCEPTIONS) #check_cxx_compiler_flag("" COMP_HAS_) endif() # Compiler warning flags. message(STATUS "-- Compiler options (warnings):") #check_cxx_compiler_flag("-Wuseless-cast" COMP_HAS_W_USELESS_CAST) check_cxx_compiler_flag("-Wnull-dereference" COMP_HAS_W_NULL_DEREFERENCE) #check_cxx_compiler_flag("-Wconversion" COMP_HAS_W_CONVERSION) # Temporarily disabled. Implicit type conversions that might change a value, such as narrowing conversions. #check_cxx_compiler_flag("-Wcast-qual" COMP_HAS_W_CAST_QUAL) # casts that remove a type's const or volatile qualifier. check_cxx_compiler_flag("-Wzero-as-null-pointer-constant" COMP_HAS_W_ZERO_NULLPTR) #check_cxx_compiler_flag("" COMP_HAS_) # Compiler warning flags. Expected to work con GCC but not Clang, at the moment. check_cxx_compiler_flag("-Wlto-type-mismatch" COMP_HAS_W_LTO_TYPE_MISMATCH) check_cxx_compiler_flag("-Wshift-overflow=2" COMP_HAS_W_SHIFT_OVERFLOW) check_cxx_compiler_flag("-Wduplicated-cond" COMP_HAS_W_DUPLICATED_COND) check_cxx_compiler_flag("-Wsized-deallocation" COMP_HAS_W_SIZED_DEALLOC) check_cxx_compiler_flag("-Wvector-operation-performance" COMP_HAS_W_VECTOR_OP_PERF) check_cxx_compiler_flag("-Wtrampolines" COMP_HAS_W_TRAMPOLINES) # we don't want trampolines for security concerns (it's a technique to implement nested functions). check_cxx_compiler_flag("-Wformat-signedness" COMP_HAS_FORMAT_SIGNEDNESS) check_cxx_compiler_flag("-Wduplicated-branches" COMP_HAS_DUPLICATED_BRANCHES) #check_cxx_compiler_flag("" COMP_HAS_) # Compiler warning flags. Expected to work con Clang but not GCC, at the moment. check_cxx_compiler_flag("-Wweak-vtables" COMP_HAS_W_WEAK_VTABLES) check_cxx_compiler_flag("-Wmissing-prototypes" COMP_HAS_W_MISSING_PROTOTYPES) check_cxx_compiler_flag("-Wmissing-variable-declarations" COMP_HAS_W_MISSING_VARIABLE_DECL) #check_cxx_compiler_flag("" COMP_HAS_) # Compiler warning flags. TODO: To be evaluated... # -Wfree-nonheap-object (good but false positive warnings on GCC?) # -Wcast-align=strict # -Wcast-user-defined # -Wdate-time # Compiler warning flags. To be disabled. message(STATUS "-- Compiler options (disable specific warnings):") check_cxx_compiler_flag("-Wnonnull-compare" COMP_HAS_WNO_NONNULL_COMPARE) check_cxx_compiler_flag("-Wmaybe-uninitialized" COMP_HAS_WNO_MAYBE_UNINIT) check_cxx_compiler_flag("-Wlanguage-extension-token" COMP_HAS_WNO_LANGUAGE_EXTENSION_TOKEN) check_cxx_compiler_flag("-Wnested-anon-types" COMP_HAS_WNO_NESTED_ANON_TYPES) #check_cxx_compiler_flag("-Wunknown-pragmas" COMP_HAS_) #check_cxx_compiler_flag("" COMP_HAS_) elseif(MSVC) # Compiler warning flags (MSVC). message(STATUS "-- Compiler options (MSVC):") check_cxx_compiler_flag("/W5105" COMP_HAS_W_DEFINE_MACRO_EXPANSION) # False positive warning, maybe even a MSVC bug. #check_cxx_compiler_flag("" COMP_HAS_) endif() # ---- Append options into some lists. # ---- GCC/Clang if(NOT MSVC) if(COMP_HAS_FNO_EXPENSIVE_OPTIMIZATIONS) list(APPEND checked_compiler_options "-fno-expensive-optimizations") endif() if(COMP_HAS_ASAN) list(APPEND checked_compiler_options_asan "-fsanitize=address") list(APPEND checked_linker_options_all "-fsanitize=address") if(COMP_HAS_F_STATIC_LIBASAN) list(APPEND checked_linker_options_all "-static-libasan") endif() if(COMP_HAS_FSAN_CFI) list(APPEND checked_compiler_options_asan "-fsanitize-cfi") endif() if(COMP_HAS_FSAN_ADDRESS_USE_AFTER_SCOPE) list(APPEND checked_compiler_options_asan "-fsanitize-address-use-after-scope") endif() if(COMP_HAS_FSAN_PTR_COMP) list(APPEND checked_compiler_options_asan "-fsanitize=pointer-compare") endif() if(COMP_HAS_FSAN_PTR_SUB) list(APPEND checked_compiler_options_asan "-fsanitize=pointer-subtract") endif() if(COMP_HAS_FSAN_TRAP_ALL) list(APPEND checked_compiler_options_asan "-fsanitize-trap=all") endif() endif() if(COMP_HAS_UBSAN) list(APPEND checked_compiler_options_ubsan "-fsanitize=undefined") list(APPEND checked_linker_options_all "-fsanitize=undefined") if(RUNTIME_STATIC_LINK) list(APPEND checked_linker_options_all "-static-libubsan") endif() if(COMP_HAS_FSAN_FLOAT_DIVIDE_BY_ZERO) list(APPEND checked_compiler_options_ubsan "-fsanitize=float-divide-by-zero") endif() if(COMP_HAS_FSAN_UNSIGNED_INTEGER_OVERFLOW) list(APPEND checked_compiler_options_ubsan "-fsanitize=unsigned-integer-overflow") endif() if(COMP_HAS_FSAN_IMPLICIT_CONVERSION) list(APPEND checked_compiler_options_ubsan "-fsanitize=implicit-conversion") endif() if(COMP_HAS_FSAN_NO_ENUM) list(APPEND checked_compiler_options_ubsan "-fno-sanitize=enum") endif() if(COMP_HAS_FSAN_LOCAL_BOUNDS) list(APPEND checked_compiler_options_ubsan "-fsanitize=local-bounds") endif() endif() if(COMP_HAS_LSAN) list(APPEND checked_compiler_options_lsan "-fsanitize=leak") list(APPEND checked_linker_options_all "-fsanitize=leak") if(RUNTIME_STATIC_LINK) list(APPEND checked_linker_options_all "-static-liblsan") endif() endif() if(COMP_HAS_MSAN) list(APPEND checked_compiler_options_msan "-fsanitize=memory") list(APPEND checked_linker_options_all "-fsanitize=memory") if(RUNTIME_STATIC_LINK) list(APPEND checked_linker_options_all "-static-libmsan") endif() if(COMP_HAS_F_SANITIZE_MEMORY_TRACK_ORIGINS) list(APPEND checked_compiler_options_msan "-fsanitize=memory-track-origins") endif() if(COMP_HAS_F_PIE) list(APPEND checked_compiler_options_msan "-fPIE") list(APPEND checked_linker_options_all "-pie") endif() endif() if(COMP_HAS_F_SANITIZE_SAFE_STACK) list(APPEND checked_compiler_options_sanitize_harden "-fsanitize=safe-stack") endif() if(COMP_HAS_FCF_PROTECTION_FULL) list(APPEND checked_compiler_options_sanitize_harden "-fcf-protection=full") endif() if(COMP_HAS_F_STACK_CHECK) list(APPEND checked_compiler_options_sanitize_harden "-fstack-check") endif() if(COMP_HAS_F_STACK_PROTECTOR_ALL) list(APPEND checked_compiler_options_sanitize_harden "-fstack-protector-all") elseif(COMP_HAS_F_STACK_PROTECTOR_STRONG) list(APPEND checked_compiler_options_sanitize_harden "-fstack-protector-strong") elseif(COMP_HAS_F_STACK_PROTECTOR) list(APPEND checked_compiler_options_sanitize_harden "-fstack-protector") endif() if(COMP_HAS_F_STACK_CLASH_PROTECTION) list(APPEND checked_compiler_options_sanitize_harden "-fstack-clash-protection") endif() if(COMP_HAS_F_VTABLE_VERIFY_PREINIT) list(APPEND checked_compiler_options_sanitize_harden "-fvtable-verify=preinit") endif() if(COMP_HAS_F_HARDEN_CFR) list(APPEND checked_compiler_options_sanitize_harden "-fharden-control-flow-redundancy") endif() if(COMP_HAS_F_HARDCFR_CHECK_EXCEPTIONS) list(APPEND checked_compiler_options_sanitize_harden "-fhardcfr-check-exceptions") endif() if(COMP_HAS_W_USELESS_CAST) list(APPEND checked_compiler_warnings "-Wuseless-cast") endif() if(COMP_HAS_NULL_DEREFERENCE) list(APPEND checked_compiler_warnings "-Wnull-dereference") endif() if(COMP_HAS_W_CONVERSION) list(APPEND checked_compiler_warnings "-Wconversion") endif() if(COMP_HAS_W_CAST_QUAL) list(APPEND checked_compiler_warnings "-Wcast-qual") endif() if(COMP_HAS_W_ZERO_NULLPTR) list(APPEND checked_compiler_warnings "-Wzero-as-null-pointer-constant") endif() if(COMP_HAS_W_LTO_TYPE_MISMATCH) list(APPEND checked_compiler_warnings "-Wlto-type-mismatch") endif() if(COMP_HAS_W_SHIFT_OVERFLOW) list(APPEND checked_compiler_warnings "-Wshift-overflow=2") endif() if(COMP_HAS_W_DUPLICATED_COND) list(APPEND checked_compiler_warnings "-Wduplicated-cond") endif() if(COMP_HAS_W_SIZED_DEALLOC) list(APPEND checked_compiler_warnings "-Wsized-deallocation") endif() if(COMP_HAS_W_VECTOR_OP_PERF) list(APPEND checked_compiler_warnings "-Wvector-operation-performance") endif() if(COMP_HAS_W_TRAMPOLINES) list(APPEND checked_compiler_warnings "-Wtrampolines") endif() if(COMP_HAS_FORMAT_SIGNEDNESS) list(APPEND checked_compiler_warnings "-Wformat-signedness") endif() if(COMP_HAS_DUPLICATED_BRANCHES) list(APPEND checked_compiler_warnings "-Wduplicated-branches") endif() if(COMP_HAS_W_WEAK_VTABLES) list(APPEND checked_compiler_warnings "-Wweak-vtables") endif() if(COMP_HAS_W_MISSING_PROTOTYPES) list(APPEND checked_compiler_warnings "-Wmissing-prototypes") endif() if(COMP_HAS_W_MISSING_VARIABLE_DECL) list(APPEND checked_compiler_warnings "-Wmissing-variable-declarations") endif() if(COMP_HAS_WNO_NONNULL_COMPARE) list(APPEND checked_compiler_warnings_disabled "-Wno-nonnull-compare") endif() if(COMP_HAS_WNO_MAYBE_UNINIT) list(APPEND checked_compiler_warnings_disabled "-Wno-maybe-uninitialized") endif() if(COMP_HAS_WNO_LANGUAGE_EXTENSION_TOKEN) list(APPEND checked_compiler_warnings_disabled "-Wno-language-extension-token") endif() if(COMP_HAS_WNO_NESTED_ANON_TYPES) list(APPEND checked_compiler_warnings_disabled "-Wno-nested-anon-types") endif() if(LINKER_HAS_FATAL_WARN_APPLE) list(APPEND checked_linker_options_all "-Wl,-fatal_warnings") elseif(LINKER_HAS_FATAL_WARN) list(APPEND checked_linker_options_all "-Wl,--fatal-warnings") endif() if(LINKER_HAS_AS_NEEDED) list(APPEND checked_linker_options_all "-Wl,--as-needed") endif() if(LINKER_HAS_ICF) list(APPEND checked_linker_options_all "-Wl,--icf=safe") endif() endif() # ---- MSVC if(MSVC) #list( # APPEND # base_compiler_options_msvc # "/wd5105" #) if(COMP_HAS_W_DEFINE_MACRO_EXPANSION) list(APPEND checked_compiler_options_msvc "/wd5105") endif() endif() # ---- Double checks if( CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 12.1 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0 ) # False positive warning, disable this flag. unset(COMPILER_SUPPORTS_NULL_DEREFERENCE CACHE) endif() # ---- #[[ set(checked_compiler_options) set(checked_compiler_options_asan) #set(checked_compiler_options_ubsan) #set(checked_compiler_options_lsan) set(checked_compiler_options_sanitize_harden) set(checked_compiler_warnings) set(checked_compiler_warnings_disabled) set(checked_compiler_options_msvc) ]] ## ---- # Now sum up, report and encapsulate all the options. if(NOT MSVC) list( APPEND list_explicit_compiler_options_all ${checked_compiler_options} ${checked_compiler_options_asan} ${checked_compiler_options_ubsan} ${checked_compiler_options_lsan} ${checked_compiler_options_msan} ${checked_compiler_options_sanitize_harden} ${checked_compiler_warnings} ${checked_compiler_warnings_disabled} ) list( APPEND list_explicit_linker_options_all ${checked_linker_options_all} ) # -- message(STATUS "Adding the following conditional compiler options: ${checked_compiler_options}.") if(COMP_HAS_ASAN) message(STATUS "Adding the following conditional compiler options for ASan: ${checked_compiler_options_asan}.") endif() if(COMP_HAS_UBSAN) message( STATUS "Adding the following conditional compiler options for UBSan: ${checked_compiler_options_ubsan}." ) endif() if(COMP_HAS_LSAN) message(STATUS "Adding the following conditional compiler options for LSan: ${checked_compiler_options_lsan}.") endif() if(COMP_HAS_MSAN) message(STATUS "Adding the following conditional compiler options for MSan: ${checked_compiler_options_msan}.") endif() message( STATUS "Adding the following conditional compiler options for hardening: ${checked_compiler_options_sanitize_harden}." ) message(STATUS "Adding the following conditional compiler warnings: ${checked_compiler_warnings}.") message( STATUS "Adding the following conditional compiler warning ignore options: ${checked_compiler_warnings_disabled}." ) message(STATUS "Adding the following conditional linker options: ${checked_linker_options_all}.") elseif(MSVC) list( APPEND list_explicit_compiler_options_all ${checked_compiler_options_msvc} ) message(STATUS "Adding the following conditional compiler options: ${list_explicit_compiler_options_all}.") endif()