Compare commits

...

2 Commits

10 changed files with 318 additions and 251 deletions

View File

@ -28,7 +28,7 @@ find_package(Sanitizers)
## Usage ## Usage
You can enable the sanitizers with ``SANITIZE_ADDRESS``, ``SANITIZE_MEMORY``, ``SANITIZE_THREAD`` or ``SANITIZE_UNDEFINED`` options in your CMake configuration. You can do this by passing e.g. ``-DSANITIZE_ADDRESS=On`` on your command line or with your graphical interface. You can enable the sanitizers with ``SANITIZE_ADDRESS``, ``SANITIZE_MEMORY``, ``SANITIZE_THREAD``, ``SANITIZE_UNDEFINED``, ``SANITIZE_CFI`` or ``SANITIZE_SS`` options in your CMake configuration. You can do this by passing e.g. ``-DSANITIZE_ADDRESS=On`` on your command line or with your graphical interface.
If sanitizers are supported by your compiler, the specified targets will be build with sanitizer support. If your compiler has no sanitizing capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and sanitizing will simply just be ignored. If sanitizers are supported by your compiler, the specified targets will be build with sanitizer support. If your compiler has no sanitizing capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and sanitizing will simply just be ignored.

View File

@ -25,18 +25,18 @@
option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES set(FLAG_CANDIDATES
# Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional.
"-g -fsanitize=address -fno-omit-frame-pointer" "-g -fsanitize=address -fno-omit-frame-pointer"
"-g -fsanitize=address" "-g -fsanitize=address"
# Older deprecated flag for ASan # Older deprecated flag for ASan
"-g -faddress-sanitizer" "-g -faddress-sanitizer"
) )
if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY))
message(FATAL_ERROR "AddressSanitizer is not compatible with " message(FATAL_ERROR "AddressSanitizer is not compatible with "
"ThreadSanitizer or MemorySanitizer.") "ThreadSanitizer or MemorySanitizer.")
endif () endif ()
include(sanitize-helpers) include(sanitize-helpers)
@ -44,22 +44,22 @@ include(sanitize-helpers)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan") sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan")
if(ASan_FLAG_DETECTED) if(ASan_FLAG_DETECTED)
set(ASan_FOUND TRUE) set(ASan_FOUND TRUE)
endif() endif()
if (SANITIZE_ADDRESS) if (SANITIZE_ADDRESS)
find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH})
mark_as_advanced(ASan_WRAPPER) mark_as_advanced(ASan_WRAPPER)
if (CMAKE_SYSTEM_NAME EQUAL "Linux") if (CMAKE_SYSTEM_NAME EQUAL "Linux")
find_library(ASan_SHARED_LIB "clang_rt.asan-x86_64" PATH_SUFFIXES "linux") find_library(ASan_SHARED_LIB "clang_rt.asan-x86_64" PATH_SUFFIXES "linux")
mark_as_advanced(ASan_SHARED_LIB) mark_as_advanced(ASan_SHARED_LIB)
endif() endif()
endif () endif ()
function (add_sanitize_address TARGET) function (add_sanitize_address TARGET)
if (NOT SANITIZE_ADDRESS) if (NOT SANITIZE_ADDRESS)
return() return()
endif () endif ()
sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan")
endfunction () endfunction ()

31
cmake/FindCFI.cmake Normal file
View File

@ -0,0 +1,31 @@
option(SANITIZE_CFI "Enable Control Flow Integrity (CFI) for sanitized targets." OFF)
# FIXME: Might also want to add the variants of the CFI options
set(FLAG_CANDIDATES
# FIXME: Brief comment on why the additional flags
# In this case you need a
# linker that does optimization at
# linking time such as LLVM lld or GNU gold.
"-g -fsanitize=cfi -fvisibility=hidden -flto -fuse-ld=lld"
)
# There might be some conflict with the other sanitizer
# hence it might need an if statement here.
# add some handy functions
include(sanitize-helpers)
if(SANITIZE_CFI)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ControlFlowIntegrity"
"CFI")
endif()
function (add_sanitize_cfi TARGET)
if (NOT SANITIZE_CFI)
return()
endif()
sanitizer_add_flags(${TARGET} "ControlFlowIntegrity" "CFI")
endfunction()

View File

