84

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.

ObjSal
  • 1,494
  • 1
  • 14
  • 18
  • 2
    maybe check this out: https://github.com/arsenm/sanitizers-cmake – xaxxon Jun 02 '17 at 05:03
  • 1
    If you know of an Xcode project setting that turns on what you want, you can set it from CMake by populating `CMAKE_XCODE_ATTRIBUTE_...` variables or using the per-target properties `XCODE_ATTRIBUTE_...`. – Craig Scott Jun 03 '17 at 02:40

7 Answers7

86

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.

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)
halfer
  • 19,824
  • 17
  • 99
  • 186
László Papp
  • 51,870
  • 39
  • 111
  • 135
  • Just tried this it builds but no errors detected... When I build the same code using the line `g++ test_with_leak_main.cpp -fsanitize-address -g -std=c++20 -lpthread -static-libasn` it reports the memory leak. – mark Sep 14 '22 at 15:40
  • Keep in mind ASAN needs to be compiled/linked with Clang and _not_ ld. – Melroy van den Berg Dec 12 '22 at 22:58
  • 1
    I used the first form but had both as `$<$:-fsanitize-address>` so it will only be enabled for debug builds. – Aaron D. Marasco Feb 13 '23 at 15:25
  • When using the commands for a particular target (a library that gets linked against from other targets), I had to use PUBLIC instead of PRIVATE, or I'd get linker errors. – SimonH Aug 28 '23 at 11:16
41

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")
MaartenVds
  • 573
  • 6
  • 10
  • 4
    The second line looks wrong to me. Eight it should be CMAKE_STATIC_LINKER_FLAGS_DEBUG on both sides, or CMAKE_LINKER_FLAGS_DEBUG. – proski Dec 11 '17 at 22:30
  • set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=address") worked for me on iOS CMake builds. You should make sure to be using the correct configuration of the library that you build(Debug/Release) – Kristupas A. Feb 22 '18 at 10:40
  • 2
    Modern CMake: `string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit...")` – Timmmm Apr 17 '19 at 11:40
  • 3
    @Timmmm Fiddling with `CMAKE_CXX_FLAGS` directly is not a modern CMake whatsoever. Use of `check_cxx_compiler_flag` would be closest. – mloskot May 04 '19 at 00:00
  • @mloskot a) I was only talking about how to append to a string, and b) `check_cxx_compiler_flag` only checks if a flag is supported - you still need to actually add it! – Timmmm May 04 '19 at 15:32
  • 2
    @Timmmm Well spotted. Thank you. I made copy & paste mistake. I meant `target_compile_options` instead, of course. – mloskot May 04 '19 at 19:15
  • 1
    Ah yes that is indeed cleaner, however in this case it requires you to apply it to every target, which is rather tedious. – Timmmm May 04 '19 at 19:50
  • 2
    @Timmmm: you can use add_compile/link_options to apply it to every targets. But this answer is the wrong way to do it these days. Just added the correct with cmake these days. Please find the answer below: https://stackoverflow.com/a/70272150/2682142 – László Papp Dec 08 '21 at 08:53
19

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

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 \
    ...
    ...
George Hilliard
  • 15,402
  • 9
  • 58
  • 96
serghei
  • 3,069
  • 2
  • 30
  • 48
  • 3
    While this seems a bit complex compared some other answers, it does seem to be the best way to do it. Did you get this info from the book Professional CMake, by chance? – Levi Morrison Apr 23 '21 at 03:22
  • Yes, I adopted this from the book. – serghei Apr 23 '21 at 23:04
  • Anecdotally, this worked for me with cmake version 3.16.3 (and `cmake_minimum_required(VERSION 3.9)`) – alfC May 26 '21 at 02:03
11

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
zxshi
  • 357
  • 3
  • 8
5

First ensure with debug info, such as setting CMAKE_BUILD_TYPE to Debug passing -g flag for GCC/Clang.

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")
ChrisZZ
  • 1,521
  • 2
  • 17
  • 24
2

The simplest solution that I currently found is

cmake -DCMAKE_BUILD_TYPE=ASAN .

I prefer this option since it expresses the intent (run sanitizers) rather than modifies a number of flags.

I do not know when this option was added. I also cannot find any documentation to it.

Unapiedra
  • 15,037
  • 12
  • 64
  • 93
  • 9
    What version of CMake are you using? I can't find any meaningful reference to `ASAN` in the current CMake source code. – Kevin W Matthews Jun 19 '19 at 15:44
  • 1
    Maybe the reference is this: http://www.stablecoder.ca/2018/02/01/analyzer-build-types.html – Martin Gerhardy Oct 19 '19 at 19:48
  • 1
    The provided link, @MartinGerhardy, says at the very top that it is outdated. – Unapiedra Oct 19 '19 at 23:57
  • 2
    This seems to only work for projects that directly query build type or do something like [this `FindASan.cmake` script](https://svn.ec-nantes.fr/eXlibris/Util/cmakeUtil/FindASan.cmake) – Ruslan May 24 '20 at 09:11
  • not working for my cmake 3.18.0 with `cmake_minimum_required(VERSION 3.15)` – ChrisZZ Nov 26 '20 at 06:57
-1

I found that the other answers didn't work, you need to set the init variables in your toolchain file:

set(CMAKE_EXE_LINKER_FLAGS_INIT "-fsanitize=address -fno-omit-frame-pointer")
CodeMonkey
  • 4,067
  • 1
  • 31
  • 43