| # detect-sanitizer.cmake -- Detect supported compiler sanitizer flags |
| # Licensed under the Zlib license, see LICENSE.md for details |
| |
| macro(add_common_sanitizer_flags) |
| if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") |
| add_compile_options(-g3) |
| endif() |
| check_c_compiler_flag(-fno-omit-frame-pointer HAVE_NO_OMIT_FRAME_POINTER) |
| if(HAVE_NO_OMIT_FRAME_POINTER) |
| add_compile_options(-fno-omit-frame-pointer) |
| add_link_options(-fno-omit-frame-pointer) |
| endif() |
| check_c_compiler_flag(-fno-optimize-sibling-calls HAVE_NO_OPTIMIZE_SIBLING_CALLS) |
| if(HAVE_NO_OPTIMIZE_SIBLING_CALLS) |
| add_compile_options(-fno-optimize-sibling-calls) |
| add_link_options(-fno-optimize-sibling-calls) |
| endif() |
| endmacro() |
| |
| macro(check_sanitizer_support known_checks supported_checks) |
| set(available_checks "") |
| |
| # Build list of supported sanitizer flags by incrementally trying compilation with |
| # known sanitizer checks |
| |
| foreach(check ${known_checks}) |
| if(available_checks STREQUAL "") |
| set(compile_checks "${check}") |
| else() |
| set(compile_checks "${available_checks},${check}") |
| endif() |
| |
| set(CMAKE_REQUIRED_FLAGS -fsanitize=${compile_checks}) |
| |
| check_c_source_compiles("int main() { return 0; }" HAVE_SANITIZER_${check} |
| FAIL_REGEX "not supported|unrecognized command|unknown option") |
| |
| set(CMAKE_REQUIRED_FLAGS) |
| |
| if(HAVE_SANITIZER_${check}) |
| set(available_checks ${compile_checks}) |
| endif() |
| endforeach() |
| |
| set(${supported_checks} ${available_checks}) |
| endmacro() |
| |
| macro(add_address_sanitizer) |
| set(known_checks |
| address |
| pointer-compare |
| pointer-subtract |
| ) |
| |
| check_sanitizer_support("${known_checks}" supported_checks) |
| if(NOT ${supported_checks} STREQUAL "") |
| message(STATUS "Address sanitizer is enabled: ${supported_checks}") |
| add_compile_options(-fsanitize=${supported_checks}) |
| add_link_options(-fsanitize=${supported_checks}) |
| add_common_sanitizer_flags() |
| else() |
| message(STATUS "Address sanitizer is not supported") |
| endif() |
| |
| if(CMAKE_CROSSCOMPILING_EMULATOR) |
| # Only check for leak sanitizer if not cross-compiling due to qemu crash |
| message(WARNING "Leak sanitizer is not supported when cross compiling") |
| else() |
| # Leak sanitizer requires address sanitizer |
| check_sanitizer_support("leak" supported_checks) |
| if(NOT ${supported_checks} STREQUAL "") |
| message(STATUS "Leak sanitizer is enabled: ${supported_checks}") |
| add_compile_options(-fsanitize=${supported_checks}) |
| add_link_options(-fsanitize=${supported_checks}) |
| add_common_sanitizer_flags() |
| else() |
| message(STATUS "Leak sanitizer is not supported") |
| endif() |
| endif() |
| endmacro() |
| |
| macro(add_memory_sanitizer) |
| check_sanitizer_support("memory" supported_checks) |
| if(NOT ${supported_checks} STREQUAL "") |
| message(STATUS "Memory sanitizer is enabled: ${supported_checks}") |
| add_compile_options(-fsanitize=${supported_checks}) |
| add_link_options(-fsanitize=${supported_checks}) |
| add_common_sanitizer_flags() |
| |
| check_c_compiler_flag(-fsanitize-memory-track-origins HAVE_MEMORY_TRACK_ORIGINS) |
| if(HAVE_MEMORY_TRACK_ORIGINS) |
| add_compile_options(-fsanitize-memory-track-origins) |
| add_link_options(-fsanitize-memory-track-origins) |
| endif() |
| else() |
| message(STATUS "Memory sanitizer is not supported") |
| endif() |
| endmacro() |
| |
| macro(add_thread_sanitizer) |
| check_sanitizer_support("thread" supported_checks) |
| if(NOT ${supported_checks} STREQUAL "") |
| message(STATUS "Thread sanitizer is enabled: ${supported_checks}") |
| add_compile_options(-fsanitize=${supported_checks}) |
| add_link_options(-fsanitize=${supported_checks}) |
| add_common_sanitizer_flags() |
| else() |
| message(STATUS "Thread sanitizer is not supported") |
| endif() |
| endmacro() |
| |
| macro(add_undefined_sanitizer) |
| set(known_checks |
| array-bounds |
| bool |
| bounds |
| builtin |
| enum |
| float-cast-overflow |
| float-divide-by-zero |
| function |
| integer-divide-by-zero |
| local-bounds |
| null |
| nonnull-attribute |
| pointer-overflow |
| return |
| returns-nonnull-attribute |
| shift |
| shift-base |
| shift-exponent |
| signed-integer-overflow |
| undefined |
| unsigned-integer-overflow |
| unsigned-shift-base |
| vla-bound |
| vptr |
| ) |
| |
| # Only check for alignment sanitizer flag if unaligned access is not supported |
| if(NOT WITH_UNALIGNED) |
| list(APPEND known_checks alignment) |
| endif() |
| # Object size sanitizer has no effect at -O0 and produces compiler warning if enabled |
| if(NOT CMAKE_C_FLAGS MATCHES "-O0") |
| list(APPEND known_checks object-size) |
| endif() |
| |
| check_sanitizer_support("${known_checks}" supported_checks) |
| |
| if(NOT ${supported_checks} STREQUAL "") |
| message(STATUS "Undefined behavior sanitizer is enabled: ${supported_checks}") |
| add_compile_options(-fsanitize=${supported_checks}) |
| add_link_options(-fsanitize=${supported_checks}) |
| |
| # Group sanitizer flag -fsanitize=undefined will automatically add alignment, even if |
| # it is not in our sanitize flag list, so we need to explicitly disable alignment sanitizing. |
| if(WITH_UNALIGNED) |
| add_compile_options(-fno-sanitize=alignment) |
| endif() |
| |
| add_common_sanitizer_flags() |
| else() |
| message(STATUS "Undefined behavior sanitizer is not supported") |
| endif() |
| endmacro() |