2013.08.23 updated
一 概念:這裡將共用庫(shared library)稱做了動態庫。
在Linux中,ELF格式的可執行檔或動態庫有兩個域,DT_RPATH,DT_RUNPATH(這個是新版本加入的)。
// 這個方法唯寫入DT_RPATH域gcc -g -Wall -o prog prog.c -Wl,-rpath,/home/dir1 -ldemo// 這個方法,同時寫入DT_RPATH和DT_RUNPATHgcc -g -Wall -o prog prog.c -Wl,--enable-new-dtags -Wl,-rpath,/home/dir1 -ldemo
編譯分為幾個階段,其中-W表示在哪個階段將後面參數加入,-Wl表示在link階段加入後面參數,-rpath是為linker指定runtime dynamic path參數。
至於為什麼後面的要同時寫入DT_RPATH和DT_RUNPATH域,是因為之前的版本沒有DT_RUNTIME,為了和老版本程式相容。
二 Linux動態庫的搜尋路徑搜尋的先後順序是:
0> 如果DT_RUNPATH域為空白,且DT_RPATH不為空白,則搜尋DT_RPATH指定的路徑列表(多個用冒號隔開);
1> 環境變數LD_LIBRARY_PATH指定的動態庫搜尋路徑,多個路徑可以用“:”分開;
2> 如果DT_RUNPATH不為空白,那麼搜尋DT_RUNPATH對應的路徑列表;
3> 在/etc/ld.so.cache檔案中指定,這個檔案是一個二進位檔案,如果想添加,先編輯設定檔/etc/ld.so.conf(這個檔案是文本的),然後運行ldconfig產生新的/etc/ld.so.cache;
4> 預設的動態庫搜尋路徑/lib;
5> 預設的動態庫搜尋路徑/usr/lib。
以下是使用LD_LIBRARY_PATH的建議:
1> 不要全域設定LD_LIBRARY_PATH
2> 如果一定要用動態庫的方式發布你的代碼,並且允許使用者自訂安裝路徑,那麼
- 用.o檔案發布,在安裝過程中,重新串連它們到正確的安裝路徑。
- 在可執行程式中,寫一個非常冗長,不存在的路徑(-Wl,-rpath),然後安裝過程中,用一個二進位編輯器將其中的路徑修改為正確的。
3> 如果一定要用LD_LIBRARY_PATH,一定要封裝起來用,例如,shell指令碼。
講一個我親身體驗的例子。
一個已經安裝的軟體包A,構建於平台Platform 1.0,所有的平台相關動態庫檔案位於/opt/lib64/
一個新的軟體包B,構建於平台Platform 2.0,所有平台相關動態庫位於本軟體目錄的/lib下
啟動軟體B,發現運行錯誤,運行ldd命令,發現串連的動態庫不對,應該連本目錄的lib,卻連到了/opt/lib64/
用設定LD_LIBRARY_PATH的方法啟動還是不行,因為路徑已經寫死到可執行檔裡了,用strings命令可以看到二進位裡的字串。
後來查到兩種解決辦法:
1> 用二進位編輯器將可執行檔內的路徑改寫,/opt/lib64改成/opt/lib65,這樣運行時找不到/opt/lib65,那麼LD_LIBRARY_PATH就開始生效了。
2> 編譯B之前,將configure.ac中的路徑修改一下,使得傳遞給-Wl,-rpath的參數是一個不可能存在的路徑。