ObjSal
ObjSal

Reputation: 1794

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

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?

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.

Upvotes: 100

Views: 113572

Answers (7)

Hsilgos
Hsilgos

Reputation: 91

I made it work for Xcode without hardcoding -fsanitize=address. One may work normally in Xcode without sanitizers and when necessary enable them in Scheme settings without re-running CMake.

I copy asan libs to the following folder and that's it.

set (XCODE_DEVELOPER_TOOLCHAINS_ROOT
     "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain")

# Here is detection for iOS/MacOS, but other platforms also can be added here like tvos, watchos, xros
if (IOS)
    set (PLATFORM_SUFFIX "ios")
else ()
    set (PLATFORM_SUFFIX "osx")
endif ()

file (GLOB_RECURSE _asan_libs "${XCODE_DEVELOPER_TOOLCHAINS_ROOT}/*/libclang_rt.*an_${PLATFORM_SUFFIX}*_dynamic.dylib")

foreach (_asan_lib ${_asan_libs})
    file (RELATIVE_PATH _asan_relative_to_toolchain "${XCODE_DEVELOPER_TOOLCHAINS_ROOT}/usr/" "${_asan_lib}")
    configure_file ("${_asan_lib}" "${CMAKE_BINARY_DIR}/../${_asan_relative_to_toolchain}" COPYONLY)
endforeach ()

Also here I answered how to make it work with ccache: https://stackoverflow.com/a/77547119/5517503

Here is demo project https://github.com/Hsilgos/xcode-ccache-demo

Upvotes: 0

László Papp
László Papp

Reputation: 53173

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)

Upvotes: 112

serghei
serghei

Reputation: 3381

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 \
    ...
    ...

Upvotes: 28

ChrisZZ
ChrisZZ

Reputation: 2141

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

Upvotes: 5

Unapiedra
Unapiedra

Reputation: 16197

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.

Upvotes: -3

zxshi
zxshi

Reputation: 387

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

Upvotes: 14

MaartenVds
MaartenVds

Reputation: 643

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

Upvotes: 46

Related Questions