@ -24,24 +24,24 @@
# SOFTWARE. # SOFTWARE.
option(SANITIZE_LEAK option(SANITIZE_LEAK
"Enable LeakSanitizer for sanitized targets." Off) "Enable LeakSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES set(FLAG_CANDIDATES
"-g -fsanitize=leak" "-g -fsanitize=leak"
) )
include(sanitize-helpers) include(sanitize-helpers)
if (SANITIZE_LEAK) if (SANITIZE_LEAK)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" sanitizer_check_compiler_flags("${FLAG_CANDIDATES}"
"LeakSanitizer" "LeakSan") "LeakSanitizer" "LeakSan")
endif () endif ()
function (add_sanitize_leak TARGET) function (add_sanitize_leak TARGET)
if (NOT SANITIZE_LEAK) if (NOT SANITIZE_LEAK)
return() return()
endif () endif ()
sanitizer_add_flags(${TARGET} "LeakSanitizer" "LeakSan") sanitizer_add_flags(${TARGET} "LeakSanitizer" "LeakSan")
endfunction () endfunction ()

View File

@ -25,8 +25,8 @@
option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES set(FLAG_CANDIDATES
"-g -fsanitize=memory" "-g -fsanitize=memory"
) )
include(sanitize-helpers) include(sanitize-helpers)
@ -34,28 +34,28 @@ include(sanitize-helpers)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" "MSan") sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" "MSan")
if(MSan_FLAG_DETECTED) if(MSan_FLAG_DETECTED)
set(MSan_FOUND TRUE) set(MSan_FOUND TRUE)
endif() endif()
if (SANITIZE_MEMORY) if (SANITIZE_MEMORY)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
message(WARNING "MemorySanitizer disabled for target ${TARGET} because " message(STATUS "MemorySanitizer disabled for target ${TARGET} because "
"MemorySanitizer is supported for Linux systems only.") "MemorySanitizer is supported for Linux systems only.")
set(SANITIZE_MEMORY Off CACHE BOOL set(SANITIZE_MEMORY Off CACHE BOOL
"Enable MemorySanitizer for sanitized targets." FORCE) "Enable MemorySanitizer for sanitized targets." FORCE)
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
message(WARNING "MemorySanitizer disabled for target ${TARGET} because " message(STATUS "MemorySanitizer disabled for target ${TARGET} because "
"MemorySanitizer is supported for 64bit systems only.") "MemorySanitizer is supported for 64bit systems only.")
set(SANITIZE_MEMORY Off CACHE BOOL set(SANITIZE_MEMORY Off CACHE BOOL
"Enable MemorySanitizer for sanitized targets." FORCE) "Enable MemorySanitizer for sanitized targets." FORCE)
else () else ()
endif () endif ()
endif () endif ()
function (add_sanitize_memory TARGET) function (add_sanitize_memory TARGET)
if (NOT SANITIZE_MEMORY) if (NOT SANITIZE_MEMORY)
return() return()
endif () endif ()
sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan")
endfunction () endfunction ()

24
cmake/FindSS.cmake Normal file
View File

@ -0,0 +1,24 @@
option(SANITIZE_SS "Enable SafeStack for sanitized targets." OFF)
set(FLAG_CANDIDATES
"-g -fsanitize=safe-stack"
)
# There might be some conflict with the other sanitizer
# hence it might need an if statement here.
# add some handy functions
include(sanitize-helpers)
if(SANITIZE_SS)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "SafeStack"
"SS")
endif()
function (add_sanitize_ss TARGET)
if (NOT SANITIZE_SS)
return()
endif()
sanitizer_add_flags(${TARGET} "SafeStack" "SS")
endfunction()

View File

