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 DLLs. With MSYS2 libdl is available via mingw-w64-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, we use these options on virtually all our projects. We have them set all the time – no need for “if()” statements.

include(GNUInstallDirs)

set(CMAKE_INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH true)

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true)

Exporting symbols for MSVC-based compilers is necessary to generate a “my.lib” corresponding to the “my.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 -DCMAKE_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: