gcc - 为什么链接库的顺序有时会导致 GCC 出错?

标签 gcc linker

为什么链接库的顺序有时会导致 GCC 出错?

最佳答案

(查看此答案的历史记录以获得更详尽的文本,但我现在认为读者更容易看到真正的命令行)。


以下所有命令共享的公共(public)文件

// a depends on b, b depends on d
$ cat a.cpp
extern int a;
int main() {
  return a;
}

$ cat b.cpp
extern int b;
int a = b;

$ cat d.cpp
int b;

链接到静态库

$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o

$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order

链接器从左到右搜索,并记录未解析的符号。如果库解析了符号,它会使用该库的目标文件来解析符号(在本例中是从 libb.a 中提取出来的)。

静态库之间的依赖关系是相同的 - 需要符号的库必须在第一位,然后是解析符号的库。

如果一个静态库依赖于另一个库,而另一个库又依赖于前一个库,则存在循环。您可以通过 -(-) 包含循环依赖库来解决此问题,例如 -( -la -lb -) (您可能需要转义括号,例如 -\(-\))。然后链接器多次搜索那些包含的库以确保解决循环依赖性。或者,您可以多次指定库,这样每个库都在另一个之前:-la -lb -la

链接到动态库

$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!

$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order

这里也是一样——库必须跟在程序的目标文件之后。与静态库相比,此处的区别在于您无需关心库之间的依赖关系,因为动态库会自行解决它们的依赖关系

一些最近的发行版显然默认使用 --as-needed 链接器标志,它强制程序的目标文件出现在动态库之前。如果传递了该标志,链接器将不会链接到可执行文件实际上不需要的库(并且它会从左到右检测到这一点)。我最近的 archlinux 发行版默认不使用这个标志,所以它没有给出不遵循正确顺序的错误。

在创建 b.so 时忽略 d.so 的依赖是不正确的。然后在链接 a 时需要指定库,但是 a 本身并不真正需要整数 b,所以它不应该需要关心 b 自己的依赖关系。

这是一个示例,说明如果您没有为 libb.so

指定依赖项,将会产生的影响
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)

$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"

如果您现在查看二进制文件有哪些依赖项,您会注意到二进制文件本身也依赖于 libd,而不仅仅是它应该依赖的 libb。如果以后 libb 依赖于另一个库,则二进制文件将需要重新链接,如果您这样做的话。如果其他人在运行时使用 dlopen 加载 libb(考虑动态加载插件),调用也会失败。所以 “正确的” 实际上也应该是 错误的

关于gcc - 为什么链接库的顺序有时会导致 GCC 出错?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56074680/

相关文章:

c++ - 在 solaris 上链接 zeromq 静态库

c++ - 我可以在 C++ 项目中混合使用 c 和 cpp 文件吗?

c - 如何使用 GDB 从多个文件中查找段错误

c++ - PATH 中未找到 "g++"[已安装 mac/Eclipse/XCODE/已下载 CDT]

c - 错误: array initializer must be an initializer list or wide string literal in C Merge Sort program

assembly - Grub 错误 28 : selected item cannot fit into memory when writing higher half kernel

iOS 库/框架,如何从构建产品中排除特定类?

c - 将变量类型与 token 合并

c - 将动态大小的可变长度数组 (VLA) 初始化为 0

c++ - 将预编译的tensorflow与cmake一起使用