Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using --whole-archive linker option with CMake and libraries with other library dependencies

Tags:

c++

g++

cmake

I've got a project that used to be a giant set of source files that all got compiled and then linked as one executable. As a first step in making the project more modular, I am breaking up the build into several smaller chunks and making them static libraries. There's a hierarchy, so Exe1 will link against static libs Lib2A and Lib2B. Lib2A will depend on static Lib3A, lib3B, lib3C, etc. The numbers here show their layer in the hierarchy.

The problem is that I need to use --whole-archive when linking or else some symbols from the underlying libraries are not found.

When I add the below for the linking of Exe1:

target_link_libraries(Exe1 -Wl,--whole-archive Lib2A Lib2B -Wl,--no-whole-archive)

I end up with an actual link stage command like:

g++ -o Exe1 -Wl,--whole-archive libLib2A.a libLib2B.a -Wl,--no-whole-archive libLib3A.a libLib3B.a libLib3C.a

Inevitably, symbols from some of the layer 3 static libraries get lost and I get missing symbol errors.

I expected that because Lib2A has Lib3* libraries as dependencies, that they would also be "inside" the --whole-archive part of the linker command, but they show up outside.

I've tried many different combinations (e.g. putting the --whole-archive stuff at lower layers), but haven't come across an approach that works using CMake. What am I doing wrong?

Thanks

like image 335
Rich von Lehe Avatar asked Oct 30 '18 19:10

Rich von Lehe


2 Answers

For 3.12 and newer versions of CMake, I would use object libraries.

The workaround I found for versions earlier than that was to create an intermediate static library that used some property magic to place all linkage dependencies inside the --whole-archive section. For me, the top-level static library was called 'source'. It contained actually nothing itself, but had linkage dependencies on a bunch of other static libraries. I created 'source-combined' as follows:

add_library(source-combined STATIC "")
set_target_properties(source-combined PROPERTIES LINKER_LANGUAGE CXX)

target_link_libraries(source-combined PUBLIC
  -Wl,--whole-archive
  $<TARGET_PROPERTY:source,INTERFACE_LINK_LIBRARIES>
  -Wl,--no-whole-archive
)

Now when I create an executable or a shared library by linking against this souce-combined library, I get the --whole-archive and --no-whole-archive as bookends around the entire set of static libraries that were the link dependencies of 'source'. It took forever to stumble across this technique, so I'm sharing it.

like image 141
Rich von Lehe Avatar answered Sep 16 '22 12:09

Rich von Lehe


The following worked for me. Consider two libraries:

  • my_platform
  • my_clib

We want the whole archive of my_clib, and my_platform links to it.

add_library(my_platform INTERFACE) # this could also be a regular library

add_library(my_clib STATIC)
target_sources(my_clib
PRIVATE 
    gcc_newlib_nano.c 
    gcc_newlib_nano_cpp.cc 
)

# Link my_clib and any other libs
target_link_libraries(my_platform
INTERFACE
    my_clib
)
# Ensure the whole archive is linked
target_link_options(my_platform
INTERFACE
-Wl,--whole-archive ${CMAKE_CURRENT_BINARY_DIR}/libmy_clib.a -Wl,--no-whole-archive
)
like image 44
driedler Avatar answered Sep 19 '22 12:09

driedler