ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Fluent-Bit 构建系统中的 Sanitizer 支持:sanitizers-cmake 模块的原理、选项与集成实践

Fluent-Bit 构建系统中的 Sanitizer 支持:sanitizers-cmake 模块的原理、选项与集成实践 Fluent-Bit 构建系统中的 Sanitizer 支持sanitizers-cmake 模块的原理、选项与集成实践【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bitFluent-Bit 的构建系统内置了 sanitizers-cmake 这一 CMake 模块用于在编译时为二进制目标自动注入 AddressSanitizer、ThreadSanitizer、MemorySanitizer 和 UndefinedBehaviorSanitizer 四类内存/行为检测工具。本文基于该模块自带的 README 文档结合仓库中的 CMake 实现源码完整讲解其集成方式、全部配置选项、编译器标志探测机制以及 Fluent-Bit 顶层构建脚本中的实际接入点读完后可理解如何在 Fluent-Bit 的 CMake 构建流程中安全地启用 sanitizer 构建。模块定位一个可复用的 CMake Sanitizer 封装层sanitizers-cmake 的 README 将该模块定义为 “CMake module to enable sanitizers for binary targets”为二进制目标启用 sanitizers 的 CMake 模块。它的核心价值在于把四类 sanitizer 的编译器差异GCC/Clang/MSVC 的 flag 不同、平台限制TSan/MSan 的系统与位宽要求以及运行时 wrapper 的准备工作统一封装为一组 CMake 选项和函数项目侧只需要一个find_package(Sanitizers)加add_sanitizers(TARGET)即可获得 sanitizer 支持。模块由以下几个核心文件组成均位于 cmake 目录文件职责FindSanitizers.cmake总入口声明SANITIZE_LINK_STATIC选项、级联调用四个子 Find 模块、实现add_sanitizers()与sanitizer_add_blacklist_file()FindASan.cmakeAddressSanitizer 的选项、flag 候选与互斥检查FindTSan.cmakeThreadSanitizer 的选项与平台Linux/macOS、64 位检查FindMSan.cmakeMemorySanitizer 的选项与平台仅 Linux、64 位检查FindUBSan.cmakeUndefinedBehaviorSanitizer 的选项与 flag 探测sanitize-helpers.cmake通用辅助函数源文件语言识别、目标编译器收集、flag 编译探测、按编译器缓存与下发asan-wrapperASan 场景下预加载 AddressSanitizer 运行库的执行包装脚本模块自带 测试目录其中包含asan_test.cpp、shortest.ext.test.cpp等用例及 tests/CMakeLists.txt 中的add_testcase()辅助函数用于验证 flag 探测与黑名单文件名解析例如带点号扩展名的源文件等边界情况。模块版本信息记录在 VERSION 文件 中。集成到项目submodule 或文件复制两种方式README 给出了两种集成方式。第一种是把仓库作为 git submodule 引入并将externals/sanitizers-cmake/cmake加入CMAKE_MODULE_PATHmkdir externals git submodule add gitgithub.com:arsenm/sanitizers-cmake.git externals/sanitizers-cmakeset(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/externals/sanitizers-cmake/cmake ${CMAKE_MODULE_PATH})第二种是不使用 git 时直接复制 cmake 目录 下的文件到自有仓库——README 特别提醒这种方式要自行跟踪上游更新Be careful and keep updates in mind!。Fluent-Bit 采用的是第二种方式模块以 vendored 目录形式存放在 cmake/sanitizers-cmake/ 下。在顶层 CMakeLists.txt 中可以看到实际接线set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/sanitizers-cmake/cmake ${CMAKE_MODULE_PATH}) find_package(Sanitizers)即在把模块目录注入CMAKE_MODULE_PATH之后执行 README 所述的find_package(Sanitizers)。这一步只是“加载模块”是否真正启用 sanitizer 完全由后续命令行传入的选项决定。四个总开关与静态链接选项README 的 Usage 章节说明通过在 CMake 配置时传入SANITIZE_ADDRESS、SANITIZE_MEMORY、SANITIZE_THREAD或SANITIZE_UNDEFINED选项来启用对应 sanitizer例如命令行传入-DSANITIZE_ADDRESSOn也可以经由图形化 CMake 界面设置。这些选项在源码中均为 CMake 的option()默认值一律是OffSANITIZE_ADDRESSFindASan.cmake#L25启用 AddressSanitizerSANITIZE_THREADFindTSan.cmake#L25启用 ThreadSanitizerSANITIZE_MEMORYFindMSan.cmake#L25启用 MemorySanitizerSANITIZE_UNDEFINEDFindUBSan.cmake#L25-L26启用 UndefinedBehaviorSanitizerSANITIZE_LINK_STATICFindSanitizers.cmake#L27当使用 GNU 编译器时尝试静态链接 sanitizer 运行时默认Off。以 Fluent-Bit 为例在 configure 阶段可以这样启用 ASancmake -B build -DSANITIZE_ADDRESSOn cmake --build build若希望同时启用 UBSanASan 与 UBSan 在模块中不冲突可叠加cmake -B build -DSANITIZE_ADDRESSOn -DSANITIZE_UNDEFINEDOn需要注意的是README 明确说明如果当前编译器不支持 sanitizer原文以 Intel 编译器为例模块只会打印警告CMake 会继续处理sanitizer 直接被忽略——不会导致配置失败。互斥约束模块会主动 FATAL_ERROR源码比 README 走得更远一步部分 sanitizer 之间互不兼容模块在探测阶段就直接报错终止配置而不是等到编译期才发现ASan 与 TSan 或 MSan 同时启用时FindASan.cmake#L40-L43 触发FATAL_ERROR AddressSanitizer is not compatible with ThreadSanitizer or MemorySanitizer.TSan 与 MSan 同时启用时FindTSan.cmake#L36-L39 同样FATAL_ERROR。也就是说-DSANITIZE_ADDRESSOn -DSANITIZE_THREADOn这样的组合会在 configure 阶段立即失败这是使用模块时必须记住的第一条红线。平台约束TSan 与 MSan 的自动降级README 没有展开但源码中明确实现的平台限制值得单独说明。ThreadSanitizer 在 FindTSan.cmake#L44-L59 中有两道检查系统必须是 Linux 或 Darwin否则打印警告并将SANITIZE_THREAD强制置回OffCMAKE_SIZEOF_VOID_P必须等于 8即仅支持 64 位系统32 位下同样警告并强制关闭。MemorySanitizer 的限制更严格FindMSan.cmake#L37-L52仅支持 Linux 且仅支持 64 位两个条件任一不满足都会警告并强制关闭选项。这两类降级都是“警告 自动关闭”因此在不支持的平台上开启 TSan/MSan 不会报错只是静默失效——排障时应留意 configure 输出中的 WARNING。编译器标志探测机制flag 候选列表与按编译器缓存README 承诺“如果编译器支持 sanitizer指定目标就会带上 sanitizer 构建”其底层实现在 sanitize-helpers.cmake 的sanitizer_check_compiler_flags()函数L106-L158。以 AddressSanitizer 为例各 Find 模块定义了一份按优先级排列的 flag 候选列表例如 FindASan.cmake#L27-L37set(FLAG_CANDIDATES # MSVC uses /fsanitizeaddress # Clang 3.2 use this version. The no-omit-frame-pointer option is optional. -g -fsanitizeaddress -fno-omit-frame-pointer -g -fsanitizeaddress # Older deprecated flag for ASan -g -faddress-sanitizer )探测流程如下按编译器而非按语言遍历sanitizer_check_compiler_flags()遍历ENABLED_LANGUAGES但取出的是CMAKE_${LANG}_COMPILER_ID如GNU、Clang、MSVC。源码注释指出 “Sanitizer flags are not dependend on language, but the used compiler”因此按编译器维度探测并缓存逐个候选做编译测试候选 flag 写入CMAKE_REQUIRED_FLAGS后调用check_c_compiler_flag/check_cxx_compiler_flagFortran 则走check_fortran_compiler_flag且兼容没有CheckFortranCompilerFlag模块的旧版 CMake见 L87-L100第一个被编译器接受的候选即生效并break()结果写入缓存变量成功的 flag 存入${PREFIX}_${COMPILER}_FLAGS如ASan_GNU_FLAGS并mark_as_advanced失败则置空并打印 “... is not available for ${COMPILER} compiler” 警告——这正是 README 所述“不支持时警告但继续”的实现位置L147-L155。SANITIZE_LINK_STATIC也在此函数中处理L127-L138仅当检测到的编译器 ID 为GNU且该选项为 On 时额外探测-static-libasan|tsan|msan|ubsan若可用则前置到最终 flag 串中。这与 README “Compiler issues” 章节呼应某些场景下如 LD_PRELOAD、动态链接相关的 ASan 问题可以用-DSANITIZE_LINK_STATICOn让 GCC 静态链接 sanitizer 运行时以规避。编译器组合冲突跨编译器目标的自动保护README “Compiler issues” 章节指出同一目标若混合了 C 和 Fortran 代码但分别使用 clang 与 gfortran 编译由于不同编译器可能采用不同的 sanitizer 实现会造成链接问题模块会检测此类问题并对这些目标禁用 sanitizer。这一行为由add_sanitizers()实现FindSanitizers.cmake#L52-L91foreach (TARGET ${ARGV}) get_target_property(TARGET_TYPE ${TARGET} TYPE) if (TARGET_TYPE STREQUAL INTERFACE_LIBRARY) message(WARNING Cant use any sanitizers for target ${TARGET}, because it is an interface library and cannot be compiled directly.) return() endif () sanitizer_target_compilers(${TARGET} TARGET_COMPILER) list(LENGTH TARGET_COMPILER NUM_COMPILERS) if (NUM_COMPILERS GREATER 1) message(WARNING Cant use any sanitizers for target ${TARGET}, because it will be compiled by incompatible compilers. Target will be compiled without sanitizers.) return() endif () ... add_sanitize_address(${TARGET}) add_sanitize_thread(${TARGET}) add_sanitize_memory(${TARGET}) add_sanitize_undefined(${TARGET}) endforeach ()其中sanitizer_target_compilers()sanitize-helpers.cmake#L52-L73遍历目标源文件按扩展名反查所属语言sanitizer_lang_of_source()会正确处理带点号的文件名取最短扩展名收集去重后的编译器 ID 列表。由此得到三种结局恰好一个编译器正常为四个 sanitizer 逐一注入 flag多于一个编译器警告并跳过该目标即 README 所述的链接问题规避零个已知编译器例如目标只由 object library 组成提示一条“可能是误报”的警告不中断。add_sanitizers()的入口还有一个短路若四个SANITIZE_*选项全部未启用直接return()对未开 sanitizer 的常规构建零副作用。为具体目标启用 sanitizer 与黑名单文件README 给出的标准用法是在定义目标之后调用add_sanitizers(TARGET)可选地用sanitizer_add_blacklist_file(FILE)提供 sanitizer 黑名单文件find_package(Sanitizers) sanitizer_add_blacklist_file(blacklist.txt) add_executable(some_exe foo.c bar.c) add_sanitizers(some_exe) add_library(some_lib foo.c bar.c) add_sanitizers(some_lib)源码侧的实现要点FindSanitizers.cmake#L42-L50黑名单路径若为相对路径会先拼上CMAKE_CURRENT_SOURCE_DIR再取REALPATH绝对路径最终生成-fsanitize-blacklist${FILE}探测结果缓存到SanBlist_${COMPILER}_FLAGS在sanitizer_add_flags()中sanitize-helpers.cmake#L162-L178黑名单 flag 与 sanitizer 本体 flag 合并后通过target_compile_options(... PUBLIC ...)下发编译sanitizer 本体 flag 再经target_link_options(... PUBLIC ...)下发链接。注意黑名单 flag 只进编译选项、不进链接选项符合 sanitizer 黑名单的语义。由于使用PUBLIC下发下游链接该目标的目标会继承 sanitizer 编译选项这与模块“按编译器缓存、按目标注入”的设计保持一致。运行期asan-wrapper 预加载包装README “Run your application” 章节说明sanitizer 在程序运行时进行检查某些情况下例如需要对目标做 LD_PRELOAD必须先预加载所使用的 AddressSanitizer 运行库此时应使用ASan_WRAPPER变量指向的asan-wrapper脚本来执行应用${ASan_WRAPPER} myexe arg1 ...该变量并非手写设置而是由 FindASan.cmake#L52-L53 在SANITIZE_ADDRESS开启时通过find_program(ASan_WRAPPER asan-wrapper PATHS ${CMAKE_MODULE_PATH})自动定位到模块目录中的 asan-wrapper 脚本。因此在 Fluent-Bit 的 sanitizer 构建目录中ASan_WRAPPER是随 configure 自动产生的可用变量可用于 CI 流水线或手工运行被测二进制。Fluent-Bit 顶层构建中的实际接入点在 Fluent-Bit 仓库中模块的全部接入点只有三处便于审计模块路径注入与加载CMakeLists.txt#L137-L138 将${PROJECT_SOURCE_DIR}/cmake/sanitizers-cmake/cmake加入CMAKE_MODULE_PATH并find_package(Sanitizers)条件编译宏CMakeLists.txt#L594-L596 中当SANITIZE_ADDRESS被启用时定义FLB_HAVE_SANITIZE_ADDRESS宏# Is sanitize_address defined ? if(SANITIZE_ADDRESS) FLB_DEFINITION(FLB_HAVE_SANITIZE_ADDRESS) endif()从源码结构看该宏供编译产物在需要时感知“本构建是 ASan 构建”使运行期行为可以按构建模式做区分 3.vendored 模块本体cmake/sanitizers-cmake/ 目录即 README 所述模块的完整副本含cmake/、tests/、CMakeLists.txt、LICENSE与VERSION可单独作为子项目其自身 CMakeLists.txt 声明cmake_minimum_required(VERSION 2.8.12)并启用enable_testing()独立构建与运行测试。实践清单在 Fluent-Bit 上启用 sanitizer 构建综合上述机制可操作的配置矩阵与注意事项如下可组合SANITIZE_ADDRESSOnSANITIZE_UNDEFINEDOn可以叠加模块未禁止该组合flag 各自独立注入不可组合ASan × (TSan|MSan)、TSan × MSan 会触发 configure 阶段FATAL_ERROR平台前提TSan 仅 Linux/macOS 且 64 位MSan 仅 Linux 且 64 位不满足时自动警告降级需检查 configure 日志确认是否真正生效编译器前提当前工具链必须能通过候选 flag 编译测试GCC/Clang/MSVC 各有候选 flag不支持时仅警告目标按无 sanitizer 编译规避动态链接问题遇到 ASan 预加载/动态链接类问题时尝试SANITIZE_LINK_STATICOn仅对 GNU 编译器生效;抑制已知误报准备黑名单文件后在find_package(Sanitizers)之后调用sanitizer_add_blacklist_file()路径相对CMAKE_CURRENT_SOURCE_DIR解析运行被测程序ASan 构建下用${ASan_WRAPPER}包装执行命令保证 sanitizer 运行库先于其他预加载库就位。sanitizers-cmake 模块在 Fluent-Bit 构建体系中是一个“低侵入”的可选项不传任何SANITIZE_*选项时add_sanitizers()直接返回常规构建完全不受影响开启后模块会负责编译器能力探测、跨编译器冲突保护、静态链接降级与运行期 wrapper 准备把四类 sanitizer 的工程化差异收敛在一组 CMake 选项之后。【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表