detect-sanitizer.cmake 5.6 KB

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