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.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