目錄
1.Linux 動態庫相關知識
1.1.動態庫查找路徑
1.2.查看程序依賴的動態庫
1.3.修改動態庫查找路徑的方法
1.4.動態鏈接器緩存管理
2.-Wl參數
3.-L選項(編譯時路徑)
4.-rpath參數(運行時路徑)
5.-rpath-link?參數
6.常見問題與解決方案
7.總結
????????在 Linux 系統中編譯和鏈接動態庫時,-L
、-rpath
和-rpath-link
是三個非常重要的參數,它們分別解決了動態庫編譯、鏈接和運行時的不同問題。
1.Linux 動態庫相關知識
1.1.動態庫查找路徑
????????一個典型的 C/C++ 程序的構建流程是:預處理,匯編,編譯,鏈接。而執行鏈接的程序其實是?ld
,通常編譯器比如 GCC 都會自動調用?ld
?去進行鏈接,用戶不必關注其中的細節。而?ld
?查找動態庫的順序是:? ??
? 1.程序本身指定的路徑, 即是rpath
或?runpath
指定的目錄;
RPATH
:硬編碼在 ELF 文件中,優先級最高。RUNPATH
:與?RPATH
?類似,但優先級低于?LD_LIBRARY_PATH
。
? ? ?2.環境變量?LD_LIBRARY_PATH
?指定的目錄;
- 用戶自定義的臨時路徑,多個路徑用冒號?
:
?分隔。 - 例如:
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH
。
? ? ?3.runpath
?指定的目錄;
? 4./etc/ld.so.cache
緩存文件,通常包含?/etc/ld.so.conf
?文件編譯出的二進制(比如 CentOS 上,該文件會使用 include 從而使用?ld.so.conf.d
?目錄下面所有的?*.conf
?文件,這些都會緩存在 ld.so.cache 中)
- 這些文件中指定的路徑會被動態鏈接器緩存(通過?
ldconfig
?命令更新)。
? ? ?5.系統默認路徑,比如?/lib
,/usr/lib
。
/lib
:系統核心庫(如?libc.so
)。/usr/lib
:用戶級庫。/usr/local/lib
:本地安裝的庫(如自行編譯的軟件)。
在編譯時若使用?-z nodefaultlib
?選項編譯,則會跳過 4 和 5。至于 runpath,和 rpath 類似,都是二進制(ELF)文件的動態 section 屬性(分別為?DT_RUNPATH
?和?DT_RPATH
),唯一區別就是是否優先于?LD_LIBRARY_PATH
?來查找。
rpath
?vs.?runpath
:rpath?和?runpath?是嵌入在可執行文件或共享庫中的路徑列表,用于指定運行時查找共享庫的位置。rpath?是舊標準,runpath?是新標準,功能類似但優先級不同。
1.2.查看程序依賴的動態庫
1.ldd
?命令
ldd /path/to/program # 顯示程序依賴的所有動態庫及其路徑
- 若某庫顯示為?
not found
,表示系統未找到該庫。
2.readelf
?命令
readelf -d /path/to/program | grep -E '(RPATH|RUNPATH|NEEDED)'
輸出示例:
0x000000000000000f (RPATH) Library rpath: [/path/to/lib]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
1.3.修改動態庫查找路徑的方法
1.臨時修改:通過?LD_LIBRARY_PATH
?環境變量
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH # 當前終端生效
# 或添加到 ~/.bashrc 中使其永久生效
echo 'export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
2.永久修改:編輯?/etc/ld.so.conf
?或添加新配置
# 方法1:直接編輯主配置文件
sudo vim /etc/ld.so.conf
# 添加一行:/path/to/lib
# 保存后更新緩存
sudo ldconfig# 方法2:在 /etc/ld.so.conf.d/ 目錄下創建新配置文件
sudo touch /etc/ld.so.conf.d/myapp.conf
sudo echo '/path/to/lib' > /etc/ld.so.conf.d/myapp.conf
sudo ldconfig # 更新緩存
3.編譯時指定:通過?-Wl,-rpath
?選項
在編譯程序時,使用?-Wl,-rpath
?將路徑硬編碼到程序中:
gcc -o myapp myapp.c -L/path/to/lib -lmylib -Wl,-rpath,/path/to/lib
- 優點:無需修改系統配置或環境變量。
- 缺點:路徑被固定,若庫位置改變需重新編譯。
1.4.動態鏈接器緩存管理
1.更新緩存
當添加新庫或修改?/etc/ld.so.conf
?后,需執行:
sudo ldconfig # 重建動態鏈接器緩存
2.查看緩存內容
ldconfig -p # 顯示當前緩存的所有庫及其路徑
2.-Wl參數
????????在 Linux 系統中,-Wl
?是 GCC 編譯器的一個重要參數,用于向鏈接器(如?ld
)傳遞額外選項。由于 GCC 是編譯和鏈接的前端工具,真正處理鏈接過程的是鏈接器,因此需要通過?-Wl
?將參數傳遞給鏈接器。
????????基本語法
gcc [編譯選項] -Wl,鏈接器選項1[,鏈接器選項2,...] [源文件]
將逗號分隔的選項傳遞給鏈接器。如:
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
3.-L
選項(編譯時路徑)
- 編譯時:指定鏈接器搜索庫文件的路徑。
- 僅影響鏈接過程,不影響程序運行時的庫查找。
假設你有一個自定義庫?libmath.so
?位于?/opt/mylibs
?目錄下:
g++ main.cpp -L/opt/mylibs -lmath -o myapp
-L/opt/mylibs
:告訴鏈接器在?/opt/mylibs
?目錄下搜索?libmath.so
。-lmath
:鏈接名為?libmath.so
?的庫。
-L一般用于鏈接非標準位置的第三方庫(如?/usr/local/lib
?以外的路徑)。
4.-rpath參數(運行時路徑)
????????運行時搜索路徑,將動態庫搜索路徑硬編碼到可執行文件中(運行時生效)。通過修改可執行文件的?.dynamic
?段,將路徑硬編碼到二進制文件中。
????????-rpath
?的作用相當于在程序運行時設置了?LD_LIBRARY_PATH
?環境變量,因為?-rpath
指定的路徑會被記錄在生成的可執行程序中,用于運行時查找需要加載的動態庫 。因此,在開發板中無需設置環境變量即可找到相關的動態庫。通常情況下,推薦使用?-Wl,-rpath
?選項。
# 單個路徑
-L/mylib -lmylib -Wl,-rpath=dir# 多個路徑
-L/mylib -lmylib -Wl,-rpath,dir1:dir2:...:dirN
? ? ? ? 示例:
g++ main.cpp -L/opt/mylibs -lmath -Wl,-rpath=/opt/mylibs -o myapp
-Wl,-rpath=/opt/mylibs
:將?/opt/mylibs
?添加到程序運行時的庫搜索路徑。
等價寫法
# 直接使用鏈接器參數
g++ main.cpp -L/opt/mylibs -lmath -Wl,-rpath=/opt/mylibs -o myapp# 或使用環境變量
export LDFLAGS="-Wl,-rpath=/opt/mylibs"
g++ main.cpp -L/opt/mylibs -lmath $LDFLAGS -o myapp
特殊變量
$ORIGIN
:表示可執行文件所在目錄,常用于相對路徑。
# 搜索與可執行文件同級的 lib 目錄
-Wl,-rpath='$ORIGIN/../lib'
注意:在 Makefile 或 shell 腳本中使用時,需用雙引號防止變量提前展開:
-Wl,-rpath="\$$ORIGIN/../lib"
在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'")
5.-rpath-link
?參數
????????用于指定動態庫的搜索路徑(在鏈接階段),該選項只在鏈接階段起作用,不會被寫入elf文件中。輔助鏈接器解析庫之間的依賴關系,但不影響程序運行時的庫搜索路徑。適用于復雜的庫依賴鏈(如 A 依賴 B,B 依賴 C)。
? ? ? ? 示例:假設:
libapp.so
?依賴?libmath.so
。libmath.so
?依賴?libutils.so
(位于?/opt/thirdparty
)。
# 鏈接 libapp.so 時指定 libmath.so 的依賴路徑
g++ -shared -fPIC app.cpp -L/opt/mylibs -lmath -Wl,-rpath-link=/opt/thirdparty -o libapp.so
-Wl,-rpath-link=/opt/thirdparty
:告訴鏈接器,libmath.so
?依賴的?libutils.so
?在?/opt/thirdparty
?目錄下。- 但程序運行時,仍需通過?
-rpath
?或環境變量指定?/opt/thirdparty
。
在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
?的區別
-rpath
:同時影響鏈接和運行時的庫搜索。-rpath-link
:僅在鏈接時生效,不修改可執行文件的運行時路徑。
三者對比
參數 | 生效階段 | 是否影響運行時 | 作用 |
---|---|---|---|
-L | 鏈接時 | 否 | 指定鏈接器搜索庫的路徑 |
-rpath | 運行時 | 是 | 修改程序運行時的庫搜索路徑(硬編碼到二進制文件) |
-rpath-link | 鏈接時 | 否 | 輔助鏈接器解析庫依賴關系,但不影響程序運行時的搜索路徑 |
6.常見問題與解決方案
1.錯誤:libxxx.so: cannot open shared object file: No such file or directory
- 可能原因:
- 庫未安裝。
- 庫路徑未包含在查找路徑中。
- 解決方案
# 1. 確認庫是否存在
find / -name "libxxx.so*" 2>/dev/null# 2. 若存在,將路徑添加到 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH# 3. 或添加到系統配置
echo '/path/to/lib' | sudo tee -a /etc/ld.so.conf
sudo ldconfig
2.區分?RPATH
?和?RUNPATH
- RPATH:在?
RUNPATH
?和?LD_LIBRARY_PATH
?之前被搜索。 - RUNPATH:在?
LD_LIBRARY_PATH
?之后被搜索。
修改方法:
# 編譯時指定 RPATH
gcc -Wl,-rpath,/path/to/lib ...# 編譯時指定 RUNPATH(需顯式使用 -rpath-link)
gcc -Wl,-rpath-link,/path/to/lib -Wl,-rpath,'$ORIGIN/../lib' ...
$ORIGIN
?表示程序所在目錄,用于相對路徑。
3.鏈接時提示找不到庫的依賴
/usr/bin/ld: libmath.so: undefined reference to `function_in_libutils'
原因:
libmath.so
?依賴?libutils.so
,但鏈接器找不到?libutils.so
。
解決方案:
# 臨時指定依賴路徑
g++ -shared -fPIC app.cpp -L/opt/mylibs -lmath -Wl,-rpath-link=/opt/thirdparty -o libapp.so
7.總結
- 優先使用?
-rpath
:減少對環境變量的依賴,提高程序可移植性。 - 使用?
$ORIGIN
:讓庫路徑相對于可執行文件,避免硬編碼絕對路徑。 - 謹慎使用?
-rpath-link
:僅在鏈接復雜依賴的庫時使用,避免污染運行時路徑。 - 測試部署:確保程序在目標環境中能正常找到庫(如使用?
ldd myapp
?檢查)。
通過合理組合?-L
、-rpath
?和?-rpath-link
,可以靈活控制動態庫的編譯、鏈接和運行時行為,解決復雜的依賴問題。