@ -31,7 +31,7 @@ option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off)
set(FIND_QUIETLY_FLAG "") set(FIND_QUIETLY_FLAG "")
if (DEFINED Sanitizers_FIND_QUIETLY) if (DEFINED Sanitizers_FIND_QUIETLY)
set(FIND_QUIETLY_FLAG "QUIET") set(FIND_QUIETLY_FLAG "QUIET")
endif () endif ()
find_package(ASan ${FIND_QUIETLY_FLAG}) find_package(ASan ${FIND_QUIETLY_FLAG})
@ -39,89 +39,93 @@ find_package(TSan ${FIND_QUIETLY_FLAG})
find_package(MSan ${FIND_QUIETLY_FLAG}) find_package(MSan ${FIND_QUIETLY_FLAG})
find_package(UBSan ${FIND_QUIETLY_FLAG}) find_package(UBSan ${FIND_QUIETLY_FLAG})
find_package(LeakSan ${FIND_QUIETLY_FLAG}) find_package(LeakSan ${FIND_QUIETLY_FLAG})
find_package(CFI ${FIND_QUIETLY_FLAG})
find_package(SS ${FIND_QUIETLY_FLAG})
set(Sanitizers_COMPONENTS "") set(Sanitizers_COMPONENTS "")
if(ASan_FOUND) if(ASan_FOUND)
set(Sanitizers_ASan_FOUND TRUE) set(Sanitizers_ASan_FOUND TRUE)
list(APPEND Sanitizers_COMPONENTS ASan) list(APPEND Sanitizers_COMPONENTS ASan)
endif() endif()
if(TSan_FOUND) if(TSan_FOUND)
set(Sanitizers_TSan_FOUND TRUE) set(Sanitizers_TSan_FOUND TRUE)
list(APPEND Sanitizers_COMPONENTS TSan) list(APPEND Sanitizers_COMPONENTS TSan)
endif() endif()
if(MSan_FOUND) if(MSan_FOUND)
set(Sanitizers_MSan_FOUND TRUE) set(Sanitizers_MSan_FOUND TRUE)
list(APPEND Sanitizers_COMPONENTS MSan) list(APPEND Sanitizers_COMPONENTS MSan)
endif() endif()
if(UBSan_FOUND) if(UBSan_FOUND)
set(Sanitizers_UBSan_FOUND TRUE) set(Sanitizers_UBSan_FOUND TRUE)
list(APPEND Sanitizers_COMPONENTS UBSan) list(APPEND Sanitizers_COMPONENTS UBSan)
endif() endif()
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
find_package_handle_standard_args( find_package_handle_standard_args(
Sanitizers Sanitizers
FOUND_VAR Sanitizers_FOUND FOUND_VAR Sanitizers_FOUND
REQUIRED_VARS Sanitizers_COMPONENTS REQUIRED_VARS Sanitizers_COMPONENTS
HANDLE_COMPONENTS HANDLE_COMPONENTS
) )
function(sanitizer_add_blacklist_file FILE) function(sanitizer_add_blacklist_file FILE)
if(NOT IS_ABSOLUTE ${FILE}) if(NOT IS_ABSOLUTE ${FILE})
set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}")
endif() endif()
get_filename_component(FILE "${FILE}" REALPATH) get_filename_component(FILE "${FILE}" REALPATH)
sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}"
"SanitizerBlacklist" "SanBlist") "SanitizerBlacklist" "SanBlist")
endfunction() endfunction()
function(add_sanitizers ...) function(add_sanitizers ...)
# If no sanitizer is enabled, return immediately. # If no sanitizer is enabled, return immediately.
if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR
SANITIZE_UNDEFINED)) SANITIZE_UNDEFINED OR SANITIZE_CFI OR SANITIZE_SS))
return() message(STATUS "No sanitizer selected.")
return()
endif ()
foreach (TARGET ${ARGV})
# Check if this target will be compiled by exactly one compiler. Other-
# wise sanitizers can't be used and a warning should be printed once.
get_target_property(TARGET_TYPE ${TARGET} TYPE)
if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY")
message(STATUS "Can't use any sanitizers for target ${TARGET}, "
"because it is an interface library and cannot be "
"compiled directly.")
return()
endif ()
sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if (NUM_COMPILERS GREATER 1)
message(STATUS "Can't use any sanitizers for target ${TARGET}, "
"because it will be compiled by incompatible compilers. "
"Target will be compiled without sanitizers.")
return()
# If the target is compiled by no or no known compiler, give a warning.
elseif (NUM_COMPILERS EQUAL 0)
message(STATUS "Sanitizers for target ${TARGET} may not be"
" usable, because it uses no or an unknown compiler. "
"This is a false warning for targets using only "
"object lib(s) as input.")
endif () endif ()
foreach (TARGET ${ARGV}) # Add sanitizers for target.
# Check if this target will be compiled by exactly one compiler. Other- add_sanitize_address(${TARGET})
# wise sanitizers can't be used and a warning should be printed once. add_sanitize_thread(${TARGET})
get_target_property(TARGET_TYPE ${TARGET} TYPE) add_sanitize_memory(${TARGET})
if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") add_sanitize_undefined(${TARGET})
message(WARNING "Can't use any sanitizers for target ${TARGET}, " add_sanitize_leak(${TARGET})
"because it is an interface library and cannot be " add_sanitize_cfi(${TARGET})
"compiled directly.") add_sanitize_ss(${TARGET})
return() endforeach ()
endif ()
sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if (NUM_COMPILERS GREATER 1)
message(WARNING "Can't use any sanitizers for target ${TARGET}, "
"because it will be compiled by incompatible compilers. "
"Target will be compiled without sanitizers.")
return()
# If the target is compiled by no or no known compiler, give a warning.
elseif (NUM_COMPILERS EQUAL 0)
message(WARNING "Sanitizers for target ${TARGET} may not be"
" usable, because it uses no or an unknown compiler. "
"This is a false warning for targets using only "
"object lib(s) as input.")
endif ()
# Add sanitizers for target.
add_sanitize_address(${TARGET})
add_sanitize_thread(${TARGET})
add_sanitize_memory(${TARGET})
add_sanitize_undefined(${TARGET})
add_sanitize_leak(${TARGET})
endforeach ()
endfunction(add_sanitizers) endfunction(add_sanitizers)

