轉載自: http://blog.csdn.net/q1302182594/article/details/42102961
1. 三個C文件
1. world.c
#include<stdio.h> void world(void) { printf("world.\n"); }
2. hello.c
#include <stdio.h> void world(void); void hello(void) { printf("hello\n"); world(); }
3. main.c
void main(void) { hello(); }
2. 編譯動態庫
gcc -c -fPIC hello.c world.c gcc -shared -o libhello.so hello.o gcc -shared -o libworld.so world.o
-fPIC編譯選項是因為我是AMD64位系統,X86的系統似乎不需要
這時,生成的文件及其依賴如下圖:
由上圖可見,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:
gcc -shared -o libhello.so hello.o -lworld -L .
此時,再使用ldd查看libhello.so的依賴:
由上圖可見,此時libhello.so已經依賴於libworld.so。
3. 編譯main.c
3.1: -L
gcc test.c -lhello -L .
由上圖可見已經在-L指定的路徑找打了libhello.so,只是libhello.so還需要libworld.so。雖然它都在同一目錄下,但是還是沒有辦法自動找到libworld.so。
能不能使用-lworld將libworld.so也一並鏈接到test.c中呢?下面做一個嘗試:
gcc main.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
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參數:
export LD_LIBRARY_PATH=
gcc test.c -lhello -L . -Wl,-rpath .
由上圖可見,雖然沒有明確指出鏈接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移動進這個目錄。
mdir lib_tmp mv libhello.so lib_tmp/ 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所在的路徑
export LD_LIBRARY_PATH=./lib
由上圖可見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所在的路徑:
export LD_LIBRARY_PATH=./lib
由上圖可見,一切又恢復了正常。此時,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目錄中,而libhello.so則留在linker目錄中,使用以下命令對main.c進行編譯:
gcc test.c -lhello -L . -Wl,-rpath-link ./lib
找不到 libhello.so,這在預料之中。下面指定LD_LIBRARY_PATH的值為libhello.so的路徑,然后在執行a.out,並且查看a.out的依賴:
由上圖可見,libhello.so已經通過LD_LIBRARY_PATH找到,但是libworld.so由於沒有在LD_LIBRARY_PATH指定的路徑中,而且編譯時a.out又沒有包含庫的路徑,因此找不到。這對比3.2.2可以得出結論:-rpath和-rpath-link都可以在鏈接時指定庫的路徑;但是運行可執行文件時,-rpath-link指定的路徑就不再有效(鏈接器沒有將庫的路徑包含進可執行文件中),而-rpath指定的路徑還有效(因為鏈接器已經將庫的路徑包含在可執行文件中了。) 最后,不管使用了-rpath還是-rpath-link,LD_LIBRARY_PATH還是有效的。
可以參考另一博文http://www.cnblogs.com/flyinggod/p/8026567.html 查看ld的搜索路徑