include(FeatureSummary)
include(cmake/archdetect.cmake)
+include(cmake/detect-sanitizer.cmake)
if(CMAKE_TOOLCHAIN_FILE)
message(STATUS "Using CMake toolchain: ${CMAKE_TOOLCHAIN_FILE}")
#
check_include_file(unistd.h Z_HAVE_UNISTD_H)
-if(WITH_SANITIZERS AND WITH_MSAN)
+if(WITH_MSAN AND WITH_SANITIZERS)
message(FATAL_ERROR "Memory sanitizer is incompatible with address sanitizer")
+elseif(WITH_MSAN)
+ add_memory_sanitizer_check()
+elseif(WITH_SANITIZERS)
+ add_sanitizer_checks()
endif()
-
-if(WITH_MSAN)
- set(CMAKE_REQUIRED_FLAGS "-fsanitize=memory")
- check_c_source_compiles("int main() { return 0; }"
- HAS_MSAN FAIL_REGEX "not supported|unrecognized command")
- if(HAS_MSAN)
- set(SANITIZERS_FLAGS "-fsanitize=memory")
- message(STATUS "Adding memory sanitizer flag: ${SANITIZERS_FLAGS}")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZERS_FLAGS}")
- endif()
- set(CMAKE_REQUIRED_FLAGS)
-endif()
-
-if(WITH_SANITIZERS)
- set(_sanitize_flags
- address
- alignment
- array-bounds
- bool
- bounds
- builtin
- enum
- float-divide-by-zero
- function
- integer-divide-by-zero
- leak
- null
- nonnull-attribute
- object-size
- pointer-compare # Depends on 'address'
- pointer-overflow
- pointer-subtract # Depends on 'address'
- return
- returns-nonnull-attribute
- shift
- shift-base
- shift-exponent
- signed-integer-overflow
- undefined
- unsigned-integer-overflow
- vla-bound
- vptr
- )
- if(NOT UNALIGNED_OK)
- list(APPEND _sanitize_flags alignment)
- endif()
- set(SANITIZERS_FLAGS "")
- foreach(_flag ${_sanitize_flags})
- if(NOT (CMAKE_CROSSCOMPILING_EMULATOR AND ${_flag} STREQUAL "leak")) # LeakSanitizer crashes under qemu
- set(CMAKE_REQUIRED_FLAGS "-fsanitize=${SANITIZERS_FLAGS},${_flag}")
- check_c_source_compiles("int main() { return 0; }"
- HAS_SANITIZER_${_flag} FAIL_REGEX "not supported|unrecognized command")
- if(${HAS_SANITIZER_${_flag}})
- if("${SANITIZERS_FLAGS}" STREQUAL "")
- set(SANITIZERS_FLAGS "${_flag}")
- else()
- set(SANITIZERS_FLAGS "${SANITIZERS_FLAGS},${_flag}")
- endif()
- endif()
- set(CMAKE_REQUIRED_FLAGS)
- endif()
- endforeach()
-
- if(NOT ${SANITIZERS_FLAGS} STREQUAL "")
- set(SANITIZERS_FLAGS "-fsanitize=${SANITIZERS_FLAGS}")
- # Group 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(UNALIGNED_OK AND ${SANITIZERS_FLAGS} MATCHES "alignment")
- set(SANITIZERS_FLAGS "${SANITIZERS_FLAGS} -fno-sanitize=alignment")
- endif()
- endif()
-
- message(STATUS "Adding sanitizers flags: ${SANITIZERS_FLAGS}")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZERS_FLAGS}")
-endif()
-
#
# Check if we can hide zlib internal symbols that are linked between separate source files using hidden
#
--- /dev/null
+# detect-sanitizer.cmake -- Detect supported compiler sanitizer flags
+# Licensed under the Zlib license, see LICENSE.md for details
+
+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; }" HAS_SANITIZER_${check}
+ FAIL_REGEX "not supported|unrecognized command|unknown option")
+
+ set(CMAKE_REQUIRED_FLAGS)
+
+ if(HAS_SANITIZER_${check})
+ set(available_checks ${compile_checks})
+ endif()
+ endforeach()
+
+ set(${supported_checks} ${available_checks})
+endmacro()
+
+macro(add_memory_sanitizer_check)
+ check_sanitizer_support("memory" supported_checks)
+ if(NOT ${supported_checks} STREQUAL "")
+ message(STATUS "Memory sanitizer is enabled")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${supported_checks}")
+ else()
+ message(STATUS "Memory sanitizer is not supported")
+ endif()
+endmacro()
+
+macro(add_sanitizer_checks)
+ set(known_checks
+ address
+ array-bounds
+ bool
+ bounds
+ builtin
+ enum
+ float-divide-by-zero
+ function
+ integer-divide-by-zero
+ null
+ nonnull-attribute
+ object-size
+ pointer-compare # Depends on 'address'
+ pointer-overflow
+ pointer-subtract # Depends on 'address'
+ return
+ returns-nonnull-attribute
+ shift
+ shift-base
+ shift-exponent
+ signed-integer-overflow
+ undefined
+ unsigned-integer-overflow
+ vla-bound
+ vptr
+ )
+
+ # Only check for leak sanitizer if not cross-compiling due to qemu crash
+ if(NOT CMAKE_CROSSCOMPILING_EMULATOR)
+ list(APPEND known_checks leak)
+ endif()
+ # Only check for alignment sanitizer flag if unaligned access is not supported
+ if(NOT UNALIGNED_OK)
+ list(APPEND known_checks alignment)
+ endif()
+
+ check_sanitizer_support("${known_checks}" supported_checks)
+
+ if(NOT ${supported_checks} STREQUAL "")
+ message(STATUS "Supported sanitizers checks are enabled: ${supported_checks}")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -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(UNALIGNED_OK)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=alignment")
+ endif()
+ else()
+ message(STATUS "Sanitizer checks are not supported")
+ endif()
+endmacro()
\ No newline at end of file