diff --git a/CMakeLists.txt b/CMakeLists.txt index a4baa8c5361f..f57c55b5b61e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,11 @@ OPTION(USE_HDFS "Enable HDFS support (EXPERIMENTAL)" OFF) OPTION(USE_TIMETAG "Set to ON to output time costs" OFF) OPTION(USE_CUDA "Enable CUDA-accelerated training (EXPERIMENTAL)" OFF) OPTION(USE_DEBUG "Set to ON for Debug mode" OFF) +OPTION(USE_SANITIZER "Use santizer flags" OFF) +option(SANITIZER_PATH "Path to sanitizer libs") +set(ENABLED_SANITIZERS "address" "leak" "undefined" CACHE STRING + "Semicolon separated list of sanitizer names. E.g 'address;leak'. Supported sanitizers are +address, leak, undefined and thread.") OPTION(BUILD_STATIC_LIB "Build static library" OFF) OPTION(__BUILD_FOR_R "Set to ON if building lib_lightgbm for use with the R package" OFF) OPTION(__INTEGRATE_OPENCL "Set to ON if building LightGBM with the OpenCL ICD Loader and its dependencies included" OFF) @@ -37,6 +42,14 @@ else() PROJECT(lightgbm LANGUAGES C CXX) endif() +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") + +#-- Sanitizer +if (USE_SANITIZER) + include(cmake/Sanitizer.cmake) + enable_sanitizers("${ENABLED_SANITIZERS}") +endif (USE_SANITIZER) + if(__INTEGRATE_OPENCL) set(__INTEGRATE_OPENCL ON CACHE BOOL "" FORCE) set(USE_GPU OFF CACHE BOOL "" FORCE) diff --git a/cmake/Sanitizer.cmake b/cmake/Sanitizer.cmake new file mode 100644 index 000000000000..5405ef497241 --- /dev/null +++ b/cmake/Sanitizer.cmake @@ -0,0 +1,69 @@ +# Set appropriate compiler and linker flags for sanitizers. +# +# Usage of this module: +# enable_sanitizers("address;leak") + +# Add flags +macro(enable_sanitizer sanitizer) + if(${sanitizer} MATCHES "address") + find_package(ASan) + set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=address") + if (ASan_FOUND) + link_libraries(${ASan_LIBRARY}) + endif (ASan_FOUND) + + elseif(${sanitizer} MATCHES "thread") + find_package(TSan) + set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=thread") + if (TSan_FOUND) + link_libraries(${TSan_LIBRARY}) + endif (TSan_FOUND) + + elseif(${sanitizer} MATCHES "leak") + find_package(LSan) + set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=leak") + if (LSan_FOUND) + link_libraries(${LSan_LIBRARY}) + endif (LSan_FOUND) + + elseif(${sanitizer} MATCHES "undefined") + find_package(UBSan) + set(SAN_COMPILE_FLAGS "${SAN_COMPILE_FLAGS} -fsanitize=undefined -fno-sanitize-recover=undefined") + if (UBSan_FOUND) + link_libraries(${UBSan_LIBRARY}) + endif (UBSan_FOUND) + + else() + message(FATAL_ERROR "Santizer ${sanitizer} not supported.") + endif() +endmacro() + +macro(enable_sanitizers SANITIZERS) + # Check sanitizers compatibility. + foreach ( _san ${SANITIZERS} ) + string(TOLOWER ${_san} _san) + if (_san MATCHES "thread") + if (${_use_other_sanitizers}) + message(FATAL_ERROR + "thread sanitizer is not compatible with ${_san} sanitizer.") + endif() + set(_use_thread_sanitizer 1) + else () + if (${_use_thread_sanitizer}) + message(FATAL_ERROR + "${_san} sanitizer is not compatible with thread sanitizer.") + endif() + set(_use_other_sanitizers 1) + endif() + endforeach() + + message("Sanitizers: ${SANITIZERS}") + + foreach( _san ${SANITIZERS} ) + string(TOLOWER ${_san} _san) + enable_sanitizer(${_san}) + endforeach() + message("Sanitizers compile flags: ${SAN_COMPILE_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SAN_COMPILE_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SAN_COMPILE_FLAGS}") +endmacro() diff --git a/cmake/modules/FindASan.cmake b/cmake/modules/FindASan.cmake new file mode 100644 index 000000000000..e7b273853337 --- /dev/null +++ b/cmake/modules/FindASan.cmake @@ -0,0 +1,13 @@ +set(ASan_LIB_NAME ASan) + +find_library(ASan_LIBRARY + NAMES libasan.so libasan.so.5 libasan.so.4 libasan.so.3 libasan.so.2 libasan.so.1 libasan.so.0 + PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ASan DEFAULT_MSG + ASan_LIBRARY) + +mark_as_advanced( + ASan_LIBRARY + ASan_LIB_NAME) diff --git a/cmake/modules/FindLSan.cmake b/cmake/modules/FindLSan.cmake new file mode 100644 index 000000000000..3f68fb05bf08 --- /dev/null +++ b/cmake/modules/FindLSan.cmake @@ -0,0 +1,13 @@ +set(LSan_LIB_NAME lsan) + +find_library(LSan_LIBRARY + NAMES liblsan.so liblsan.so.0 liblsan.so.0.0.0 + PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LSan DEFAULT_MSG + LSan_LIBRARY) + +mark_as_advanced( + LSan_LIBRARY + LSan_LIB_NAME) diff --git a/cmake/modules/FindTSan.cmake b/cmake/modules/FindTSan.cmake new file mode 100644 index 000000000000..aa01802f8063 --- /dev/null +++ b/cmake/modules/FindTSan.cmake @@ -0,0 +1,13 @@ +set(TSan_LIB_NAME tsan) + +find_library(TSan_LIBRARY + NAMES libtsan.so libtsan.so.0 libtsan.so.0.0.0 + PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(TSan DEFAULT_MSG + TSan_LIBRARY) + +mark_as_advanced( + TSan_LIBRARY + TSan_LIB_NAME) diff --git a/cmake/modules/FindUBSan.cmake b/cmake/modules/FindUBSan.cmake new file mode 100644 index 000000000000..e1b72eb6df1b --- /dev/null +++ b/cmake/modules/FindUBSan.cmake @@ -0,0 +1,13 @@ +set(UBSan_LIB_NAME UBSan) + +find_library(UBSan_LIBRARY + NAMES libubsan.so libubsan.so.5 libubsan.so.4 libubsan.so.3 libubsan.so.2 libubsan.so.1 libubsan.so.0 + PATHS ${SANITIZER_PATH} /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib ${CMAKE_PREFIX_PATH}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UBSan DEFAULT_MSG + UBSan_LIBRARY) + +mark_as_advanced( + UBSan_LIBRARY + UBSan_LIB_NAME)