View File

@ -25,14 +25,14 @@
option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES set(FLAG_CANDIDATES
"-g -fsanitize=thread" "-g -fsanitize=thread"
) )
# ThreadSanitizer is not compatible with MemorySanitizer. # ThreadSanitizer is not compatible with MemorySanitizer.
if (SANITIZE_THREAD AND SANITIZE_MEMORY) if (SANITIZE_THREAD AND SANITIZE_MEMORY)
message(FATAL_ERROR "ThreadSanitizer is not compatible with " message(FATAL_ERROR "ThreadSanitizer is not compatible with "
"MemorySanitizer.") "MemorySanitizer.")
endif () endif ()
@ -41,28 +41,28 @@ include(sanitize-helpers)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" "TSan") sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" "TSan")
if(TSan_FLAG_DETECTED) if(TSan_FLAG_DETECTED)
set(TSan_FOUND TRUE) set(TSan_FOUND TRUE)
endif() endif()
if (SANITIZE_THREAD) if (SANITIZE_THREAD)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND
NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " message(STATUS "ThreadSanitizer disabled for target ${TARGET} because "
"ThreadSanitizer is supported for Linux systems and macOS only.") "ThreadSanitizer is supported for Linux systems and macOS only.")
set(SANITIZE_THREAD Off CACHE BOOL set(SANITIZE_THREAD Off CACHE BOOL
"Enable ThreadSanitizer for sanitized targets." FORCE) "Enable ThreadSanitizer for sanitized targets." FORCE)
elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " message(STATUS "ThreadSanitizer disabled for target ${TARGET} because "
"ThreadSanitizer is supported for 64bit systems only.") "ThreadSanitizer is supported for 64bit systems only.")
set(SANITIZE_THREAD Off CACHE BOOL set(SANITIZE_THREAD Off CACHE BOOL
"Enable ThreadSanitizer for sanitized targets." FORCE) "Enable ThreadSanitizer for sanitized targets." FORCE)
endif () endif ()
endif () endif ()
function (add_sanitize_thread TARGET) function (add_sanitize_thread TARGET)
if (NOT SANITIZE_THREAD) if (NOT SANITIZE_THREAD)
return() return()
endif () endif ()
sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan")
endfunction () endfunction ()

View File

@ -23,11 +23,11 @@
# SOFTWARE. # SOFTWARE.
option(SANITIZE_UNDEFINED option(SANITIZE_UNDEFINED
"Enable UndefinedBehaviorSanitizer for sanitized targets." Off) "Enable UndefinedBehaviorSanitizer for sanitized targets." Off)
set(FLAG_CANDIDATES set(FLAG_CANDIDATES
"-g -fsanitize=undefined" "-g -fsanitize=undefined"
) )
include(sanitize-helpers) include(sanitize-helpers)
@ -35,13 +35,13 @@ include(sanitize-helpers)
sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "UndefinedBehaviorSanitizer" "UBSan") sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "UndefinedBehaviorSanitizer" "UBSan")
if(UBSan_FLAG_DETECTED) if(UBSan_FLAG_DETECTED)
set(UBSan_FOUND TRUE) set(UBSan_FOUND TRUE)
endif() endif()
function (add_sanitize_undefined TARGET) function (add_sanitize_undefined TARGET)
if (NOT SANITIZE_UNDEFINED) if (NOT SANITIZE_UNDEFINED)
return() return()
endif () endif ()
sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan")
endfunction () endfunction ()

View File

