diff --git a/cmake/FindASan.cmake b/cmake/FindASan.cmake index 2de9ab4..5c95a0b 100644 --- a/cmake/FindASan.cmake +++ b/cmake/FindASan.cmake @@ -1,6 +1,8 @@ # The MIT License (MIT) # -# Copyright (c) 2013 Matthew Arsenault +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,108 +22,25 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -# This module tests if address sanitizer is supported by the compiler. The -# necessary flags for compiler and linker will be stored in variables. ASan can -# be enabled for all targets with CMake build type "ASan", individual targets -# can enable ASan with the saitize_address() function. +option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) -option(SANITIZE_ADDRESS "Selects wheter Address Sanitizer will be enabled for - individual targets" Off) - -set(ASAN_FLAG_CANDIDATES +set(FLAG_CANDIDATES # Clang 3.2+ use this version - "-g -O0 -fsanitize=address" + "-g -O1 -fsanitize=address" # Older deprecated flag for ASan - "-g -O0 -faddress-sanitizer" + "-g -O1 -faddress-sanitizer" ) -set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) -set(CMAKE_REQUIRED_QUIET ${ASan_FIND_QUIETLY}) - -get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) -foreach (LANG ${ENABLED_LANGUAGES}) - # Sanitizer flags are not dependend on language, but the used compiler. So - # instead of searching flags foreach language, search flags foreach compiler - # used. - set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - if (NOT ASAN_${COMPILER}_FLAGS) - foreach (FLAG ${ASAN_FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS - "Try ${COMPILER} AddressSanitizer flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(ASAN_FLAG_DETECTED CACHE) - - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - - elseif (${LANG} STREQUAL "Fortran") - # CheckFortranCompilerFlag was introduced in CMake 3.x. To be - # compatible with older Cmake versions, we will check if this - # module is present before we use it. Otherwise we will define - # Fortran coverage support as not available. - include(CheckFortranCompilerFlag OPTIONAL - RESULT_VARIABLE INCLUDED) - if (INCLUDED) - check_fortran_compiler_flag("${FLAG}" ASAN_FLAG_DETECTED) - elseif (NOT CMAKE_REQUIRED_QUIET) - message("-- Performing Test ASAN_FLAG_DETECTED") - message("-- Performing Test ASAN_FLAG_DETECTED - Failed " - "(Check not supported)") - endif () - endif() - - if (ASAN_FLAG_DETECTED) - set(ASAN_${COMPILER}_FLAGS "${FLAG}" - CACHE STRING "${COMPILER} flags for AddressSanitizer.") - mark_as_advanced(ASAN_${COMPILER}_FLAGS) - break() - endif () - endforeach () - endif () -endforeach () - -set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) - - - - include(sanitize-helpers) +sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan") -function (sanitize_address TARGET) +function (add_sanitize_address TARGET) if (NOT SANITIZE_ADDRESS) return() endif () - # Get list of compilers used by target and check, if target can be checked - # by sanitizer. - sanitizer_target_compilers(${TARGET} TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - if (NUM_COMPILERS GREATER 1) - message(AUTHOR_WARNING "AddressSanitizer disabled for target ${TARGET} " - "because it will be compiled by different compilers.") - return() - - elseif ((NUM_COMPILERS EQUAL 0) OR - (NOT DEFINED "ASAN_${TARGET_COMPILER}_FLAGS")) - message(AUTHOR_WARNING "AddressSanitizer disabled for target ${TARGET} " - "because there is no sanitizer available for target sources.") - return() - endif() - - # Set compile- and link-flags for target. - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${ASAN_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY LINK_FLAGS " ${ASAN_${TARGET_COMPILER}_FLAGS}") + saitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") endfunction () diff --git a/cmake/FindSanitizers.cmake b/cmake/FindSanitizers.cmake new file mode 100644 index 0000000..272bb2d --- /dev/null +++ b/cmake/FindSanitizers.cmake @@ -0,0 +1,50 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +# The following options will enable the desired sanitizers. +option(SANITIZE "Enable all available sanitizers for sanitized targets." OFF) + +# If option SANITIZE is enabled, enable all available sanitizers. +if (SANITIZE) + set(SANITIZE_ADDRESS ON CACHE BOOL + "Enable AddressSanitizer for sanitized targets." FORCE) +endif (SANITIZE) + + + + +set(FIND_QUIETLY_FLAG "") +if (DEFINED Sanitizers_FIND_QUIETLY) + set(FIND_QUIETLY_FLAG "QUIET") +endif () + +find_package(ASan ${FIND_QUIETLY_FLAG}) + + + + +function(add_sanitizers TARGET) + add_sanitize_address(${TARGET}) +endfunction(add_sanitizers) diff --git a/cmake/sanitize-helpers.cmake b/cmake/sanitize-helpers.cmake index f163588..63586f1 100644 --- a/cmake/sanitize-helpers.cmake +++ b/cmake/sanitize-helpers.cmake @@ -64,3 +64,86 @@ function (sanitizer_target_compilers TARGET RETURN_VAR) list(REMOVE_DUPLICATES BUFFER) set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) endfunction () + + +# Helper function to test compiler flags. +function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) + set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + # Sanitizer flags are not dependend on language, but the used compiler. + # So instead of searching flags foreach language, search flags foreach + # compiler used. + set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) + if (NOT ${PREFIX}_${COMPILER}_FLAGS) + foreach (FLAG ${FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(${PREFIX}_FLAG_DETECTED CACHE) + + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ${PREFIX}_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ${PREFIX}_FLAG_DETECTED) + + elseif (${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To + # be compatible with older Cmake versions, we will check if + # this module is present before we use it. Otherwise we will + # define Fortran coverage support as not available. + include(CheckFortranCompilerFlag OPTIONAL + RESULT_VARIABLE INCLUDED) + if (INCLUDED) + check_fortran_compiler_flag("${FLAG}" + ASAN_FLAG_DETECTED) + elseif (NOT CMAKE_REQUIRED_QUIET) + message(STATUS + "Performing Test ${PREFIX}_FLAG_DETECTED") + message(STATUS "Performing Test ${PREFIX}_FLAG_DETECTED" + " - Failed (Check not supported)") + endif () + endif() + + if (${PREFIX}_FLAG_DETECTED) + set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + break() + endif () + endforeach () + endif () + endforeach () +endfunction () + + +# Helper to assign sanitizer flags for TARGET. +function (saitizer_add_flags TARGET NAME PREFIX) + # Get list of compilers used by target and check, if target can be checked + # by sanitizer. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if (NUM_COMPILERS GREATER 1) + message(AUTHOR_WARNING "${NAME} disabled for target ${TARGET} because " + "it will be compiled by different compilers.") + return() + + elseif ((NUM_COMPILERS EQUAL 0) OR + (NOT DEFINED "${PREFIX}_${TARGET_COMPILER}_FLAGS")) + message(WARNING "${NAME} disabled for target ${TARGET} because there is" + " no sanitizer available for target sources.") + return() + endif() + + # Set compile- and link-flags for target. + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") +endfunction () diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bf0649a..6ffb38f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,7 @@ function(add_testcase TESTNAME SOURCEFILES) # add a new executable add_executable(${TESTNAME} ${ARGV}) + add_sanitizers(${TESTNAME}) # add a testcase for executable add_test(${TESTNAME} ${TESTNAME}) @@ -41,10 +42,7 @@ endfunction(add_testcase) # # search for sanitizers # -find_package(ASan) -find_package(MSan) -find_package(TSan) -find_package(UBSan) +find_package(Sanitizers)