背景
在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 <stdio.h>
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鏈接庫會在首先指定的庫中"貪婪匹配"所有未定義的函數、變量,因此,
即使兩個庫有相同的函數、變量的定義,只會使用最先找到的庫中的定義
