关于C#:链接动态库以链接静态库中的符号:macOS与Linux

 2021-04-27 

Linking a dynamic library that links in symbols from a static library: macOS vs Linux

我正在将Linux应用程序移植到macOS,并且链接行为有所不同,这花费了我一些时间来展示自己。该项目使用基于CMake的两阶段构建过程:一棵CMake树创建一个动态库,该库链接到在第二个树中创建的静态库,该静态树随后创建。创建动态库时,静态库尚不存在。这在Linux上有效:动态库是使用静态库中的符号创建的,并且它们已被预先声明。构建第二棵树时,动态库将链接到可执行文件,该可执行文件也链接静态库,因此一切正常。这在macOS上不起作用,因为在第一棵CMake树中,编译器在动态库的链接步骤失败,因为第二棵树中的静态库尚不存在。

我已将我的应用程序简化为一个最小的示例(代码可以在问题末尾找到)。

设置如下:

  • 带有main()函数的最小C程序
  • 具有一个功能的动态库
  • 具有一个功能的静态库
  • 该程序从动态库中调用一个函数。动态库当然是动态链接到程序的。
  • 动态库从静态库调用一个函数。静态库静态链接到动态库。

如果我们停止将静态库链接到动态库:

1
2
# This target_link_libraries is intentionally commented out.
#target_link_libraries(dynamic_lib static_lib)

我们在构建程序时当然会出错。但是错误在macOS和Linux上有所不同:

在动态库被链接与
程序被链接后,Linux / gcc稍后在该步骤失败

我确实认识到差异可能是因为我在macOS上使用clang而在Linux上使用gcc,但这并不能向我解释这个问题。

我想知道:

  • 为什么存在这种差异?
  • 通过调整编译器/链接器标志,可以在macOS上获得Linux行为吗?
  • 该示例已发布到Github:链接动态库,该动态库链接静态库中的符号(macOS与Linux)。

    这些是我在Github上的示例中的关键文件:

    CMakeLists.txt

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    cmake_minimum_required(VERSION 3.14)
    project(untitled1 C)

    add_compile_options("-fPIC")

    set(CMAKE_C_STANDARD 99)

    add_library(static_lib static_lib.c)
    add_library(dynamic_lib SHARED dynamic_lib.c)

    # THE ISSUE IS HERE:
    # This target_link_libraries is intentionally commented out.
    # on macOS the build process fails when linking dynamic_lib
    # on Linux the build process fails when linking the
    # 'untitled1' program.
    #target_link_libraries(dynamic_lib static_lib)

    add_executable(untitled1 main.c)
    target_link_libraries(untitled1 dynamic_lib)

    dynamic_lib.c

    1
    2
    3
    4
    5
    6
    7
    #include"dynamic_lib.h"

    #include"static_lib.h"

    void dynamic_lib_func() {
      static_lib_func();
    }

    static_lib.c

    1
    2
    #include"static_lib.h"
    void static_lib_func() {}

    macOS输出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [ 25%] Building C object CMakeFiles/dynamic_lib.dir/dynamic_lib.c.o
    /Library/Developer/CommandLineTools/usr/bin/cc -Ddynamic_lib_EXPORTS  -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -fPIC   -fPIC -std=gnu99 -o CMakeFiles/dynamic_lib.dir/dynamic_lib.c.o   -c /Users/stanislaw/workspace/code/Examples/untitled1/dynamic_lib.c
    [ 50%] Linking C shared library libdynamic_lib.dylib
    /Applications/CLion.app/Contents/bin/cmake/mac/bin/cmake -E cmake_link_script CMakeFiles/dynamic_lib.dir/link.txt --verbose=1
    /Library/Developer/CommandLineTools/usr/bin/cc -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -dynamiclib -Wl,-headerpad_max_install_names  -o libdynamic_lib.dylib -install_name @rpath/libdynamic_lib.dylib CMakeFiles/dynamic_lib.dir/dynamic_lib.c.o
    Undefined symbols for architecture x86_64:
     "_static_lib_func", referenced from:
          _dynamic_lib_func in dynamic_lib.c.o
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

    Linux输出

    1
    2
    3
    4
    5
    6
    [ 16%] Linking C shared library libdynamic_lib.so
    [ 33%] Built target dynamic_lib
    Scanning dependencies of target untitled1
    [ 50%] Linking C executable untitled1
    libdynamic_lib.so: undefined reference to `static_lib_func'
    collect2: error: ld returned 1 exit status


    在设法将同一项目从Linux移植到macOS时,我也设法找到了相关问题的解决方案:如何通过静态库(macOS)在主进程和动态库之间共享全局变量? 。

    事实证明,在macOS上可以使用这种库符号的"转发声明":

    添加-undefined dynamic_lookup标志使macOS传递原始错误。

    将此添加到我示例的CMakeLists.txt文件中即可解决此问题:

    1
    target_link_options(dynamic_lib PRIVATE -undefined dynamic_lookup)