文章目錄
- RPATH與RUNPATH的差別
-
- 源檔案
- 複現步驟
- 問題原因
-
- 排除共享庫本身問題
- 分析庫查找過程
- RPATH與RUNPATH的差別
- 解決方案
-
- LD_LIBRARY_PATH(不推薦)
- --disable-new-dtags
- 參考
本文從一個實際遇到的問題出發,分析
RPATH
與
RUNPATH
的差別,以及産生的原因。
RPATH與RUNPATH的差別
年前更新了作業系統後,同樣的代碼在新系統編譯後無法執行,提示找不到依賴庫,本文用來記錄一下是如何解決這個問題的。
源檔案
main.c
:
#include "a.h"
int main() {
a();
return 0;
}
libA.so
:
// a.c
#include "b.h"
void a() {
b();
}
// a.h
void a();
ubuntu 16.04(gcc version 5.4.0)
:
$ gcc -fPIC -shared a.c -I. -L. -lB -o libA.so
libB.so
:
// b.c
#include <stdio.h>
void b() {
printf("Hello, World\n");
}
// b.h
void b();
ubuntu 16.04(gcc version 5.4.0)
:
$ gcc -fPIC -shared -I. b.c -o libB.so
函數調用依賴關系:->
main
->
a
。
b
複現步驟
- 分别在
和ubuntu 16.04(gcc version 5.4.0)
編譯可執行程式:ubuntu 20.04(gcc version 9.3.0)
$ gcc -I. -o main main.c -Wl,--rpath,. -L. -lA -lB
- 在
運作成功:ubuntu 16.04(gcc version 5.4.0)
$ ./main
Hello, World
- 在
運作失敗:ubuntu 20.04(gcc version 9.3.0)
$ ./main
./main: error while loading shared libraries: libB.so: cannot open shared object file: No such file or directory
錯誤提示為找不到可執行程式依賴的共享庫
./main
。
libB.so
問題原因
排除共享庫本身問題
首先,由于
libA.so
和
libB.so
是在
ubuntu 16.04(gcc version 5.4.0)
上編譯的,是以重新在
ubuntu 20.04(gcc version 9.3.0)
上編譯
libA.so
和
libB.so
,并重複複現步驟,現象相同,說明不是
libA.so
和
libB.so
導緻的問題;
分析庫查找過程
使用
LD_DEBUG
,打開連結器的調試功能,分析共享庫的查找過程:
ubuntu 16.04(gcc version 5.4.0)
:
$ LD_DEBUG=libs ./main [16:37:15]
22331: find library=libA.so [0]; searching
22331: search path=./tls/x86_64:./tls:./x86_64:. (RPATH from file ./main)
......
22331: trying file=./libA.so
......
22331: find library=libB.so [0]; searching
22331: search path=./tls/x86_64:./tls:./x86_64:. (RPATH from file ./main)
......
22331: trying file=./libB.so
......
ubuntu 20.04(gcc version 9.3.0)
:
$ LD_DEBUG=libs ./main
33218: find library=libA.so [0]; searching
33218: search path=./tls/haswell/x86_64:./tls/haswell:./tls/x86_64:./tls:./haswell/x86_64:./haswell:./x86_64:. (RUNPATH from file ./main)
......
33218: trying file=./libA.so
......
33218: find library=libB.so [0]; searching
33218: search cache=/etc/ld.so.cache
33218: search path=/lib/x86_64-linux-gnu/tls/haswell/x86_64:/lib/x86_64-linux-gnu/tls/haswell:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/haswell/x86_64:/lib/x86_64-linux-gnu/haswell:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64:/usr/lib/x86_64-linux-gnu/tls/haswell:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls:/usr/lib/x86_64-linux-gnu/haswell/x86_64:/usr/lib/x86_64-linux-gnu/haswell:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/haswell/x86_64:/lib/tls/haswell:/lib/tls/x86_64:/lib/tls:/lib/haswell/x86_64:/lib/haswell:/lib/x86_64:/lib:/usr/lib/tls/haswell/x86_64:/usr/lib/tls/haswell:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/haswell/x86_64:/usr/lib/haswell:/usr/lib/x86_64:/usr/lib (system search path)
......
./main: error while loading shared libraries: libB.so: cannot open shared object file: No such file or directory
結果在
ubuntu 20.04(gcc version 9.3.0)
中, 可以正确查找到
libA.so
,但是無法正确查找到
libB.so
。
libB.so
的查找路徑是
system search path
,而非我們在編譯時設定的查找時路徑
./
,導緻可執行程式無法加載
libB.so
。
動态連結器對共享庫的查找順序:
、
LD_LIBRARY_PATH
和
-L
;
-rpath
;
/etc/ld.so.cache
- 預設共享庫目錄:
、
/usr/lib
;
/lib
RPATH與RUNPATH的差別
由上分析可以得出是共享庫運作時加載路徑非法導緻的問題,在排除設定
LD_LIBRARY_PATH
等環境變量的情況下,可以将焦點鎖定在使用的連結選項
-rpath
上,檢視源檔案依賴:
ubuntu 16.04(gcc version 5.4.0)
:
$ readelf -d main
Dynamic section at offset 0xe08 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libA.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [.]
$ readelf -d libA.so
Dynamic section at offset 0xe08 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libB.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
$ readelf -d libB.so
Dynamic section at offset 0xe18 contains 24 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
ubuntu 20.04(gcc version 9.3.0)
:
$ readelf -d main
Dynamic section at offset 0x2da8 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libA.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [.]
$ readelf -d libA.so
Dynamic section at offset 0xe08 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libB.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
$ readelf -d libB.so
Dynamic section at offset 0xe18 contains 24 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
發現在
ubuntu 20.04(gcc version 9.3.0)
上
RPATH
變成了
RUNPATH
,說明連結器選項
-rpath
的行為發生了改變。
源檔案依賴關系:->
main
->
libA.so
;
libB.so
綜上,問題的原因是
ubuntu 20.04(gcc version 9.3.0)
上,連結器選項
-rpath
的行為發生改變,預設配置為
RUNPATH
而不是
RPATH
;由于
RUNPATH
不适用于間接依賴的庫,是以導緻在
ubuntu 20.04(gcc version 9.3.0)
上隻能正确查找到
libA.so
,而無法正确查找到
libB.so
。
時,
gcc version >= 7.5.0
預設行為即發生改變。
-rpath
解決方案
LD_LIBRARY_PATH(不推薦)
LD_LIBRARY_PATH
是一個環境變量,作用是臨時改變連結器的加載路徑,可以存儲多個路徑,用冒号分隔:
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./libs
不推薦的原因:
- 若全局設定
,會影響其它應用程式的共享庫加載過程;LD_LIBRARY_PATH
- 若隻在該應用程式啟動時局部設定
,則每次啟動都需要設定,步驟過于繁瑣;LD_LIBRARY_PATH
–disable-new-dtags
可以使用
-Wl,--disable-new-dtags
選項來使連結器保持舊行為,即在
ubuntu 20.04(gcc version 9.3.0)
使用如下指令編譯:
$ gcc -I. -o main main.c -Wl,--disable-new-dtags,--rpath,. -L. -lA -lB
重新運作并檢視依賴:
$ ./main
Hello, World
$ readelf -d main
Dynamic section at offset 0x2da8 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libA.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [.]
可執行程式
main
可以正确運作,
RUNPATH
也變成了
RPATH
,連結器行為與
ubuntu 16.04(gcc version 5.4.0)
保持一緻了。
同理,也有 -Wl,--enable-new-dtags
選項來使連結器保持新行為
如下為官方解釋:
--enable-new-dtags
--disable-new-dtags
This linker can create the new dynamic tags in ELF. But the older ELF systems may not understand them. If you specify --enable-new-dtags, the new dynamic tags will be created as needed and older dynamic tags will be omitted. If you specify --disable-new-dtags, no new dynamic tags will be created. By default, the new dynamic tags are not created. Note that those options are only available for ELF systems.
參考
- How to set RPATH and RUNPATH with GCC/LD?
- use RPATH but not RUNPATH?
微信公衆号同步更新,微信搜尋"AnSwEr不是答案"或者掃描二維碼,即可訂閱。

- GitHub:AnSwErYWJ
- Blog:http://www.answerywj.com
- Email:[email protected]
- Weibo:@AnSwEr不是答案