@ -24,154 +24,162 @@
# Helper function to get the language of a source file. # Helper function to get the language of a source file.
function (sanitizer_lang_of_source FILE RETURN_VAR) function (sanitizer_lang_of_source FILE RETURN_VAR)
get_filename_component(LONGEST_EXT "${FILE}" EXT) get_filename_component(LONGEST_EXT "${FILE}" EXT)
# If extension is empty return. This can happen for extensionless headers # If extension is empty return. This can happen for extensionless headers
if("${LONGEST_EXT}" STREQUAL "") if("${LONGEST_EXT}" STREQUAL "")
set(${RETURN_VAR} "" PARENT_SCOPE)
return()
endif()
# Get shortest extension as some files can have dot in their names
string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT})
string(TOLOWER "${FILE_EXT}" FILE_EXT)
string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
if (NOT ${TEMP} EQUAL -1)
set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
return()
endif ()
endforeach()
set(${RETURN_VAR} "" PARENT_SCOPE) set(${RETURN_VAR} "" PARENT_SCOPE)
return()
endif()
# Get shortest extension as some files can have dot in their names
string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT})
string(TOLOWER "${FILE_EXT}" FILE_EXT)
string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
if (NOT ${TEMP} EQUAL -1)
set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
return()
endif ()
endforeach()
set(${RETURN_VAR} "" PARENT_SCOPE)
endfunction () endfunction ()
# Helper function to get compilers used by a target. # Helper function to get compilers used by a target.
function (sanitizer_target_compilers TARGET RETURN_VAR) function (sanitizer_target_compilers TARGET RETURN_VAR)
# Check if all sources for target use the same compiler. If a target uses # Check if all sources for target use the same compiler. If a target uses
# e.g. C and Fortran mixed and uses different compilers (e.g. clang and # e.g. C and Fortran mixed and uses different compilers (e.g. clang and
# gfortran) this can trigger huge problems, because different compilers may # gfortran) this can trigger huge problems, because different compilers may
# use different implementations for sanitizers. # use different implementations for sanitizers.
set(BUFFER "") set(BUFFER "")
get_target_property(TSOURCES ${TARGET} SOURCES) get_target_property(TSOURCES ${TARGET} SOURCES)
foreach (FILE ${TSOURCES}) foreach (FILE ${TSOURCES})
# If expression was found, FILE is a generator-expression for an object # If expression was found, FILE is a generator-expression for an object
# library. Object libraries will be ignored. # library. Object libraries will be ignored.
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
if ("${_file}" STREQUAL "") if ("${_file}" STREQUAL "")
sanitizer_lang_of_source(${FILE} LANG) sanitizer_lang_of_source(${FILE} LANG)
if (LANG) if (LANG)
list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID})
endif () endif ()
endif () endif ()
endforeach () endforeach ()
list(REMOVE_DUPLICATES BUFFER) list(REMOVE_DUPLICATES BUFFER)
set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE)
endfunction () endfunction ()
# Helper function to check compiler flags for language compiler. # Helper function to check compiler flags for language compiler.
function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) function (sanitizer_check_compiler_flag FLAG LANG VARIABLE)
if (${LANG} STREQUAL "C") if (${LANG} STREQUAL "C")
include(CheckCCompilerFlag) include(CheckCCompilerFlag)
check_c_compiler_flag("${FLAG}" ${VARIABLE}) check_c_compiler_flag("${FLAG}" ${VARIABLE})
elseif (${LANG} STREQUAL "CXX") elseif (${LANG} STREQUAL "CXX")
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) check_cxx_compiler_flag("${FLAG}" ${VARIABLE})
elseif (${LANG} STREQUAL "Fortran") elseif (${LANG} STREQUAL "Fortran")
# CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible
# with older Cmake versions, we will check if this module is present # with older Cmake versions, we will check if this module is present
# before we use it. Otherwise we will define Fortran coverage support as # before we use it. Otherwise we will define Fortran coverage support as
# not available. # not available.
include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED)
if (INCLUDED) if (INCLUDED)
check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) check_fortran_compiler_flag("${FLAG}" ${VARIABLE})
elseif (NOT CMAKE_REQUIRED_QUIET) elseif (NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Performing Test ${VARIABLE}") message(STATUS "Performing Test ${VARIABLE}")
message(STATUS "Performing Test ${VARIABLE}" message(STATUS "Performing Test ${VARIABLE}"
" - Failed (Check not supported)") " - Failed (Check not supported)")
endif () endif ()
endif() endif()
endfunction () endfunction ()
# Helper function to test compiler flags. # Helper function to test compiler flags.
function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX)
set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY})
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES}) foreach (LANG ${ENABLED_LANGUAGES})
# Sanitizer flags are not dependend on language, but the used compiler. # Sanitizer flags are not dependend on language, but the used compiler.
# So instead of searching flags foreach language, search flags foreach # So instead of searching flags foreach language, search flags foreach
# compiler used. # compiler used.
set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS)
foreach (FLAG ${FLAG_CANDIDATES}) foreach (FLAG ${FLAG_CANDIDATES})
if(NOT CMAKE_REQUIRED_QUIET) if(NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]")
endif() endif()
set(CMAKE_REQUIRED_FLAGS "${FLAG}") set(CMAKE_REQUIRED_FLAGS "${FLAG}")
unset(${PREFIX}_FLAG_DETECTED CACHE) unset(${PREFIX}_FLAG_DETECTED CACHE)
sanitizer_check_compiler_flag("${FLAG}" ${LANG} sanitizer_check_compiler_flag("${FLAG}" ${LANG}
${PREFIX}_FLAG_DETECTED) ${PREFIX}_FLAG_DETECTED)
if (${PREFIX}_FLAG_DETECTED) # Unfortunately cmake using clang is not picking up
# If compiler is a GNU compiler, search for static flag, if # the CFI sanitizer flags
# SANITIZE_LINK_STATIC is enabled. # CAREFUL HERE!!
if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) if (NOT CFI_FLAG_DETECTED AND (${PREFIX} STREQUAL "CFI"))
string(TOLOWER ${PREFIX} PREFIX_lower) message(STATUS "[CFI] Clang failed on us but not all is lost!!!")
sanitizer_check_compiler_flag( set(CFI_FLAG_DETECTED TRUE)
"-static-lib${PREFIX_lower}" ${LANG} endif()
${PREFIX}_STATIC_FLAG_DETECTED)
if (${PREFIX}_STATIC_FLAG_DETECTED) if (${PREFIX}_FLAG_DETECTED)
set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") # If compiler is a GNU compiler, search for static flag, if
endif () # SANITIZE_LINK_STATIC is enabled.
endif () if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU"))
string(TOLOWER ${PREFIX} PREFIX_lower)
sanitizer_check_compiler_flag(
"-static-lib${PREFIX_lower}" ${LANG}
${PREFIX}_STATIC_FLAG_DETECTED)
set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING if (${PREFIX}_STATIC_FLAG_DETECTED)
"${NAME} flags for ${COMPILER} compiler.") set(FLAG "-static-lib${PREFIX_lower} ${FLAG}")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
break()
endif ()
endforeach ()
if (NOT ${PREFIX}_FLAG_DETECTED)
set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING
"${NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
message(WARNING "${NAME} is not available for ${COMPILER} "
"compiler. Targets using this compiler will be "
"compiled without ${NAME}.")
endif () endif ()
endif ()
set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING
"${NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
break()
endif () endif ()
endforeach () endforeach ()
if (NOT ${PREFIX}_FLAG_DETECTED)
set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING
"${NAME} flags for ${COMPILER} compiler.")
mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS)
message(STATUS "${NAME} is not available for ${COMPILER} "
"compiler. Targets using this compiler will be "
"compiled without ${NAME}.")
endif ()
endif ()
endforeach ()
endfunction () endfunction ()
# Helper to assign sanitizer flags for TARGET. # Helper to assign sanitizer flags for TARGET.
function (sanitizer_add_flags TARGET NAME PREFIX) function (sanitizer_add_flags TARGET NAME PREFIX)
# Get list of compilers used by target and check, if sanitizer is available # Get list of compilers used by target and check, if sanitizer is available
# for this target. Other compiler checks like check for conflicting # for this target. Other compiler checks like check for conflicting
# compilers will be done in add_sanitizers function. # compilers will be done in add_sanitizers function.
sanitizer_target_compilers(${TARGET} TARGET_COMPILER) sanitizer_target_compilers(${TARGET} TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS) list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "")
return() return()
endif() endif()
# Set compile- and link-flags for target. # Set compile- and link-flags for target.
set_property(TARGET ${TARGET} APPEND_STRING set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
set_property(TARGET ${TARGET} APPEND_STRING set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}")
set_property(TARGET ${TARGET} APPEND_STRING set_property(TARGET ${TARGET} APPEND_STRING
PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}")
endfunction () endfunction ()