背景
在GCC中已经指定链接库,然而编译时却提示动态库函数未定义!
测试出现的错误提示如下:
[GMPY@13:48 tmp]$gcc -o test -L. -lmylib test.c
/tmp/ccysQZI3.o:在函数‘main’中:
test.c:(.text+0x1a):对‘func_lib’未定义的引用
collect2: error: ld returned 1 exit status
而在测试用的动态库libmylib.so
中是有定义函数func_lib
的
[GMPY@13:55 tmp]$cat mylib.c
#include
int func_lib(void)
{
printf("In share library\n");
return 0;
}
[GMPY@13:56 tmp]$gcc -fPIC -shared mylib.c -o libmylib.so
GCC的链接坑
此处的"坑"指对不熟悉GCC机制的童鞋而言,会出现无法理解的不符合预期的效果
在用gcc编译时,我们可以用-L
指定链接库位置,用-l
指定。
man gcc
查询时,我发现这么一段描述:
-llibrary
-l library
... ## 这里为了方便阅读,对原文进行了换行排版优化
It makes a difference where in the command you write this option;
the linker searches and processes libraries and object files in the order they are specified.
Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o.
If bar.o refers to functions in z, those functions may not be loaded.
...
嗯,这段话什么意思呢? 如果-l
链接库在源码之前,就会链接不到库!!
就像下面两个命令的差别:
异常:gcc -o test -L. -lmylib test.c
正常:gcc -o test -L. test.c -lmylib
竟然对执行时参数的位置都有要求,也是醉了
GCC的链接步骤
感谢 @firstrose 提供的原理说明链接
GCC是怎么样理解-l
的呢?
A library is a collection (an archive) of object files. When you add a library using the -l option,
the linker does not unconditionally take all object files from the library. It only takes those object
files that are currently needed, i.e. files that resolve some currently unresolved (pending) symbols.
After that, the linker completely forgets about that library.
The list of pending symbols is continuously maintained by the linker as the linker processes input
object files, one after another from left to right. As it processes each object file, some symbols get
resolved and removed from the list, other newly discovered unresolved symbols get added to the list.
So, if you included some library by using -l, the linker uses that library to resolve as many currently
pending symbols as it can, and then completely forgets about that library. If it later suddenly
discovers that it now needs some additional object file(s) from that library, the linker will not "return"
to that library to retrieve those additional object files. It is already too late.
什么个意思呢?就是说,GCC链接器按下面的步骤链接文件:
- 从左往右链接源文件
- 在链接时,如果出现源文件调用了却没有定义的函数、变量等,则记录下来
- 如果遇到-l指定的库,则在库中尽可能检索所有记录下来的没有定义的函数、变量,只从库中提取用到的部分,其他完全抛弃
- 在全部链接完后,如果依然存在未定义的函数、变量,则报错
正因为GCC链接器的"始乱终弃",在检索-l
的库后,就抛弃了这个库,后面还需要用时,自然就找不到了
GCC并不会回过头来检索之前链接过的库
从这样的链接步骤,我猜测还会有个特性:
由于GCC链接库会在首先指定的库中"贪婪匹配"所有未定义的函数、变量,因此,
即使两个库有相同的函数、变量的定义,只会使用最先找到的库中的定义