rpath全稱是run-time search path。Linux下所有elf格式的文件都包含它,特別是可執行文件。它規定了可執行文件在尋找.so文件時的第一優先位置。
另外,elf文件中還規定了runpath。它與rpath相同,只是優先級低一些。
搜索.so的優先級順序
- 編譯目標代碼時指定的動態庫搜索路徑; 如果在編譯程序時增加參數-Wl,-rpath='.' , 這時生成程序的Dynamic section會新加一個RPATH段
- 環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑; ( 可用export LD_LIBRARY_PATH="NEWDIRS" 命令添加臨時環境變量 )
- RUNPATH: 寫在elf文件中
- ldconfig的緩存:配置文件/etc/ld.so.conf中指定的動態庫搜索路徑;(系統默認情況下未設置)
- 默認的動態庫搜索路徑/lib;
- 默認的動態庫搜索路徑/usr/lib;
RPATH與RUNPATH中間隔着LD_LIBRARY_PATH,可以通過修改LD_LIBRARY_PATH來指定.so文件,大多數編譯器都將輸出的RPATH留空,並用RUNPATH代替RPATH。
查看RPATH
對於任意的elf文件,可以使用$ readelf -d filename | grep PATH 來查看。
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN/]
結果有兩類,一個是RPATH,另一個是RUNPATH。一般情況下,RPATH為空,而RUNPATH不為空。
RPATH中有個特殊的標識符$ORIGIN。這個標識符代表elf文件自身所在的目錄。
當希望使用相對位置尋找.so文件,就需要利用$ORIGIN設置RPATH。多個路徑之間使用冒號:隔開。
設置RPATH
在gcc中,設置RPATH的辦法很簡單,就是設置linker的rpath選項:
gcc -L. -larith main.c -Wl,-rpath='.' -o main
(-Wl參數意思是把rpath選項傳遞到鏈接階段)
如果需要設置$ORIGIN:$ gcc -Wl,-rpath,'$ORIGIN/lib' test.cpp。
注意,雖然選項里寫着RPATH,但它設置的還是RUNPATH。原因在前文有交代。
在CMake中,使用變量來控制RPATH:INSTALL_RPATH和BUILD_RPATH。
設置的辦法是:SET_TARGET_PROPERTIES(target PROPERTIES INSTALL_RPATH "$ORIGIN;/another/run/path")
(cmake中多個RPATH使用分號隔開)
patchelf
patchelf 是一個用來修改elf格式的動態庫和可執行程序的小工具,可以修改動態鏈接庫的庫名字,以及鏈接庫的RPATH。
-
打印出動態庫的soname
patchelf --print-soname xxx.so
-
修改動態庫的soname
patchelf --set-soname oldxxx.so newxxx.so
-
查看並修改第三方依賴庫
patchelf --print-needed xxx.so
patchelf --replace-needed oldxxx.so newxxx.so this.so
-
修改rpath
patchelf --set-rpath '$ORIGIN/' main