問題簡述
描述一下問題的大概狀況
程序 P---->直接調用 libA.so
+----> 調用 libB.so
也就是程序 P 間接調用了 libB.so
之前記錄過這個問題(鏈接選項-rpath的一個問題記錄),並沒有詳細去找尋原因。這里再次記錄一下。
在編譯 libA.so
的時候,沒有使用鏈接選項 -Wl,-rpath
,在編譯 P
的時候,只鏈接了 libA.so
,沒有鏈接 libB.so
(這個在之前的文章也提到了)。
實際示例
1、運行提示信息
編寫的程序 autoover
使用到了第三方庫 libgdal.so
,而這個庫間接使用到了 libproj.so
。編譯 autoover
的時候指定了 -Wl,-rpath=./lib
鏈接選項。
直接運行,報錯,無法找到 libproj.so.20
文件,實際這個文件也是放在 ./lib
目錄下的。
bin/autoover
bin/autoover: error while loading shared libraries: libproj.so.20: cannot open shared object file: No such file or directory
2、依賴庫狀況
先通過 ldd
命令查看下 autoover
所依賴到的庫,可以看到它找不到 libproj.so.20
這個動態庫。
ldd bin/autoover
linux-vdso.so.1 (0x00007ffc707f9000)
libgdal.so.27 => ./lib/libgdal.so.27 (0x00007f030c7b6000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f030c784000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f030c596000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f030c447000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f030c42d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f030c23c000)
libproj.so.20 => not found
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f030c22f000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f030c229000)
/lib64/ld-linux-x86-64.so.2 (0x00007f030e92a000)
先通過 readelf -d
查看下程序 都引用了哪些so
,可以看到程序autoover
引用了libgdal.so.27
,Library runpath
是 ./lib
。它並沒有直接引用 libproj.so.20
。
readelf -d bin/autoover
Dynamic section at offset 0x2a8 contains 30 entries:
標記 類型 名稱/值
0x000000000000001d (RUNPATH) Library runpath: [./lib]
0x0000000000000001 (NEEDED) 共享庫:[libgdal.so.27]
0x0000000000000001 (NEEDED) 共享庫:[libpthread.so.0]
0x0000000000000001 (NEEDED) 共享庫:[libstdc++.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libm.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libgcc_s.so.1]
0x0000000000000001 (NEEDED) 共享庫:[libc.so.6]
# ..... 省略很多行不重要的信息
readelf -d lib/libgdal.so.27.0.0
Dynamic section at offset 0x1f1e128 contains 33 entries:
標記 類型 名稱/值
0x0000000000000001 (NEEDED) 共享庫:[libproj.so.20]
0x0000000000000001 (NEEDED) 共享庫:[librt.so.1]
0x0000000000000001 (NEEDED) 共享庫:[libpthread.so.0]
0x0000000000000001 (NEEDED) 共享庫:[libdl.so.2]
0x0000000000000001 (NEEDED) 共享庫:[libstdc++.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libm.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libc.so.6]
0x0000000000000001 (NEEDED) 共享庫:[ld-linux-x86-64.so.2]
0x0000000000000001 (NEEDED) 共享庫:[libgcc_s.so.1]
0x000000000000000e (SONAME) Library soname: [libgdal.so.27]
3、運行時加載依賴庫狀況
使用 strace
跟蹤執行下(也可以使用ltrace
跟蹤) ,查看查找 libproj.so.20
的過程,可以知道在加載間接引用的庫的時候,不會去當前程序的 Library runpath
查找。
strace -e trace=file bin/autoover
execve("bin/autoover", ["bin/autoover"], 0x7ffe318e2060 /* 28 vars */) = 0
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/haswell/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/haswell/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/libgdal.so.27", O_RDONLY|O_CLOEXEC) = 3
# ...... 上面在 ./lib 目錄下找到了 libgdal.so.27 下面省略很多行 ..........
# ....... 程序 自身直接引用的加載完成,之后再去加載間接引用的庫(如果已經加載了則不再加載)..........
# ........ 可以看到下面的加載路徑,不會去 ./lib 下加載,只去了系統相關路徑(實際上會去 libgdal.so.27 的 Library runpath 加載)....
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/haswell/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/haswell/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/haswell/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/haswell/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
# .... 這里也省略很多行(刪除了很多不重要的輸出) ...........
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/tls/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/tls/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/usr/lib/tls/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/usr/lib/haswell/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/usr/lib/haswell/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/usr/lib/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/usr/lib/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
stat("/usr/lib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
bin/autoover: error while loading shared libraries: libproj.so.20: cannot open shared object file: No such file or directory
+++ exited with 127 +++
給 libgdal.so.27
設置一個 rpath
然后再跟蹤一次(之前libgdal.so.27
是沒有設置rpath
的,這里就展示了)
# 設置一下 rpath
patchelf --set-rpath ./lib lib/libgdal.so.27
# 查看下設置是否生效
readelf -d lib/libgdal.so.27.0.0
Dynamic section at offset 0x1f5c000 contains 34 entries:
標記 類型 名稱/值
0x000000000000001d (RUNPATH) Library runpath: [./lib]
0x0000000000000001 (NEEDED) 共享庫:[libproj.so.20]
0x0000000000000001 (NEEDED) 共享庫:[librt.so.1]
0x0000000000000001 (NEEDED) 共享庫:[libpthread.so.0]
0x0000000000000001 (NEEDED) 共享庫:[libdl.so.2]
0x0000000000000001 (NEEDED) 共享庫:[libstdc++.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libm.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libc.so.6]
0x0000000000000001 (NEEDED) 共享庫:[ld-linux-x86-64.so.2]
0x0000000000000001 (NEEDED) 共享庫:[libgcc_s.so.1]
0x000000000000000e (SONAME) Library soname: [libgdal.so.27]
跟蹤下庫加載過程
strace -e trace=file bin/autoover
execve("bin/autoover", ["bin/autoover"], 0x7ffcda7c1eb0 /* 28 vars */) = 0
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/haswell/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/haswell/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/x86_64/libgdal.so.27", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/libgdal.so.27", O_RDONLY|O_CLOEXEC) = 3
# .... 上面已經加載成功 libgdal.so.27 下面加載 autoover 直接引用的其它庫的省略 ....
# ..... 加載 libproj.so.20 先從 libgdal.so.27 的 rpath 進行加載(這里可以給libgdal.so.27設置rpath為絕對路徑可以看出來)
# ............ 這里加載的時候 rpath 如果是相對路徑,都是相對於當前工作目錄的,不是文件路徑的 ....
openat(AT_FDCWD, "./lib/tls/haswell/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/haswell/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/x86_64/libproj.so.20", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/libproj.so.20", O_RDONLY|O_CLOEXEC) = 3
# 加載完成之后會繼續去加載 間接引用庫 的引用(如果已經加載則不再加載),這里 librt 是 libgdal.so 引用的,libdl 是 libproj.so 引用的
openat(AT_FDCWD, "./lib/tls/haswell/x86_64/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/haswell/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/x86_64/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/x86_64/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/x86_64/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "./lib/tls/haswell/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/haswell/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/tls/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/haswell/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/x86_64/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "./lib/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (沒有那個文件或目錄)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
再看一下 autoover
的依賴庫情況
ldd bin/autoover
linux-vdso.so.1 (0x00007ffd619d8000)
libgdal.so.27 => ./lib/libgdal.so.27 (0x00007f3b7846b000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3b78439000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f3b7824b000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f3b780fc000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f3b780e2000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3b77ef1000)
libproj.so.20 => ./lib/libproj.so.20 (0x00007f3b77883000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f3b77878000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3b77872000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3b7a7c5000)
解決辦法匯總
- 運行程序前使用環境變量
LD_LIBRARY_PATH=xxxx
指定庫加載路徑(間接引用的庫在里面)LD_LIBRARY_PATH=./lib bin/autoover
- 對直接引用的庫,設置
rpath
用於查找間接引用的庫(可能有多級間接) - 將間接引用的庫所在的目錄路徑添加到
/etc/ld.so.conf
中,並使用ldconfig
更新下/etc/ld.so.cache
- 將間接引用的庫,添加到程序的依賴列表里面去
patchelf --add-needed libproj.so.20 bin/autoover