【问题标题】:Linking a dynamic library that links in symbols from a static library: macOS vs Linux链接从静态库中链接符号的动态库:macOS 与 Linux
【发布时间】:2020-01-10 19:29:55
【问题描述】:

我正在将 Linux 应用程序移植到 macOS,但链接行为存在差异,我花了一些时间才显露出来。该项目使用基于 CMake 的两阶段构建过程:一个 CMake 树创建一个动态库,该库链接到在稍后创建的第二个树中创建的静态库。创建动态库时,静态库还不存在。这适用于 Linux:动态库是使用静态库中的符号创建的,并且它们是前向声明的。构建第二棵树时,动态库链接到一个可执行文件,该可执行文件也链接静态库,这样一切正常。这在 macOS 上不起作用,因为在第一个 CMake 树中,编译器在动态库的链接步骤中失败,因为来自第二个树的静态库尚不存在。

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

设置如下:

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

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

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

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

ma​​cOS/clang 在动态库与 Linux/gcc 稍后会失败在程序链接的那一步

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

我想知道:

  1. 为什么会存在这种差异?
  2. 我能否通过调整编译器/链接器标志来获得 macOS 上的 Linux 行为?

示例已发布到 Github:Linking a dynamic library that links in symbols from a static library (macOS vs Linux)

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

CMakeLists.txt

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

#include "dynamic_lib.h"

#include "static_lib.h"

void dynamic_lib_func() {
  static_lib_func();
}

static_lib.c

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

ma​​cOS 输出

[ 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 输出

[ 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 上设置,在创建共享库时使用链接器选项-z defs。 IMO您的链接已损坏。理想情况下,共享库应该是自包含的,在运行时提供它需要的任何其他共享库。如果它需要来自其他静态库的东西,我认为您应该改为将该静态库转换为共享库。

标签: c linux macos linker-errors


【解决方案1】:

我已经设法找到解决我在将同一个项目从 Linux 移植到 macOS 时也遇到的相关问题的解决方案:How to share a global variable between a main process and a dynamic library via a static library (macOS)?

事实证明,这种库符号的“前向声明”在 macOS 上是可能的:

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

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

target_link_options(dynamic_lib PRIVATE -undefined dynamic_lookup)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多