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

标签 gcc linker

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

最佳答案

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

<小时/>

以下所有命令共享的通用文件

// 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

链接器从左到右搜索,并在搜索过程中记录未解析的符号。如果某个库解析了该符号,则它会使用该库的目标文件来解析该符号(在本例中,b.o 位于 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.sod.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/18699711/

相关文章:

c - "Undefined reference to mknod "如何解决?

c++ - 如何通过 gcc/g++ 或 clang 构建和使用 googletest (gtest) 和 googlemock (gmock)?

c++ - 链接多个使用 boost 的项目时出现 LNK2005 错误

linux - 无法在 Linux 上安装 perl 模块

c++ - 如何处理 C++/GNU 工具链中的链接器错误?

c++ - 包含 header 如何导致链接器错误?

xcode - 由于架构不匹配,将静态库从 Xcode 链接到 Lazarus 应用程序失败

c - SDL_TTF : undefined reference to "TTF_INIT"

c++ - 动态依赖 lib/tls 搜索路径

c++ - 构建共享库时 -fPIC 是什么意思?