您的链接在引用它们的目标文件之前使用库
- 您正在尝试使用 GCC 工具链编译和链接您的程序。
- 您的链接指定了所有必要的库和库搜索路径
- 如果
libfoo 依赖于 libbar,那么您的链接正确地将 libfoo 放在 libbar 之前。
- 你的链接失败
undefined reference to某物错误。
- 但是所有未定义的某物s 在你的头文件中声明
#included 实际上是在您链接的库中定义的。
示例在 C 中。它们同样可以是 C++
涉及您自己构建的静态库的最小示例
my_lib.c
#include "my_lib.h"
#include <stdio.h>
void hw(void)
{
puts("Hello World");
}
我的库.h
#ifndef MY_LIB_H
#define MT_LIB_H
extern void hw(void);
#endif
eg1.c
#include <my_lib.h>
int main()
{
hw();
return 0;
}
您构建静态库:
$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o
你编译你的程序:
$ gcc -I. -c -o eg1.o eg1.c
您尝试将其与 libmy_lib.a 链接并失败:
$ gcc -o eg1 -L. -lmy_lib eg1.o
eg1.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status
如果一步编译和链接,结果相同,例如:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
/tmp/ccQk1tvs.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status
涉及共享系统库的最小示例,压缩库libz
eg2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%s
",zlibVersion());
return 0;
}
编译你的程序:
$ gcc -c -o eg2.o eg2.c
尝试将您的程序与 libz 链接并失败:
$ gcc -o eg2 -lz eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status
如果您一次性编译和链接,则相同:
$ gcc -o eg2 -I. -lz eg2.c
/tmp/ccxCiGn7.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status
示例 2 的变体涉及 pkg-config:
$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
你做错了什么?
在你想要链接的目标文件和库的序列中,使你的
程序,您将库放在引用的目标文件之前
他们。您需要放置库后引用的对象文件
给他们。
正确链接示例1:
$ gcc -o eg1 eg1.o -L. -lmy_lib
成功:
$ ./eg1
Hello World
正确链接示例2:
$ gcc -o eg2 eg2.o -lz
成功:
$ ./eg2
1.2.8
正确链接示例 2 pkg-config 变体:
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
说明
从这里开始阅读是可选的.
默认情况下,GCC 在您的发行版上生成的链接命令,
从左到右使用链接中的文件
命令行序列。当它发现一个文件引用某物并且不包含它的定义,将搜索定义
在右边的文件中。如果它最终找到一个定义,则
参考解决。如果最后有任何参考文献仍未解决,
链接失败:链接器不向后搜索。
第一的,示例 1, 静态库my_lib.a
静态库是目标文件的索引存档。当链接器
在链接序列中找到-lmy_lib并计算出这是指
到静态库./libmy_lib.a,它想知道你的程序是否
需要libmy_lib.a 中的任何目标文件。
libmy_lib.a里面只有一个object文件,即my_lib.o,而且只定义了一个东西
在my_lib.o中,即函数hw。
链接器将决定您的程序需要 my_lib.o 当且仅当它已经知道
您的程序在一个或多个目标文件中引用了 hw
添加到程序中,并且它没有添加任何目标文件
包含 hw 的定义。
如果是,那么链接器将从库中提取 my_lib.o 的副本并
将它添加到您的程序中。然后,您的程序包含 hw 的定义,因此
它对hw的引用是解决.
当您尝试链接程序时:
$ gcc -o eg1 -L. -lmy_lib eg1.o
链接器没有添加eg1.o到节目当它看到
-lmy_lib。因为在那一点上,它还没有看到eg1.o。
你的程序还没有对hw做任何引用:它
还没有做任何参考根本,因为它所做的所有引用
在eg1.o。
所以链接器不会将my_lib.o添加到程序中,也没有进一步的
用于libmy_lib.a。
接下来,它找到eg1.o,并将其添加到程序中。目标文件中的
链接序列总是添加到程序中。现在,程序使
对hw的引用,不包含hw的定义;但
连锁序列中没有任何东西可以提供缺失的
定义。对 hw 的引用结束悬而未决, 链接失败。
第二,示例 2,共享库libz
共享库不是目标文件或类似文件的存档。它是
更像是一个程序没有 main 功能和
而是公开它定义的多个其他符号,以便其他
程序可以在运行时使用它们。
今天许多 Linux 发行版都配置了他们的 GCC 工具链,以便其语言驱动程序(gcc、g++、gfortran 等)
指示系统链接器 (ld) 链接共享库如所须基础。
你有这些发行版之一。
这意味着当链接器在链接序列中找到-lz,并发现这是指
到共享库(比如)/usr/lib/x86_64-linux-gnu/libz.so,它想知道它添加到您的程序中但尚未定义的任何引用是否具有由libz 导出的定义
如果这是真的,那么链接器将不是从libz 中复制任何块并
将它们添加到您的程序中;相反,它只会篡改你程序的代码
以便:-
您的程序只想引用一个由 libz 导出的定义的事物,
即函数zlibVersion,在eg2.c 中仅被引用一次。
如果链接器将该引用添加到您的程序,然后找到定义
由libz导出,引用为解决
但是当你尝试像这样链接程序时:
gcc -o eg2 -lz eg2.o
事件顺序错误,与示例 1 相同。
当链接器找到-lz时,有不对任何事物的引用
节目中:都在eg2.o,目前还没有看到。所以
链接器决定它对 libz 没有用处。当它到达eg2.o时,将其添加到程序中,
然后对zlibVersion有未定义的引用,链接序列完成;
该引用未解析,链接失败。
最后,示例 2 的 pkg-config 变体现在有了明显的解释。
外壳扩展后:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
变成:
gcc -o eg2 -lz eg2.o
这又是示例 2。
我可以重现示例 1 中的问题,但无法在示例 2 中重现
联动:
gcc -o eg2 -lz eg2.o
适合你!
(或者:这种联系在 Fedora 23 上对你来说工作得很好,但在 Ubuntu 16.04 上失败了)
那是因为链接工作的发行版是其中之一
没有配置其 GCC 工具链来链接共享库如所须.
过去,类 unix 系统链接静态和共享是很正常的
不同规则的图书馆。链接序列中的静态库被链接
在如所须示例 1 中解释的基础,但共享库是无条件链接的。
这种行为在链接时是经济的,因为链接器不必考虑
程序是否需要共享库:如果是共享库,
链接它。大多数链接中的大多数库都是共享库。但也有缺点:-
这种权衡导致了今天的分裂局面。一些发行版有
更改了共享库的 GCC 链接规则,以便如所须原则适用于所有图书馆。一些发行版坚持使用旧版本
方法。
为什么同时编译链接还是出现这个问题?
如果我只是这样做:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
肯定 gcc 必须先编译 eg1.c,然后链接结果
带有libmy_lib.a的目标文件。那么它怎么可能不知道那个目标文件
在进行链接时需要吗?
因为用单个命令编译和链接不会改变
连锁序列的顺序。
当你运行上面的命令时,gcc 发现你想要编译 +
连锁。所以在幕后,它生成一个编译命令,然后运行
它,然后生成一个链接命令,并运行它,就好像你已经运行
两个命令:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
所以链接失败就像你做运行这两个命令。这
您在失败中注意到的唯一区别是 gcc 生成了一个
编译+链接情况下的临时目标文件,因为你没有告诉它
使用eg1.o。我们看:
/tmp/ccQk1tvs.o: In function `main'
代替:
eg1.o: In function `main':
也可以看看
The order in which interdependent linked libraries are specified is wrong
以错误的顺序放置相互依赖的库只是一种方法
您可以在其中获取文件需要事物即将到来的定义
在链接中晚于文件提供定义。将库放在
引用它们的目标文件是犯同样错误的另一种方式。