1# detect-sanitizer.cmake -- Detect supported compiler sanitizer flags 2# Licensed under the Zlib license, see LICENSE.md for details 3 4macro(add_common_sanitizer_flags) 5 if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") 6 add_compile_options(-g3) 7 endif() 8 check_c_compiler_flag(-fno-omit-frame-pointer HAVE_NO_OMIT_FRAME_POINTER) 9 if(HAVE_NO_OMIT_FRAME_POINTER) 10 add_compile_options(-fno-omit-frame-pointer) 11 add_link_options(-fno-omit-frame-pointer) 12 endif() 13 check_c_compiler_flag(-fno-optimize-sibling-calls HAVE_NO_OPTIMIZE_SIBLING_CALLS) 14 if(HAVE_NO_OPTIMIZE_SIBLING_CALLS) 15 add_compile_options(-fno-optimize-sibling-calls) 16 add_link_options(-fno-optimize-sibling-calls) 17 endif() 18endmacro() 19 20macro(check_sanitizer_support known_checks supported_checks) 21 set(available_checks "") 22 23 # Build list of supported sanitizer flags by incrementally trying compilation with 24 # known sanitizer checks 25 26 foreach(check ${known_checks}) 27 if(available_checks STREQUAL "") 28 set(compile_checks "${check}") 29 else() 30 set(compile_checks "${available_checks},${check}") 31 endif() 32 33 set(CMAKE_REQUIRED_FLAGS -fsanitize=${compile_checks}) 34 35 check_c_source_compiles("int main() { return 0; }" HAVE_SANITIZER_${check} 36 FAIL_REGEX "not supported|unrecognized command|unknown option") 37 38 set(CMAKE_REQUIRED_FLAGS) 39 40 if(HAVE_SANITIZER_${check}) 41 set(available_checks ${compile_checks}) 42 endif() 43 endforeach() 44 45 set(${supported_checks} ${available_checks}) 46endmacro() 47 48macro(add_address_sanitizer) 49 set(known_checks 50 address 51 pointer-compare 52 pointer-subtract 53 ) 54 55 check_sanitizer_support("${known_checks}" supported_checks) 56 if(NOT ${supported_checks} STREQUAL "") 57 message(STATUS "Address sanitizer is enabled: ${supported_checks}") 58 add_compile_options(-fsanitize=${supported_checks}) 59 add_link_options(-fsanitize=${supported_checks}) 60 add_common_sanitizer_flags() 61 else() 62 message(STATUS "Address sanitizer is not supported") 63 endif() 64 65 if(CMAKE_CROSSCOMPILING_EMULATOR) 66 # Only check for leak sanitizer if not cross-compiling due to qemu crash 67 message(WARNING "Leak sanitizer is not supported when cross compiling") 68 else() 69 # Leak sanitizer requires address sanitizer 70 check_sanitizer_support("leak" supported_checks) 71 if(NOT ${supported_checks} STREQUAL "") 72 message(STATUS "Leak sanitizer is enabled: ${supported_checks}") 73 add_compile_options(-fsanitize=${supported_checks}) 74 add_link_options(-fsanitize=${supported_checks}) 75 add_common_sanitizer_flags() 76 else() 77 message(STATUS "Leak sanitizer is not supported") 78 endif() 79 endif() 80endmacro() 81 82macro(add_memory_sanitizer) 83 check_sanitizer_support("memory" supported_checks) 84 if(NOT ${supported_checks} STREQUAL "") 85 message(STATUS "Memory sanitizer is enabled: ${supported_checks}") 86 add_compile_options(-fsanitize=${supported_checks}) 87 add_link_options(-fsanitize=${supported_checks}) 88 add_common_sanitizer_flags() 89 90 check_c_compiler_flag(-fsanitize-memory-track-origins HAVE_MEMORY_TRACK_ORIGINS) 91 if(HAVE_MEMORY_TRACK_ORIGINS) 92 add_compile_options(-fsanitize-memory-track-origins) 93 add_link_options(-fsanitize-memory-track-origins) 94 endif() 95 else() 96 message(STATUS "Memory sanitizer is not supported") 97 endif() 98endmacro() 99 100macro(add_thread_sanitizer) 101 check_sanitizer_support("thread" supported_checks) 102 if(NOT ${supported_checks} STREQUAL "") 103 message(STATUS "Thread sanitizer is enabled: ${supported_checks}") 104 add_compile_options(-fsanitize=${supported_checks}) 105 add_link_options(-fsanitize=${supported_checks}) 106 add_common_sanitizer_flags() 107 else() 108 message(STATUS "Thread sanitizer is not supported") 109 endif() 110endmacro() 111 112macro(add_undefined_sanitizer) 113 set(known_checks 114 array-bounds 115 bool 116 bounds 117 builtin 118 enum 119 float-cast-overflow 120 float-divide-by-zero 121 function 122 integer-divide-by-zero 123 local-bounds 124 null 125 nonnull-attribute 126 pointer-overflow 127 return 128 returns-nonnull-attribute 129 shift 130 shift-base 131 shift-exponent 132 signed-integer-overflow 133 undefined 134 unsigned-integer-overflow 135 unsigned-shift-base 136 vla-bound 137 vptr 138 ) 139 140 # Only check for alignment sanitizer flag if unaligned access is not supported 141 if(NOT WITH_UNALIGNED) 142 list(APPEND known_checks alignment) 143 endif() 144 # Object size sanitizer has no effect at -O0 and produces compiler warning if enabled 145 if(NOT CMAKE_C_FLAGS MATCHES "-O0") 146 list(APPEND known_checks object-size) 147 endif() 148 149 check_sanitizer_support("${known_checks}" supported_checks) 150 151 if(NOT ${supported_checks} STREQUAL "") 152 message(STATUS "Undefined behavior sanitizer is enabled: ${supported_checks}") 153 add_compile_options(-fsanitize=${supported_checks}) 154 add_link_options(-fsanitize=${supported_checks}) 155 156 # Group sanitizer flag -fsanitize=undefined will automatically add alignment, even if 157 # it is not in our sanitize flag list, so we need to explicitly disable alignment sanitizing. 158 if(WITH_UNALIGNED) 159 add_compile_options(-fno-sanitize=alignment) 160 endif() 161 162 add_common_sanitizer_flags() 163 else() 164 message(STATUS "Undefined behavior sanitizer is not supported") 165 endif() 166endmacro() 167