1. undefined reference to `xxx_function’”的错误时
既然编译是说没有定义某个函数,所以我们先看看这个函数是哪一个库实现的。直接搜索编译环境的include目录,看看 xxx_function 这个函数是定义到哪一个头文件,再看看这个函数是哪个源文件实现并编译为库。假设 xxx_function 函数由 xxx.c 实现并最终编译输出为 xxx.so,接着使用readelf -d xxx.so,查看该命令输出的“Library soname:”信息,比如输出了“libxxx.so”,我们再在Makefile的LDFLAGS增加 -lxxx即可,这样编译的时候链接工具将会链接libxxx.so并查找得到xxx_function函数的符号链接成功。
有些时候是没有链接系统的某些库导致的,有时候又会是没有链接第三方库导致的,具体根据问题现象相应查找实现该函数的库,再相应的链接该库即可。
为什么会出现这样的错误呢?
我们在机器中运行一个程序,程序都是由源代码经过预编译、编译、汇编、链接四个阶段组成。
预编译
假设我们的是 .c 源文件和相关头文件,将会被与编译器 gcc 预编译为一个 .i 文件,预编译使用 -E 参数即可。预编译可以简单理解就是处理源码中已“#”开始的预编译指令,比如“#include”、“#define”等。当我们无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定问题。
编译
编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。使用 - S 参数将输出为汇编文件。
汇编
汇编器就是将汇编代码转换为机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。使用 -c 参数即可,经过预编译、编译和汇编将会输出目标文件。
链接
我们看汇编代码中,经常会看到 jmp aaa_function,我们知道这个是一个跳转指令,将会跳转到 aaa_function 函数执行,aaa_function 此时是一个符号,我们在编译的时候,最终就会将可执行程序中使用的各个符号链接起来,知道运行的时候到哪里查找得到该符号,知道跳转的地址是多少。链接的过程主要包括了地址和空间分配、符号决议和重定位等这些步骤。
2. undefined reference to `xxx_function’”的错误时
遇到“undefined reference to `xxx_function’”的错误时,通常意味着链接器在链接过程中找不到某个函数的定义。以下是一些解决这类问题的步骤:
-
确定函数所属的库:
- 首先,确定
xxx_function
函数属于哪个库。可以通过搜索头文件来确定函数声明的位置,然后查找该函数是由哪个源文件实现的,以及最终编译成哪个库文件(如libxxx.so
或libxxx.a
)。
- 首先,确定
-
检查库文件是否正确链接:
- 确认在编译命令或
Makefile
中是否已经包含了正确的库链接指令。如果是动态库,通常使用-lxxx
来链接(例如-lyaml-cpp
)。如果是静态库,确保.a
文件的路径被包含在编译器的搜索路径中,并且使用了-l
选项。
- 确认在编译命令或
-
检查库文件的路径:
- 如果库文件不在标准路径下,需要确保编译器能够找到它。可以通过设置
-L
参数指定库文件的搜索路径。
- 如果库文件不在标准路径下,需要确保编译器能够找到它。可以通过设置
-
检查头文件的路径:
- 确保编译器能够找到库的头文件。可以通过设置
-I
参数指定头文件的搜索路径。
- 确保编译器能够找到库的头文件。可以通过设置
-
检查库的版本:
- 如果库有多个版本,确保链接的是正确的版本。有时候,库的新旧版本之间可能不兼容。
-
检查依赖关系:
- 有些库之间存在依赖关系,确保所有依赖库都已经被正确链接。
-
使用
readelf
或nm
工具:- 使用
readelf -d libxxx.so
或nm libxxx.so
来查看库文件中包含的符号,确认xxx_function
是否在其中。
- 使用
-
清理并重新编译:
- 有时候,旧的编译产物可能会导致链接错误。尝试清理(如使用
make clean
)项目并重新编译。
- 有时候,旧的编译产物可能会导致链接错误。尝试清理(如使用
-
检查编译器和链接器的错误信息:
- 仔细阅读编译器和链接器的错误信息,它们通常会提供关于缺失符号的详细信息。
-
查看文档和社区:
- 如果上述步骤都无法解决问题,查看库的文档或者搜索社区论坛,看看是否有其他人遇到并解决了类似的问题。
《程序员的自我修养------链接、装载与库》是一本很好的书,它详细介绍了程序从源代码到可执行文件的整个编译链接过程,对于理解链接错误和解决这类问题非常有帮助。