以下結論及分析僅針對動態庫。
結論:
(1)-rpath和-rpath-link都可以在鏈接時指定庫的路徑;
(2)運行可執行文件時,-rpath-link指定的路徑不再有效(鏈接器沒有將庫的路徑包含進可執行文件中),
而-rpath指定的路徑還有效(因為鏈接器已經將庫的路徑包含在可執行文件中);
(3)-L指定的是鏈接時的庫路徑,生成的可執行文件在運行時庫的路徑仍由LD_LIBRARY_PATH環境變量指定;
(4)不管采用何種選項鏈接,當提示找不到動態庫時均可通過設置LD_LIBRARY_PATH解決。
【轉http://blog.csdn.net/q1302182594/article/details/42102961】
關於這3個參數的說明,有不少資料,但是看完了還是覺得模糊,分不清它們的區別。本文將用實驗的方法去探討這3個參數的區別。
1、三個.c文件
1.1 world.c
- #include<stdio.h>
- void world(void) {
- printf("world.\n");
- }
1.2 hello.c
- #include <stdio.h>
- void world(void);
- void hello(void) {
- printf("hello\n");
- world();
- }
1.3 test.c
- void main(void) {
- hello();
- }
2、生成動態庫
參照《Linux靜態庫與動態庫制作》,將hello.c和world.c分別生成動態庫
- ubuntu $ gcc -c hello.c world.c
- ubuntu $ gcc -shared -o libhello.so hello.o
- ubuntu $ gcc -shared -o libworld.so world.o
這時,生成的文件及其依賴如下圖:
由上圖可見,libhello.so和libworld都依賴於Linux-gate.so.1、libc.so.6以及/lib/ld-linux.so.2,並且這3個庫的路徑都以及硬編碼進libhello.so和libworld.so中了(=>右邊的部分)。
然而,雖然libhello.so中調用了libworld.so的函數,但是在上圖中並沒有顯示出此關系。為了達到使libhello.so依賴於libworld.so的目的,在生成libhello.so時要鏈接到libworld.so:
- ubuntu $ gcc -shared -o libworld.so world.o -lhello -L .
此時,再使用ldd查看libhello.so的依賴:
由上圖可見,此時libhello.so已經依賴於libworld.so。
3、編譯test.c
3.1 -L
由於test.c直接依賴於libhello.so,因此使用-lhello -L
- ubuntu $ gcc test.c -lhello -L .
結果如下圖:
由上圖可見已經在-L指定的路徑找打了libhello.so,只是libhello.so還需要libworld.so。雖然它都在同一目錄下,但是還是沒有辦法自動找到libworld.so。
那么,能不能使用-lworld將libworld.so也一並鏈接到test.c中呢?下面做一個嘗試:
- ubuntu $ gcc test.c -lhello -lworld -L .
沒有報錯,成功生成a.out。
執行a.out並且使用ldd查看a.out的依賴:
由上圖可見,雖然使用-lworld參數將libworld.so鏈接到了a.out中,但是上面只顯示a.out依賴於libhello.so。由於找不到libhello.so(=> not found)的路徑,因此需要設置環境變量LD_LIBRARY_PATH
- ubuntu export LD_LIBRARY_PATH=/home/liyihai/documents
再次執行a.out並使用ldd命令查看a.out的依賴庫:
由上圖可見,libhello.so已經通過LD_LIBRARY_PATH環境變量找到,並且libworld.so也出現在a.out的依賴中!
結論:-L指定的是鏈接時的庫路徑,生成的可執行文件在運行時庫的路徑由LD_LIBRARY_PATH環境變量指定。
3.2 -rpath
根據3.1第1張圖的提示,由於libhello.so依賴於libworld.so,可以只用-rpath或者-rpath-link來指定。這里先使用-rpath。
先清空LD_LIBRARY_PATH環境變量,然后重新編譯test.c並且帶上-rpath參數:
- ubuntu $ export LD_LIBRARY_PATH=
- ubuntu $ gcc test.c -lhello -L . -Wl,-rpath .
執行a.out,並且使用ldd命令查看a.out的依賴:
由上圖可見,雖然沒有明確指出鏈接libworld.so,但是libworld.so還是出現在a.out的依賴中。
另外,雖然LD_LIBRARY_PATH已經清空,但是a.out還是可以執行,這說明庫的路徑已經被編譯進a.out中了。需要注意的是,libhello.so和libworld.so的路徑都是通過-rpath指定的路徑找到的。
3.2.1 實驗1
這時候,如果libhello.so和libworld.so的路徑改變了,將會發生什么情況呢?下面做一個實驗。
創建一個lib_tmp目錄,然后將libhello.so和libworld.so移動進這個目錄。
- ubuntu $ mdir lib_tmp
- ubuntu $ mv libhello.so lib_tmp/
- ubuntu $ mv libworld.so lib_tmp/
這時再執行a.out時,提示找不動態庫,使用ldd命令查看a.out的庫路徑:
由上圖紅色圈部分可見,libhello.so的路徑是not found的,並且libworld.so沒有出現在其中。這和3.1的情況是相同的。
究其原因,就是要先找到libhello.so再去找libworl.so,因為是libhello.so依賴於libworld.so,而不是a.out依賴於libworld.so。
由此可見,使用了-rpath參數指定庫的路徑后,生成的可執行文件的依賴庫路徑並非就固定不變了。而是執行時先從-rpath指定的路徑去找依賴庫,如果找不到,還是會報not fund。
那么,這時候,LD_LIBRARY_PATH對a.out是否還有影響呢?下面將LD_LIBRARY_PATH設為當前libhello.so和libworld.so所在的路徑
- ubuntu $ export LD_LIBRARY_PATH=./lib_tmp
再次執行a.out,並且使用ldd查看此時a.out的依賴庫路徑:
由上圖可見LD_LIBRARY_PATH還是起作用的!由上圖可見,和使用-rpath指定路徑的效果是一樣的。
3.2.2 實驗2
將LD_LIBRARY_PATH清空,然后將libhello.so移動到lib_tmp中,而libworld.so則留在documents目錄中。
執行a.out,並且使用ldd查看此時a.out的依賴庫:
由上圖可見,找不到libhello.so。這時,再指定LD_LIBRARY_PATH的路徑為libhello.so所在的路徑:
- ubuntu $ export LD_LIBRARY_PATH=./lib_tmp/
再次執行a.out,並且使用ldd查看其依賴庫:
由上圖可見,一切又恢復了正常。此時,libhello.so是通過LD_LIBRARY_PATH找到的,而libworld.so則是通過-rpath指定的路徑找到的。
3.2.3 回顧
其實,經過測試,在3.1小節中,如果先指定LD_LIBRARY_PATH的值為libhello.so和libworld.so所在的路徑,然后再編譯test.c(執行3.1節的第1條編譯命令),是可以成功編譯的,並不會報3.1小節第1張圖的那種錯誤。也就是說,LD_LIBRARY_PATH不僅指定可執行文件的庫路徑,還指定了庫所依賴於其它庫的路徑。
3.2.4 結論
並非指定-rpath參數后,就拋棄LD_LIBRARY_PATH環境變量,只是多了個可選的依賴庫路徑而已。
3.3 -rpath-link
先將LD_LIBRARY_PATH的值清空,然后將libworld.so移動到lib_tmp目錄中,而libhello.so則留在documents目錄中,使用以下命令對test.c進行編譯:
- ubuntu $ gcc test.c -lhello -L . -Wl,-rpath-link ./lib_tmp
執行a.out並且使用ldd查看a.out的依賴庫:
找不到 libhello.so,這在預料之中。下面指定LD_LIBRARY_PATH的值為libhello.so的路徑,然后在執行a.out,並且查看a.out的依賴:
由上圖可見,libhello.so已經通過LD_LIBRARY_PATH找到,但是libworld.so由於沒有在LD_LIBRARY_PATH指定的路徑中,而且編譯時a.out又沒有包含庫的路徑,因此找不到。