在Xcode中启用AddressSanitizer的正确方法是什么?请提供CMake的实现方式。

86

我已经按照如下方式添加了 AddressSanitizer 标志:

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")

使用Unix Makefiles时,一切构建和运行都很好。

生成Xcode项目时出现问题,因为它找不到ASan库,所以无法链接。

我已经找到了两个解决方案,但决定不使用它们,因为它们无法仅使用CMake自动化:

  1. -Wl,-undefined,dynamic_lookup添加到链接标志中,这样它就会跳过链接到动态库。
  2. 直接链接libclang_rt.asan_osx_dynamic.dylib

那么这两种解决方案有什么问题呢?

  • 使用解决方案#1时,我必须手动打开Xcode中的目标方案并添加指向libclang_rt.asan_osx_dynamic.dylibDYLD_INSERT_LIBRARIES环境变量。
  • 使用解决方案#2时,ASan库的路径在计算机之间变化。

另外作为另一个解决方案,我尝试从Xcode目标方案启用Address Sanitizer标志,但有趣的是它没有检测到我添加的问题,因此我没有将其列为解决方案,因为它未通过我的测试。

任何帮助将不胜感激。


2
也许你可以看看这个:https://github.com/arsenm/sanitizers-cmake - xaxxon
1
如果您知道一个Xcode项目设置可以打开您想要的功能,您可以通过填充CMAKE_XCODE_ATTRIBUTE_...变量或使用每个目标属性XCODE_ATTRIBUTE_...来从CMake中设置它。 - Craig Scott
7个回答

96

因为目前得票最高的答案已经过时,而我在阅读这个帖子时也没有得到正确的cmake解决方案,所以我想提到编写本文时的正确方法,以便下一个读者不需要花费太多时间进行处理。

这个解决方案的想法是将-fsanitize=address传递给编译器和链接器标志。

如果您想同时为所有目标启用此选项,可以使用add_compile_optionsadd_link_options。如果您有多个、可能很多的目标,这是有意义的。

add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)

或者您也可以使用target_compile_optionstarget_link_options来为特定目标设置这些选项。如果您不想将其应用于所有目标,则可能更有意义。

target_compile_options(asan-target PRIVATE -fsanitize=address)
target_link_options(asan-target PRIVATE -fsanitize=address)

刚试了一下,它能够构建成功,没有发现任何错误... 当我使用以下命令行编译相同的代码 g++ test_with_leak_main.cpp -fsanitize-address -g -std=c++20 -lpthread -static-libasn 时,它会报告内存泄漏。 - mark
请记住,ASAN需要使用Clang进行编译/链接,而不是ld。 - Melroy van den Berg
3
我使用了第一种形式,但是两者都作为 $<$<CONFIG:Debug>:-fsanitize-address>,因此它只会在调试构建中启用。 - Aaron D. Marasco
使用特定目标(作为其他目标链接的库)的命令时,我必须使用PUBLIC而不是PRIVATE,否则会出现链接器错误。 - SimonH

41
您需要向链接器提供标志(flag)。我是这样做的:
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")

4
我认为第二行看起来有误。应该在两侧都是CMAKE_STATIC_LINKER_FLAGS_DEBUG,或者是CMAKE_LINKER_FLAGS_DEBUG。 - proski
2
现代 CMake:string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit...") - Timmmm
3
直接调整CMAKE_CXX_FLAGS不是现代化的CMake方式。最接近的方法是使用check_cxx_compiler_flag - mloskot
2
@Timmmm非常敏锐,谢谢你。我复制粘贴时出了错,我当然是想用target_compile_options - mloskot
2
@Timmmm:您可以使用add_compile/link_options来将其应用于每个目标。但这种做法已经过时了。现在使用CMake添加正确的方式。请查看下面的答案:https://dev59.com/B1cP5IYBdhLWcg3wf6AG#70272150 - László Papp
显示剩余3条评论

20

我建议创建您自己的Asan个人资料。

get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)

if(isMultiConfig)
    if(NOT "Asan" IN_LIST CMAKE_CONFIGURATION_TYPES)
        list(APPEND CMAKE_CONFIGURATION_TYPES Asan)
    endif()
else()
    set(allowedBuildTypes Asan Debug Release RelWithDebInfo MinSizeRel)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowedBuildTypes}")

    if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE IN_LIST allowedBuildTypes)
        message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
    endif()
