一、參考資料
鏈接選項 rpath 的應用和原理 | BewareMyPower的博客
使用 rpath 和 rpath-link 確保 samba-util 庫正確鏈接-CSDN博客
編譯參數-Wl和rpath的理解_-wl,-rpath-CSDN博客
Using LD, the GNU linker - Options
Directory Options (Using the GNU Compiler Collection (GCC))
交叉編譯時–sysroot,-rpath,-rpath-link,-L之間的關系與注意點_交叉編譯 sysroot-CSDN博客
二、相關介紹
Linux 動態庫查找路徑
一個典型的 C/C++ 程序的構建流程是:預處理,匯編,編譯,鏈接。而執行鏈接的程序其實是 ld
,通常編譯器比如 GCC 都會自動調用 ld
去進行鏈接,用戶不必關注其中的細節。而 ld
查找動態庫的順序是:
rpath
指定的目錄;- 環境變量
LD_LIBRARY_PATH
指定的目錄; runpath
指定的目錄;/etc/ld.so.cache
緩存文件,通常包含/etc/ld.so.conf
文件編譯出的二進制倆別哦(比如 CentOS 上,該文件會使用 include 從而使用ld.so.conf.d
目錄下面所有的*.conf
文件,這些都會緩存在 ld.so.cache 中)- 系統默認路徑,比如
/lib
,/usr/lib
。
在編譯時若使用 -z nodefaultlib
選項編譯,則會跳過 4 和 5。至于 runpath,和 rpath 類似,都是二進制(ELF)文件的動態 section 屬性(分別為 DT_RUNPATH
和 DT_RPATH
),唯一區別就是是否優先于 LD_LIBRARY_PATH
來查找。
rpath
vs.runpath
:rpath 和 runpath 是嵌入在可執行文件或共享庫中的路徑列表,用于指定運行時查找共享庫的位置。rpath 是舊標準,runpath 是新標準,功能類似但優先級不同。
-Wl
參數
gcc的-Wl,xxx
選項將逗號分隔的標記列表(flags)作為空格分隔的參數列表傳遞給鏈接器,即:
gcc -Wl,aaa,bbb,ccc
最終變成了linker的用法:
ld aaa bbb ccc
如果是想把ld -rpath
通過-Wl
傳遞給gcc,可以是-Wl,-rpath,xxx
,也可以指定-Wl的重復實例:
gcc -Wl,aaa -Wl,bbb -Wl,ccc
類似的參數還有:
-Wa,<options> Pass comma-separated <options> on to the assembler
-Wp,<options> Pass comma-separated <options> on to the preprocessor
-Wl,<options> Pass comma-separated <options> on to the linker
--sysroot
選項
--sysroot = dir
將dir作為邏輯root目錄,用于搜索頭文件和依賴庫文件,例如 --sysroot=/home/build
,那么如果之前默認去 /usr/lib
下面去搜索依賴庫,則在sysroot的作用下會定位到 /home/build/usr/lib
目錄下進行搜索。這個參數在交叉編譯的時候會影響到rpath,如果沒有設置這個sysroot,則rpath在編譯階段是不起作用的。
在交叉編譯工具中,有默認的 sysroot 路徑。
# 設置交叉編譯工具鏈的環境變量
export PATH=/home/yoyo/360Downloads/toolchains/arm-linux-gnueabihf/bin:$PATH# 查看交叉編譯工具鏈的 sysroot 路徑
arm-linux-gnueabihf-gcc -print-sysroot
輸出示例:
yoyo@yoyo:~$ arm-linux-gnueabihf-gcc -print-sysroot
/home/yoyo/360Downloads/toolchains/arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc
$ORIGIN
變量
關于Linux上的$ORIGIN解說
關于Linux上的$ORIGIN解說
許多現代C / C ++項目都利用 Autotools
創建GNU構建系統,例如根據平臺生成make文件。 可執行文件(二進制文件)在生成/編譯過程中生成,并且可以在執行編譯的計算機上本地執行。 但是,如果將同一可執行文件移動到另一臺計算機上,或者只是移到同一臺計算機上的其他文件夾,則在運行該可執行文件時可能會遇到“找不到庫”錯誤。因此,引入一個特殊的環境變量 $ORIGIN
。
在Linux系統中,$ORIGIN
是一個特殊的環境變量,表示可執行文件或共享庫所在的目錄。使用 $ORIGIN
可以指定相對于可執行文件或共享庫的路徑,確保程序在不同目錄中運行時能正確找到依賴庫。
使用 $ORIGIN
的好處在于,它允許可移植性更高的應用程序部署,因為這意味著應用程序和它的依賴庫可以被放置在文件系統中的任意位置,并且在運行時動態鏈接器仍然能正確找到它們,只要維持相對結構不變即可。
簡單示例:假設有一個可執行文件 /app/bin/myprogram
,依賴庫在 /app/lib
中。可以在編譯時設置 RPATH
或 RUNPATH
為 $ORIGIN/../lib
,這樣程序運行時會在 /app/lib
中查找依賴庫。
/app/myprogram.cbin/applib/libdependency.so
在鏈接 app
時,可以使用:
gcc -Wl,-rpath,'$ORIGIN/../x264/lib' -Wl,rpath,'$ORIGIN/../x265/lib' -o app myprogram.c
-Wl,-rpath,'$ORIGIN'
如果需要設置多個路徑,可以將它們用空格分隔,并確保每個路徑前都有 -Wl,-rpath,
。
-Wl,-rpath,'$ORIGIN/../third_party/arm_opencv/3rdparty/zlib/lib' \
-Wl,-rpath,'$ORIGIN/../third_party/arm_opencv/3rdparty/szlib/lib'
這樣,不管 /usr/appdir/
復制到文件系統的哪個地方,執行 app
時,libdependency.so
都會從執行文件所在目錄下的 lib
目錄中被找到和加載。
在處理諸如需要將應用打包到一個單一文件夾以實現便攜式部署的情況下,$ORIGIN
變量異常有用。使用 $ORIGIN
,開發者便無需擔心程序部署后動態鏈接庫的路徑問題,從而大大提高了軟件的移植性和靈活性。
注意事項:
$ORIGIN
必須用引號括起來,防止被 shell 解釋。- 使用
$ORIGIN
時,確保路徑設置正確,避免運行時找不到庫。 - 在
Makefile
或其他文件中直接使用時,只用一個$
會展開成變量值,通常需要寫作$$ORIGIN
來避免變量擴展。
三、-L、-rpath-link和-rpath
通過使用 rpath
和 rpath-link
,可以確保程序在運行時正確找到依賴的庫。這對于處理不在系統默認路徑下的庫非常有用。在編譯和鏈接時正確使用這兩個選項可以確保你的程序在不同環境中都能正常運行。
引言
現代連接器在處理動態庫時將鏈接時路徑(Link-time path) 和 運行時路徑(Run-time path) 分開,用戶可以通過 -L
指定連接時庫的路徑,通過-R
(或-rpath
)指定程序運行時庫的路徑,大大提高了庫應用的靈活性。
-rpath
和-rpath-link
之間的區別:-rpath
選項指定的目錄被包含在可執行文件中并在運行時使用,而-rpath-link
選項僅在鏈接時有效。
通過測試發現,-Wl,-rpath
下面這三種寫法都是可以的:
-Wl,-rpath -Wl,/usr/lib/gstreamer-1.0
-Wl,-rpath,/usr/lib/gstreamer-1.0
-Wl,-rpath=/usr/lib/gstreamer-1.0
-l
選項
功能:添加需要鏈接的庫文件,如果沒有后綴指明動態庫還是靜態庫,則優先使用動態庫。
-L
選項(編譯時路徑)
功能:
-L
指定的是編譯時鏈接的動態庫搜索路徑。
編譯時-L
選項并不影響環境變量 LD_LIBRARY_PATH
,只是指定了程序編譯連接時庫的路徑,并不影響程序執行時庫的路徑。當程序運行時,系統還是會到默認路徑下查找該程序所需要的庫,如果找不到,會出現類似 cannot open shared object file
的錯誤。
-L/mylib -lmylib
-Wl,-rpath
選項(運行時路徑)
功能:
-Wl,-rpath
用于指定動態庫的搜索路徑(在運行階段),該路徑會被記錄在elf可執行文件中。
-rpath
的作用相當于在程序運行時設置了 LD_LIBRARY_PATH
環境變量,因為 -rpath
指定的路徑會被記錄在生成的可執行程序中,用于運行時查找需要加載的動態庫 。因此,在開發板中無需設置環境變量即可找到相關的動態庫。通常情況下,推薦使用 -Wl,-rpath
選項。
# 單個路徑
-L/mylib -lmylib -Wl,-rpath=dir# 多個路徑
-L/mylib -lmylib -Wl,-rpath,dir1:dir2:...:dirN
在cmake中使用:
# 設置第三方庫路徑
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")# 設置目標屬性,將運行時庫搜索路徑添加到目標
set_target_properties(dla_detectPROPERTIESLINK_FLAGS"-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/zlib/lib' \-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/szlib/lib' \-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath,'$ORIGIN/../../third_party/hdf5/lib' \-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath,'$ORIGIN/../../third_party/x264/lib' \-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath,'$ORIGIN/../../third_party/x265/lib' \-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath,'$ORIGIN/../../third_party/ffmpeg/lib' \-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath,'$ORIGIN}/../../third_party/sigmastar/lib' \-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath,'$ORIGIN/../../third_party/arm_opencv/lib' \-L${PARENT_DIR}/build -Wl,-rpath,'$ORIGIN/../../build'"
)
或者:
# 設置第三方庫路徑
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")# 設置rpath
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \
-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/zlib/lib' \
-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/szlib/lib' \
-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath,'$ORIGIN/../../third_party/hdf5/lib' \
-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath,'$ORIGIN/../../third_party/x264/lib' \
-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath,'$ORIGIN/../../third_party/x265/lib' \
-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath,'$ORIGIN/../../third_party/ffmpeg/lib' \
-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath,'$ORIGIN/../../third_party/sigmastar/lib' \
-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath,'$ORIGIN/../../third_party/arm_opencv/lib' \
-L${PARENT_DIR}/build -lsigmastar_vVehicle_det -Wl,-rpath,'$ORIGIN/../../build'")
編譯之后,查看rpath:
yoyo@yoyo:~/share/driver/dla_detect$ readelf -d test/build/dla_detect
...
0x0000000f (RPATH) Library rpath: [$ORIGIN/../../third_party/zlib/lib:$ORIGIN/../../third_party/szlib/lib:$ORIGIN/../../third_party/hdf5/lib:$ORIGIN/../../third_party/x264/lib:$ORIGIN/../../third_party/x265/lib:$ORIGIN/../../third_party/ffmpeg/lib:$ORIGIN/../../third_party/sigmastar/lib:$ORIGIN/../../third_party/arm_opencv/lib:$ORIGIN/../../build:/home/yoyo/share/driver/dla_detect/third_party/arm_opencv/share/opencv4/../../lib]
...
-Wl,-rpath-link
選項(運行時路徑)
功能:
-Wl,-rpath-link
用于指定動態庫的搜索路徑(在鏈接階段),該選項只在鏈接階段起作用,不會被寫入elf文件中。
-L/mylib -lmylib -Wl,-rpath-link=<library_path>
在cmake中使用:
# 設置第三方庫路徑
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")# 設置目標屬性,將運行時庫搜索路徑添加到目標
set_target_properties(dla_detectPROPERTIESLINK_FLAGS"-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/zlib/lib' \-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/szlib/lib' \-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/hdf5/lib' \-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x264/lib' \-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x265/lib' \-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/ffmpeg/lib' \-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath-link,'$ORIGIN}/../../third_party/sigmastar/lib' \-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath,'$ORIGIN/../../third_party/arm_opencv/lib' \-L${PARENT_DIR}/build -Wl,-rpath-link,'$ORIGIN/../../build'"
)
或者:
# 設置第三方庫路徑
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")# 設置rpath
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \
-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/zlib/lib' \
-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/szlib/lib' \
-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/hdf5/lib' \
-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x264/lib' \
-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x265/lib' \
-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/ffmpeg/lib' \
-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/sigmastar/lib' \
-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/arm_opencv/lib' \
-L${PARENT_DIR}/build -lsigmastar_vVehicle_det -Wl,-rpath-link,'$ORIGIN/../../build'")
編譯之后,查看二進制的 rpath
:
yoyo@yoyo:~/share/driver/dla_detect$ readelf -d test/build/dla_detect
...
0x0000000f (RPATH) Library rpath: [/home/yoyo/share/driver/dla_detect/third_party/arm_opencv/share/opencv4/../../lib]
...
查看 rpath
:
objdump -x path/to/executable | grep RPATH
readelf -d path/to/executable | head -20
chrpath -l path/to/executable