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

时间:2020-03-05 18:48:00  来源:igfitidea点击:

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

解决方案

回答

我想这是因为其中一些库与其他库具有依赖关系,如果尚未链接它们,则会出现链接器错误。

回答

我已经看到了很多,我们的某些模块链接了超过100个我们的代码库以及系统库和第三方库。

取决于不同的链接器HP / Intel / GCC / SUN / SGI / IBM / etc,我们可以获得未解析的函数/变量等,在某些平台上,我们必须列出库两次。

在大多数情况下,我们使用库,核心,平台,不同抽象层的结构化层次结构,但是对于某些系统,我们仍然必须按照link命令的顺序进行操作。

一旦找到了解决方案文档,那么下一个开发人员就不必再次解决它。

我的老讲师曾经说过"高凝聚力和低耦合",直到今天仍然如此。

回答

GNU ld链接器是所谓的智能链接器。它将跟踪先前的静态库使用的功能,并永久性地将其查找表中未使用的那些功能扔掉。结果是,如果我们过早地链接静态库,则该静态库中的函数以后在链接行上将不再对静态库可用。

典型的UNIX链接器从左到右起作用,因此将所有依赖库放在左边,而将满足这些依赖关系的库放在链接行的右边。我们可能会发现某些库依赖于其他库,而同时其他库也依赖于它们。这是复杂的地方。当涉及循环引用时,请修复代码!

回答

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

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

$ 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-)(我们可能需要转义括号,例如-\(-\))。链接器然后多次搜索那些包含的lib,以确保解决循环依赖关系。另外,我们可以指定多个库,所以每个库都在彼此之前:--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-need"链接器标志,该标志强制程序的目标文件位于动态库之前。如果传递了该标志,则链接器将不会链接到可执行文件实际上并不需要的库(并且从左到右检测到它)。我最近的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(考虑动态加载插件),则调用也会失败。因此,"正确"实际上也应该是"错误"。

回答

至少在某些平台上,链接顺序确实很重要。我已经看到以错误的顺序与库链接的应用程序崩溃(错误的意思是A在B之前链接,但B取决于A)。