• 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 跟踪执行下 ,查看查找 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
      
  • 相关阅读:
    硬盘的结构和介绍,硬盘MBR详细介绍(超详细彩图)
    websocket协议学习
    Qt4可以使用trUtf8函数,其内容可以是中文,也可以是F硬编码
    QString转换为LPTSTR(使用了reinterpret_cast,真是叹为观止,但是也开阔了思路),三篇文章合起来的各种转换方法
    系统高可用
    Visual Studio
    管道是如何建立起来的?
    CLR和.Net对象
    任务调度
    路由与控制器
  • 原文地址:https://www.cnblogs.com/oloroso/p/13224975.html
Copyright © 2020-2023  润新知