静态链接时静态库的位置问题
笔记简述
在使用静态链接时,静态库在命令中的位置不恰当,将会导致链接错误。此问题之前在看《专业嵌入式软件开发》时有看到过。李云大佬只是在书中给出了一个他的经验例子,但是其根本原因当时并没有真正明白。今天在《CSAPP》的链接这一章节看到了对此问题的详细描述,所以记录下来。
正文
以《专业嵌入式软件开发》中所给的例子引出问题(P105图4.21)
// foo.c
#include <stdio.h>
void foo()
{
printf("this is foo ()!\n");
}
// main.c
extern void foo();
int main()
{
foo();
return 0;
}
然后是编译,生成静态库以及链接的步骤:
# 生成可重定位目标文件
gcc -c foo.c -o foo.o
gcc -c main.c -o main.o
# 生成库文件
ar crs libfoo.a foo.o
# 链接
gcc -o a.out -L. -lfoo main.o
结果在链接的时候出错:
main.o: In function `main': main.c:(.text+0xa): undefined reference to `foo' collect2: error: ld returned 1 exit status
先给出解决办法以及结论:
关于库的一般准则是将他们放在命令行的结尾。如果各个库的成员是相互独立的,那么这些库就可以以任何顺序放置在命令行的结尾。另一方面,如果库不是相互独立的,那么必须对他们排序。
接下来给出对于这个问题的解释:
首先可以使用nm(符号显示器)来分别打印foo.o,main.o这连个模块中的符号:
ubuntu@ubuntu-lts:~/workspace/ld$ nm main.o
U _GLOBAL_OFFSET_TABLE_
U foo
0000000000000000 T main
ubuntu@ubuntu-lts:~/workspace/ld$ nm foo.o
0000000000000000 T foo
可以看到在main.o模块中,foo这个符号是未定义的,在foo.o模块中,foo这个符号是定义了的且放在.text段中。
为什么当链接器的输入文件是库文件时,只会根据未解析符号集合U中的符号来进行匹配?
这个问题在《CSAPP》的链接这一章也有提到过,对于一个库文件来说,一般来说是很大的,例如libc.a文件:
ubuntu@ubuntu-lts:~/workspace/ld$ ls -l /usr/lib/x86_64-linux-gnu/libc.a
-rw-r--r-- 1 root root 5477660 Apr 17 2018 /usr/lib/x86_64-linux-gnu/libc.a
可以看到其大小有5M左右,如果不根据“未解析符号集合U”来处理库文件中的符号,那么就不得不将库文件中的所有符号拷贝出来,但是库文件中的其他的很多的内容可能我们并不需要用到。