Dynamic libraries and CMake

On Unix-like platforms, CMake variable CMAKE_DL_LIBS is populated to link with target_link_libraries(), providing functions like “dlopen” and “dladdr”. For some libdl functions it’s necessary to also define “_GNU_SOURCE” like:

add_library(mylib SHARED mylib.c)
target_link_libraries(mylib PRIVATE ${CMAKE_DL_LIBS})
target_compile_definitions(mylib PRIVATE _GNU_SOURCE)

On Windows different mechanisms can be used to access dynamic libraries. With MSYS2 libdl is available via mingw-w64-ucrt-x86_64-dlfcn.

Run path (Rpath)

On Unix-like systems, the concept of run path is the search path for libraries used by a binary at runtime. For Windows there is no separate Rpath, just PATH is used–necessary .dll files must be on PATH environment variable at the time of running a binary. For Unix-like systems life can be easier since the Rpath is compiled into a binary. Optionally, using $ORIGIN in Rpath allows relocating binary packages.

For CMake, set all the time – no need for “if()” statements.

include(GNUInstallDirs)

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true)
# must be before all targets

Note that we use CMake defaults (we do NOT set values for these) for the following to avoid problems on HPC where library modules may be loaded dynamically. Instead we allow the end user to set these variables in the top level executable.

CMAKE_INSTALL_NAME_DIR
CMAKE_INSTALL_RPATH
CMAKE_INSTALL_RPATH_USE_LINK_PATH

Exporting symbols for MSVC-based compilers is necessary to generate a “example.lib” corresponding to the “example.dll”.

To have the installed CMake binaries work correctly, it’s necessary to set CMAKE_PREFIX_PATH at configure time. That is, the configure-build-install sequence for shared library project in CMake is like:

cmake -Bbuild -DBUILD_SHARED_LIBS=on --install-prefix=/opt/my_program

cmake --build build

cmake --install build

In general the rpath in a binary can be checked like:

  • Linux: readelf -d /path/to/binary | head -n 25
  • macOS: otool -l /path/to/binary | tail

References: