简体   繁体   English

链接从静态库中链接符号的动态库:macOS 与 Linux

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

I am porting a Linux application to macOS and there is a difference in the linking behavior that took some of my time to reveal itself.我正在将 Linux 应用程序移植到 macOS,链接行为有所不同,我花了一些时间才发现它自己。 The project uses a two-stage CMake-based build process: one CMake tree creates a dynamic library that links to static library that is created in the second tree that is created later.该项目使用基于 CMake 的两阶段构建过程:一个 CMake 树创建一个动态库,该动态库链接到在稍后创建的第二个树中创建的静态库。 The static library does not exist yet when the dynamic library is created.创建动态库时,静态库还不存在。 This works on Linux: dynamic library gets created with symbols from static library and they are forward-declared.这适用于 Linux:动态库是使用静态库中的符号创建的,并且它们是前向声明的。 When the second tree is built, the dynamic library gets linked to an executable which also links the static library and this way everything works fine.当构建第二棵树时,动态库链接到一个可执行文件,该可执行文件也链接静态库,这样一切正常。 This does not work on macOS because in the first CMake tree, the compiler fails at the dynamic library's linking step because the static library from the second tree does not exist yet.这在 macOS 上不起作用,因为在第一个 CMake 树中,编译器在动态库的链接步骤失败,因为第二个树中的静态库尚不存在。

I have reduced my application to a minimal example (the code can be found at the end of my question).我已将我的应用程序简化为一个最小示例(代码可以在我的问题末尾找到)。

The setup is as follows:设置如下:

  • Minimal C program with a main() function带有 main() 函数的最小 C 程序
  • Dynamic library with one function一个功能的动态库
  • Static library with one function一个功能的静态库
  • The program calls a function from the dynamic library.程序调用动态库中的函数。 The dynamic library is of course linked dynamically to the program.动态库当然是动态链接到程序的。
  • The dynamic library calls a function from the static library.动态库调用静态库中的函数。 The static library is linked statically to the dynamic library.静态库静态链接到动态库。

If we stop linking the static library to dynamic library:如果我们停止将静态库链接到动态库:

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

we, of course, get errors when building a program.当然,我们在构建程序时会出错。 But the errors are different on macOS and Linux:但是 macOS 和 Linux 上的错误是不同的:

macOS/clang fails earlier at the step when the dynamic library gets linked vs Linux/gcc fails later at the step when the program gets linked macOS/clang在链接动态库的步骤较早失败,而Linux/gcc在链接程序的步骤较晚失败

I do recognize that the difference can be in the fact that I am using clang on macOS and gcc on Linux but this does not explain the issue to me.我确实认识到不同之处可能在于我在 macOS 上使用 clang 而在 Linux 上使用 gcc 但这并不能向我解释这个问题。

I would like to know:我想知道:

  1. Why does this difference exist?为什么会存在这种差异?
  2. Can I get the Linux behavior on macOS by tweaking the compiler/linker flags?我可以通过调整编译器/链接器标志来获得 macOS 上的 Linux 行为吗?

The example has been published to Github: Linking a dynamic library that links in symbols from a static library (macOS vs Linux) .该示例已发布到 Github: 链接动态库,该动态库链接静态库中的符号(macOS 与 Linux)

These are the key files from my example on Github:这些是我在 Github 上的示例中的关键文件:

CMakeLists.txt 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() {}

macOS output macOS 输出

[ 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 output 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

I have managed to find a solution to the related issue that I also encountered while porting the same project from Linux to macOS: How to share a global variable between a main process and a dynamic library via a static library (macOS)?我设法找到了解决将同一项目从 Linux 移植到 macOS 时也遇到的相关问题的解决方案: 如何通过静态库 (macOS) 在主进程和动态库之间共享全局变量? . .

It turns out that this kind of "forward declaration" of the library symbols is possible on macOS:事实证明,这种库符号的“前向声明”在 macOS 上是可能的:

Adding the -undefined dynamic_lookup flag makes macOS to pass the original error.添加-undefined dynamic_lookup标志使 macOS 传递原始错误。

Adding this to my example's CMakeLists.txt file solves the issue:将此添加到我的示例的 CMakeLists.txt 文件中解决了这个问题:

target_link_options(dynamic_lib PRIVATE -undefined dynamic_lookup)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM