Linux 間接引用 .so 的路徑問題


問題簡述

描述一下問題的大概狀況

            程序 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.27Library 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)

解決辦法匯總

  1. 運行程序前使用環境變量 LD_LIBRARY_PATH=xxxx 指定庫加載路徑(間接引用的庫在里面)
    LD_LIBRARY_PATH=./lib bin/autoover
    
  2. 對直接引用的庫,設置 rpath 用於查找間接引用的庫(可能有多級間接)
  3. 將間接引用的庫所在的目錄路徑添加到 /etc/ld.so.conf 中,並使用 ldconfig 更新下 /etc/ld.so.cache
  4. 將間接引用的庫,添加到程序的依賴列表里面去
    patchelf --add-needed libproj.so.20 bin/autoover
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM