为什么库链接的顺序有时会导致GCC错误?
153766 观看
11回复
回应 (11)
510像
(请参阅此答案的历史记录以获得更详细的文本,但我现在认为读者更容易看到真正的命令行)。
以下所有命令共享的公共文件
$ 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 -)
(你可能需要逃避parens,例如-\(
和-\)
)。然后,链接器会多次搜索这些封闭的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-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
以后依赖于另一个库,则需要重新链接二进制文件,如果这样做的话。如果其他人在运行时加载libb
使用dlopen
(想想动态加载插件),调用也会失败。所以"right"
真的应该是一个wrong
。
92像
GNU ld链接器是所谓的智能链接器。它将跟踪前面的静态库使用的函数,永久地抛弃那些未在其查找表中使用的函数。结果是,如果过早链接静态库,那么稍后链接行上的静态库将不再提供该库中的函数。
典型的UNIX链接器从左到右工作,因此将所有依赖库放在左侧,将满足这些依赖性的库放在链接行的右侧。您可能会发现某些库依赖于其他库,而同时其他库依赖于它们。这是它变得复杂的地方。谈到循环引用,修复代码!
作者: casualcoder 发布者: 03.01.2009 05:2149像
这是一个示例,用于说明在涉及静态库时GCC的工作原理。所以我们假设我们有以下场景:
myprog.o
- 包含main()
功能,依赖于libmysqlclient
libmysqlclient
- 静态,为了这个例子(你更喜欢共享库,当然,因为它libmysqlclient
是巨大的); in/usr/local/lib
; 并依赖于来自的东西libz
libz
(动态)
我们如何链接这个?(注意:使用gcc 4.3.4编译Cygwin的示例)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
作者: Lumi
发布者: 16.07.2011 12:32
24像
如果添加-Wl,--start-group
到链接器标志,它不关心它们在哪个顺序或者是否存在循环依赖关系。
在Qt上这意味着添加:
QMAKE_LFLAGS += -Wl,--start-group
节省了大量的时间,它似乎没有减慢连接速度(这比编译所花费的时间少得多)。
作者: SvaLopLop 发布者: 05.04.2015 12:248像
另一种方法是两次指定库列表:
gcc prog.o libA.a libB.a libA.a libB.a -o prog.x
这样做,您不必打扰正确的序列,因为参考将在第二个块中解析。
作者: eckes 发布者: 21.03.2014 10:075像
您可以使用-Xlinker选项。
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
几乎等于
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
小心!
- 组内的顺序很重要!下面是一个示例:调试库具有调试例程,但非调试库具有相同的弱版本。您必须将调试库FIRST置于组中,否则您将解析为非调试版本。
- 您需要在组列表中的每个库之前使用-Xlinker
4像
一个快速的提示让我感到高兴:如果您将链接器调用为“gcc”或“g ++”,那么使用“--start-group”和“--end-group”将不会将这些选项传递给链接器 - 也不会标记错误。如果您的库顺序错误,它将使未定义符号的链接失败。
您需要将它们写为“-Wl, - start-group”等,以告诉GCC将参数传递给链接器。
作者: M.M 发布者: 11.08.2013 02:073像
我已经看到了很多,我们的一些模块链接超过100个代码库以及系统和第三方库。
根据不同的链接器HP / Intel / GCC / SUN / SGI / IBM / etc,您可以获得未解析的函数/变量等,在某些平台上,您必须两次列出库。
在大多数情况下,我们使用库的结构化层次结构,核心,平台,不同的抽象层,但对于某些系统,您仍然必须使用link命令中的顺序。
一旦你找到解决方案文档,下一个开发人员就不必再次解决它了。
我的老讲师曾经说过,“ 高凝聚力和低耦合 ”,今天仍然如此。
作者: titanae 发布者: 05.09.2008 03:562像
链接顺序当然重要,至少在某些平台上。我看到与错误顺序的库链接的应用程序的崩溃(错误意味着A在B之前链接但B依赖于A)。
作者: David Cournapeau 发布者: 28.04.2009 01:570像
当我跑步时,我想让Spro进入alizébaut我有这个问题:
make 2:* spro.info错误127 makefile:639 repicepe for target“all-recursive”failed make 1:all-recursive error 1 makefile:413:recipe for all'all'failed make:* all error
作者: Hanane AMESSAFI 发布者: 12.03.2019 03:32-1像
来自类别的问题 :
- c++ 理解指针有什么障碍,克服它们可以做些什么?
- c++ 在C ++中解析INI文件的最简单方法是什么?
- c++ 什么时候应该在C ++中使用“朋友”?
- c++ 你如何清除stringstream变量?
- c++ 在C ++中,构造函数和析构函数可以是内联函数吗?
- c++ 在C ++中,什么是虚拟基类?
- gcc 为什么库链接的顺序有时会导致GCC错误?
- gcc 如何在GCC中摆脱从字符串常量到'char *'的过时转换警告?
- gcc 我的程序崩溃时如何自动生成堆栈跟踪
- gcc 如何模拟内存分配错误
- gcc 你如何在gcc中从C / C ++源获得汇编程序输出?
- gcc 使用CMake预编译的头文件
- linker 为什么在Visual Studio中编译C ++项目时会出现致命错误“LNK1104:无法打开文件'C:\ Program.obj'”?
- linker 链接器内存不足LNK1102
- linker 将DLL嵌入已编译的可执行文件中
- linker 如何与GCC进行弱连接工作?
- linker g ++ undefined对typeinfo的引用