endif()

set(CMAKE_C_FLAGS_ASAN
    "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer" CACHE STRING
    "Flags used by the C compiler for Asan build type or configuration." FORCE)

set(CMAKE_CXX_FLAGS_ASAN
    "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer" CACHE STRING
    "Flags used by the C++ compiler for Asan build type or configuration." FORCE)

set(CMAKE_EXE_LINKER_FLAGS_ASAN
    "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address" CACHE STRING
    "Linker flags to be used to create executables for Asan build type." FORCE)

set(CMAKE_SHARED_LINKER_FLAGS_ASAN
    "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=address" CACHE STRING
    "Linker lags to be used to create shared libraries for Asan build type." FORCE)

注:

  1. Windows平台上使用MSVC的AddressSanitizer(ASan)仍处于试验阶段,因此我没有在这里提供MSVC的方法。

  2. CMAKE_BUILD_TYPE对于多配置生成器(如Xcode、Visual Studio等)是无效的,因此我提供了一个检查此选项的示例。

  3. CMAKE_BUILD_TYPE的默认值为“空字符串”,用户可以在cmake命令行中设置任何值。因此,我们需要检查两种情况,并确保我们处理的是已知的构建类型(如果提供)。

  4. 您还可以配置CMAKE_MODULE_LINKER_FLAGS

用法:

$ cmake \
    -DCMAKE_BUILD_TYPE=Asan \
    ...
    ...

3
虽然与其他答案相比,这似乎有点复杂,但确实是最好的方法。你是不是从《Professional CMake》这本书中获得了这些信息? - Levi Morrison
是的,我从书上采用了这个。 - serghei
据我的经验,这在cmake版本3.16.3上有效(以及cmake_minimum_required(VERSION 3.9))。 - alfC

12

cmake 3.13
介绍了针对xcode schema的配置。

CMake中。

cmake_minimum_required(VERSION 3.13)
set(CMAKE_XCODE_GENERATE_SCHEME ON)
set(CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER ON)
set(CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN ON)

构建时

xcodebuild -enableAddressSanitizer YES

5

首先要确保开启调试信息,比如使用-g参数设置GCC/Clang,并且不要忘记将CMAKE_BUILD_TYPE设置为Debug

接下来,如果你的目标是可执行文件或共享库,你可以设置以下CMake变量:

  • CMAKE_EXE_LINKER_FLAGS
  • CMAKE_EXE_LINKER_FLAGS_DEBUG
  • CMAKE_SHARED_LINKER_FLAGS
  • CMAKE_SHARED_LINKER_FLAGS_DEBUG

例如,当你的目标是可执行文件时:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")

当你的目标是一个共享库时:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")

但是,当你的可执行文件依赖于静态库,而你又想使用ASAN来检查你的静态库时,你需要像这样进行设置:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")

0

我目前找到的最简单的解决方案是

Original Answer翻译成"最初的回答"

cmake -DCMAKE_BUILD_TYPE=ASAN .

我更喜欢使用这个选项,因为它表达了意图(运行清洁程序),而不是修改许多标记。
我不知道这个选项是什么时候加入的。我也找不到任何相关的文档。
最初的回答: 我更喜欢使用这个选项,因为它表达了意图(运行清洁程序),而不是修改许多标记。
我不确定这个选项是何时添加的,也无法找到任何相关文档。

9
你使用的是哪个版本的CMake?我在当前的CMake源代码中找不到任何有意义的关于ASAN的参考。 - Kevin W Matthews
1
也许这里的参考链接是:http://www.stablecoder.ca/2018/02/01/analyzer-build-types.html - Martin Gerhardy
1
提供的链接@MartinGerhardy在顶部明确说明已经过时。 - Unapiedra
2
这似乎只适用于直接查询构建类型或执行类似于FindASan.cmake脚本的操作的项目。 - Ruslan
我的cmake 3.18.0版本无法工作,即使使用了cmake_minimum_required(VERSION 3.15) - ChrisZZ

-1

我发现其他答案不起作用,你需要在工具链文件中设置初始变量:

set(CMAKE_EXE_LINKER_FLAGS_INIT "-fsanitize=address -fno-omit-frame-pointer")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接