项目中的LVS用到keepalived和ipvsadm等三方件,在suse11和suse12上编译最新版本的过程中遇到的最多的错误便是 undefined reference to xxx。由于对背后的原理基本没啥理解,所以遇到问题的解决办法就是把错误信息拿去google,baidu搜。当遇到的问题越来越偏僻时,这种做法不仅学不到多少东西,也无法快速的解决问题。所以下定决心从头学起,刚好对此也非常感兴趣,学习这些知识对学习操作系统一定非常有帮助。
动手实践之前,先来一点理论知识的指导:
gcc程序的编译过程和链接原理
为了加深印象与理解,按自己的理解总结一下,gcc的编译选项:
- -o 指定输出文件名为file,这个名称不能跟源文件名同名。不指定时,-E会输出到标准输出(一般为屏幕)。-S输出到file.s。-c 输出到file.o。不使用以上选项时,输出到a.out。
- -E Preprocess only; do not compile, assemble or link;只预处理
- -S Compile only; do not assemble or link;只编译(得到的是汇编代码,mov,push这种
- -c Compile and assemble, but do not link; 编译和汇编。得到的是二进制。我理解此时的二进制已经和操作系统的具体指令相关了。
- 不指定参数,默认进行编译,汇编,链接。得到是可执行二进制。
了解了原理,接下来进行实践加深理解。
参考:
Linux makefile – undefined reference to 问题解决方法
测试代码如下:
copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
int main()
{
test();
}
copbint@debian2:~/workspace/test1$ cat test.c
#include <stdio.h>
void test()
{
printf("I am test!\n");
}
直接进行编译:
copbint@debian2:~/workspace/test1$ gcc -o main main.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
test();
^~~~
/tmp/ccXDYxeL.o: In function `main':
main.c:(.text+0x1c): undefined reference to `test'
collect2: error: ld returned 1 exit status
加上test.c便正确:
copbint@debian2:~/workspace/test1$ gcc -o main main.c test.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
test();
^~~~
虽然能够链接成功,但是还是会有警告。经过测试,原来是在编译的过程中,发出了警告。
copbint@debian2:~/workspace/test1$ gcc -S main.c test.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
test();
^~~~
由此可以理解,在编译的过程中,如果找不到函数的实现,只会抛出implicit declaration of的警告。如果在链接的过程中,找不到函数的实现,则会导致错误。
在main.c中加入test()的声明,再实验如下:
copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
void test();
int main()
{
test();
}
copbint@debian2:~/workspace/test1$ cat test.c
#include <stdio.h>
void test()
{
printf("I am test!\n");
}
copbint@debian2:~/workspace/test1$ gcc -c main.c
copbint@debian2:~/workspace/test1$ gcc -o main main.c
/tmp/ccISqKfs.o: In function `main':
main.c:(.text+0x1c): undefined reference to `test'
collect2: error: ld returned 1 exit status
由此可以得出结论:
undefined reference to xxx是由于gcc在链接的过程中找不到函数的实现而导致的错误。如果是找不到函数的声明,会在编译的过程出抛出警告。
那么,如果在代码中使用未定义的变量,在编译(不链接)的过程中是否像未定义的函数一样,仅仅是抛出警告而不会导致错误呢?
copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
int main()
{
test_h_var = 3;
}
copbint@debian2:~/workspace/test1$ gcc -c main.c
main.c: In function ‘main’:
main.c:5:2: error: ‘test_h_var’ undeclared (first use in this function)
test_h_var = 3;
^~~~~~~~~~
main.c:5:2: note: each undeclared identifier is reported only once for each function it appears in
在编译的过程中,与使用未定义的函数不同,使用未定义的变量会直接导致错误。
在稍大一点的软件项目中,如果用到一个函数,就手动的去声明一次,会有工作量大且难以维护的问题。头文件就是为了解决这个问题,如为test.c添加一个test.h。然后在main.c中引用。
copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
#include "test.h"
int main()
{
test();
}
copbint@debian2:~/workspace/test1$ cat test.h
void test();
使用<>括起来的头文件,gcc会去系统路径下查找(具体路径还不了解),而”“括起来的头文件,gcc会尝试在当前目录搜索。
头文件除了声明函数,还有定义变量,宏,结构体等的作用。所以,在实际应用过程中,头文件必不可少。
在suse11上编译keepalived1.3.5的过程中,遇到大量 undefined reference to xxx错误,但是却并未看到implicit declaration of的告警信息。说明是在链接的过程中未找到对应函数的实现体。那么什么原因会导致这种情况呢?
未完待续……