What's the proper way to enable AddressSanitizer in CMake that works in Xcode

45,868

Solution 1

You need to provide the flag(s) to the linker too. I'm doing it like this:

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")

Solution 2

Since the top-voted answer is the wrong way to do it these days and I did not get the proper cmake solution for this reading this thread, I thought I would mention the correct way at the time of writing this so that the next reader does not need to spend much time with this. Hope it helps.

The idea of this solution is to pass -fsanitize=address to the compiler and linker flags.

If you would like to enable this for all your targets at the same time, you can use add_compile_options and add_link_options. This makes sense if you have multiple, potentially a large of, targets.

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

Alternatively, you can also use target_compile_options and target_link_options to set these for a particular target. This might make more sense if you do not want this to apply to all the targets.

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

Solution 3

I propose create your own Asan profile.

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_SHARED_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)

Notes:

  1. AddressSanitizer (ASan) for Windows with MSVC is under experimental stage thus I didn't provided the MSVC way here.

  2. CMAKE_BUILD_TYPE isn't used by multi-configuration generators (Xcode, Visual Studio, etc), thus I provided an example to check this first.

  3. The default value for CMAKE_BUILD_TYPE is an empty string. And user can set CMAKE_BUILD_TYPE to any value at the cmake command line. Therefore, we check both cases, and make sure that we are dealing with a known build type (if provided).

  4. There is also CMAKE_MODULE_LINKER_FLAGS you may want to configure.

Usage:

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

Solution 4

cmake 3.13
introduce configuration for xcode schema

in 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)

When Build

xcodebuild -enableAddressSanitizer YES

Solution 5

First ensure with debug info, such as setting CMAKE_BUILD_TYPE to Debug.

Then, if your target is an executable or an shared library, then you may set those cmake variables:

  • CMAKE_EXE_LINKER_FLAGS
  • CMAKE_EXE_LINKER_FLAGS_DEBUG
  • CMAKE_SHARED_LINKER_FLAGS
  • CMAKE_SHARED_LINKER_FLAGS_DEBUG

i.e. When your target is an executable:

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

When your target is an shared library:

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

However, when your executable relies on an static library and you'd like to use asan to check your static library, then you have to set like this:

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")
Share:
45,868
ObjSal
Author by

ObjSal

Updated on July 08, 2022

Comments

  • ObjSal
    ObjSal almost 2 years

    I've added AddressSanitizer flag as follow:

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

    Everything builds and runs fine when using Unix Makefiles.

    The problem comes when generating the Xcode project, it just doesn't want to link because it cannot find the ASan library.

    I already found two solutions, but decided not to use them because they cannot be automated using just CMake:

    1. Adding -Wl,-undefined,dynamic_lookup to the linked flags, so it skips linking to dynamic libraries.
    2. Link with libclang_rt.asan_osx_dynamic.dylib directly.

    So what's the problem with these two solutions?

    • When using solution #1, I have to manually open the target scheme in Xcode and add DYLD_INSERT_LIBRARIES environment variable pointing to libclang_rt.asan_osx_dynamic.dylib.
    • When using solution #2, the path for the ASan library varies between computers.

    Additionally as another solution, I tried enabling Address Sanitizer flag from the Xcode target scheme but interestingly it didn't detect the issues I added, so I didn't list this as a solution because it failed my test.

    Any help will be much appreciated.