2

I have a shared library in which the tests for a project are located. This is using Cgreen as a test framework. This allows one to run all the tests using the cgreen runner as this:

cgreen-runner my-tests.so

I can do this from CMake pretty easy:

add_test(NAME mytest COMMAND ${CGREEN_EXECUTABLE} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/my-tests.so)

The problem comes when I'm building my-tests.so with ASAN. Because it is a shared library built with ASAN loaded by an executable that's not built with ASAN I have to use LD_PRELOAD in order to load libasan.so (as covered by the ASAN FAQ).

This is easy to do from the command line without needing to know where libasan.so is located, as long as you know the compiler used to compile the tests:

LD_PRELOAD=`gcc -print-file-name=libasan.so` cgreen-runner my-tests.so

I can set LD_PRELOAD from CMake by using set_property:

set_property(TEST mytest PROPERTY ENVIRONMENT "LD_PRELOAD=/path/to/libasan.so")

Obviously, I don't want to have a hardcoded path there. Is there a way to obtain this path when configuring cmake?

icebp
  • 1,608
  • 1
  • 14
  • 24

2 Answers2

3

You can simply call the command from CMake:

execute_process(COMMAND gcc -print-file-name=libasan.so
                OUTPUT_VARIABLE LIBASAN_PATH
                OUTPUT_STRIP_TRAILING_WHITESPACE)

To catch failures you can pass a RESULT_VARIABLE and check if its value is equal to 0.

Botje
  • 26,269
  • 3
  • 31
  • 41
  • 3
    This could work. I think it is better to use `CMAKE_C_COMPILER` instead of `gcc` directly as someone may use `clang` and I'm not sure if things will work as intended in that case. Thanks! – icebp Nov 02 '20 at 14:41
1

In case you are on a Fedora/RHEL system which uses INPUT syntax, see https://stackoverflow.com/a/54386573/9759769, you can find the location with cmake like so:

set(__LOCATE_LIBASAN_CODE "int main() {return 0;}")
try_compile(
  STATUS SOURCE_FROM_VAR
  locateLibAsan.cpp __LOCATE_LIBASAN_CODE
  OUTPUT_VARIABLE __CMAKE_C_COMPILER_OUTPUT
  LINK_OPTIONS -fsanitize=address
  COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/locate_libasan NO_LOG)
if(NOT STATUS)
  message(FATAL_ERROR "Could not find location for libasan.so: ${__CMAKE_C_COMPILER_OUTPUT}")
else()
  execute_process(
    COMMAND ldd ${CMAKE_CURRENT_BINARY_DIR}/locate_libasan
    OUTPUT_VARIABLE LDD_OUTPUT
    OUTPUT_STRIP_TRAILING_WHITESPACE
    RESULT_VARIABLE STATUS)
  if(STATUS
     AND NOT
         STATUS
         EQUAL
         0)
    message(FATAL_ERROR "Could not find location for libasan.so: ${STATUS}")
  else()
    string(
      REGEX
      REPLACE ".*libasan\.so(\.[0-9]+) => ([a-zA-Z0-9\/\.]+).*"
              "\\2"
              LIBASAN_LOCATION
              ${LDD_OUTPUT})
    message(STATUS "Found libasan.so: ${LIBASAN_LOCATION}")
    file(WRITE "${CMAKE_BINARY_DIR}/.env" "LD_PRELOAD=${LIBASAN_LOCATION}")
  endif()
endif()
Max
  • 638
  • 1
  • 4
  • 19