4

Code for a shared library is modular, consists of several independent units. Each unit is built into a static library.


unit1.c

#include <stdio.h>

void HelloWorld() {
  printf("Hello World!\n");
}

unit2.c

#include <stdio.h>

void GoodbyeWorld() {
  printf("Goodbye World!\n");
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)

add_library(unit1 STATIC unit1.c)
target_compile_options(unit1 PRIVATE -fPIC)

add_library(unit2 STATIC unit2.c)
target_compile_options(unit2 PRIVATE -fPIC)

add_library(merged SHARED)
target_link_libraries(merged unit1 unit2)
set_target_properties(merged PROPERTIES LINKER_LANGUAGE C)

Build steps:

cmake . && cmake --build .

Exported symbols by libmerged.so:

$ nm -D --defined-only libmerged.so 
0000000000201020 B __bss_start
0000000000201020 D _edata
0000000000201028 B _end
00000000000005a0 T _fini
0000000000000458 T _init

Q Why symbols HelloWorld and GoodbyeWorld are not exported? How to fix it?

  • I've tried --version-script without success.

    Additional setting in CMakeLists.txt

    set_target_properties(merged PROPERTIES LINK_FLAGS -Wl,--version- 
    script=merged.version)
    

    merged.version

    merged {
      global: HelloWorld; GoodbyeWorld;
      local: *;
    };
    
  • Also tried force load static libaries without success
    set_target_properties(merged PROPERTIES LINK_FLAGS -Wl,-force_load,libunit1.a)
    
273K
  • 29,503
  • 10
  • 41
  • 64
  • What is about [--whole-achive](https://stackoverflow.com/questions/2649735/how-to-link-static-library-into-dynamic-library-in-gcc)? – Tsyvarev Apr 16 '20 at 22:33
  • Sorry, it was a typo. The option is `--whole-archive`. – Tsyvarev Apr 16 '20 at 23:12
  • 1
    @Tsyvarev Thanks. `-Wl,-whole-archive libunit1.a libunit2.a -Wl,--no-whole-archive` solves my issue. Now thinking how to do it in CMakeLists.txt, so `-Wl,--no-whole-archive` is appended to a linker command, not prefixed. – 273K Apr 16 '20 at 23:27
  • 1
    Just google for e.g. `cmake "--whole-archive"`. There are many questions on Stack Overflow on this topic. (But none of them contains ideal solution). – Tsyvarev Apr 17 '20 at 07:24

1 Answers1

9

You need to pass the --whole-archive option to the linker. In CMake, you can do it as follows.

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)

add_library(unit1 STATIC unit1.c)
target_compile_options(unit1 PRIVATE -fPIC)

add_library(unit2 STATIC unit2.c)
target_compile_options(unit2 PRIVATE -fPIC)

add_library(merged SHARED)
set_target_properties(merged PROPERTIES LINKER_LANGUAGE C)
target_link_libraries(merged
        "-Wl,--whole-archive libunit1.a libunit2.a -Wl,--no-whole-archive"
        unit1 unit2
)

Note: The target_link_libraries command can be used to specify linker flags as well, not only library names. The quotes are important, otherwise CMake might rearrange the flags and remove duplicates.

Exported symbols

$ nm libmerged.so | grep " T "
000000000000065d T GoodbyeWorld
000000000000064a T HelloWorld
0000000000000670 T _fini
0000000000000520 T _init

Another option, to avoid the problem, would be to create OBJECT instead of STATIC libraries for unit1 and unit2.

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)

add_library(unit1 OBJECT unit1.c)
target_compile_options(unit1 PRIVATE -fPIC)

add_library(unit2 OBJECT unit2.c)
target_compile_options(unit2 PRIVATE -fPIC)

add_library(merged SHARED $<TARGET_OBJECTS:unit1> $<TARGET_OBJECTS:unit2>)
set_target_properties(merged PROPERTIES LINKER_LANGUAGE C)
sergej
  • 17,147
  • 6
  • 52
  • 89