本文介绍: 我们输出结果可以知道动态链接器只加载/etc/ld.so.cache配置文件,并没有加载/etc/ld.so.conf配置文件。-Wl,-rpath编译阶段修改可执行程序rpath参数,但是往往我们工程中是不太确认最终的集成路径的。我们需要将动态库放到对应路径即可我们工作中经常会遇到动态库链接问题,因为正常的方式并不能满足我们场景知道可执行程序依赖哪些动态库口,动态链接器就需要去找这些动态库,查找方式主要有以下四种。是我们最最常用方式,大部分情况下,我们使用方式即可

引文

        我们在工作中经常会遇到动态库链接问题,因为正常的方式并不能满足我们的场景常见问题可以总结如下

        针对不同场景,根据链接器的加载逻辑,进行相应的处理

程序加载流程

        根据专栏程序员的自我修养》中的【程序员的自我修养02】初识ELF文件格式-CSDN博客可知,可执行文件运行流程简述如下

  1. 操作系统加载ELF的文件头。以检查文件格式操作权限属性
  2. 根据文件头中的段表地址定位各个内容。将各个映射虚拟地址中。
  3. 查找依赖的动态库并加载
  4. 进入文件头中的Entry point address。执行业代码

        其中链接查找依赖的动态库并加载这个流程实质很复杂,后续我会在《程序员的自我修养》专栏详细介绍本文我们只关心链接如何去找动态库

问:链接器如何知道可执行程序依赖哪些动态库呢?

答:ELF文件中有一个.dynamic这个里面保存了动态链接器所需要基本信息比如依赖哪些动态库动态链接符号表的位置动态链接重定位表的位置共享对象初始化代码地址等。我们可以通过readelfd main命令查看该段内容如下

Dynamic section at offset 0xda8 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [liba.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [/home/yihua/]
 0x000000000000000c (INIT)               0x5b8
 0x000000000000000d (FINI)               0x794
 0x0000000000000019 (INIT_ARRAY)         0x200d98
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200da0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x298
 0x0000000000000005 (STRTAB)             0x3f0
 0x0000000000000006 (SYMTAB)             0x2d0
 0x000000000000000a (STRSZ)              182 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x200fb8
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x5a0
 0x0000000000000007 (RELA)               0x4e0
 0x0000000000000008 (RELASZ)             192 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x0000000000000018 (BIND_NOW)
 0x000000006ffffffb (FLAGS_1)            Flags: NOW PIE
 0x000000006ffffffe (VERNEED)            0x4c0
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x4a6
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

        如上可知:main执行程序依赖 两个动态库 liba.solibc.so.6。其中黄色字体0x000000000000000f (RPATH)              Library rpath: [/home/yihua/]表示链接器查找动态库的路径,其优先级最高。需要我们关注。其它的参数暂不考虑

        查看依赖的动态库还可以通过ldd main命令objdump -x main | grep NEEDED命令

知道可执行程序依赖哪些动态库口,动态链接器就需要去找这些动态库,查找的方式主要有以下四种。

        其动态链接器加载顺序分别是rpath –> LD_LIBRARY_PATH –>  /etc/ld.so.conf  –> /lib 、 /usr/lib。

知道四个方式后,我们尝试了解如何使用本文示例代码如下

//a.c
#include<stdio.h>
int a()
{
        printf(“im liba.an”);
}
// main.c
#include<stdlib.h>
#include<stdio.h>
extern int a();
int main()
{
        a();
        return 0;
}

编译

C
yihua@ubuntu:~/test/dynamic$ gcc -FPIC –shared -o liba.so a.c
yihua@ubuntu:~/test/dynamic$ gcc main.c -o main -L. -la

集成:分别将 mainliba.so 放入bin 和lib目录下。

Shell
yihua@ubuntu:~/test/dynamic$ tree
.
├── a.c
├── bin
│   └── main
├── lib
│   └── liba.so
└── main.c

运行

Shell
yihua@ubuntu:~/test/dynamic$ ./bin/main
./bin/main: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory

如上错误,是因为动态链接器没有找到liba.so导致的。可通过以下四种方式修复

-Wl,-rpath

        该方式是通过编译阶段,修改main 可执行程序中的dynamic段达到目的。可查看当前main的dynamic内容

        是没有RPATH参数的。可通过如下编译命令

yihua@ubuntu:~/test/dynamic$ gcc -FPIC –shared -o liba.so a.c
yihua@ubuntu:~/test/dynamic$ gcc main.c -o main -L. -la -Wl,-rpath=/home/yihua/test/dynamic/lib

查看dynamic段内容:

运行

拓展:

        -Wl,-rpath编译阶段修改执行程序rpath参数,但是往往我们在工程中是不太确认最终的集成路径的。可以集成时,采用chrpath命令修改执行程序rpath参数。如下

yihua@ubuntu:~/test/dynamic$ chrpath –r ./lib/ bin/main    //修改rpath
bin/main: RPATH=/home/yihua/
bin/main: new RPATH: ./lib/
yihua@ubuntu:~/test/dynamic$ chrpath -l bin/main   //查看rpath
bin/main: RPATH=./lib/

完结撒花~~~

LD_LIBRARY_PATH

        环境变量LD_LIBRARY_PATH是我们最最常用的方式,大部分情况下,我们使用该方式即可使用方式如下

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/yihua//test/dynamic/lib编译如下

yihua@ubuntu:~/test/dynamic$ gcc -FPIC –shared -o liba.so a.c
yihua@ubuntu:~/test/dynamic$ gcc main.c -o main -L. -la

集成:分别将 main 和 liba.so 放入bin 和lib目录下。

运行

完结撒花~~~

/etc/ld.so.conf

        该配置文件系统动态链接器加载的配置文件。我们可以重新创建一个窗口(目的是关闭上述LD_LIBRARY_PATH环境变量)。修改/etc/ld.so.conf文件,如下:

运行

        发现依然没有找到动态库,这是为什么呢?我们可以通过strace ./bin/main命令查看程序的加载流程输出如下:

yihua@ubuntu:~/test/dynamic$ strace ./bin/main
execve(“./bin/main”, [“./bin/main”], 0x7ffdfd030ae0 /* 26 vars */) = 0
brk(NULL)                               = 0x5643c65c3000
access(“/etc/ld.so.nohwcap“, F_OK)      = -1 ENOENT (No such file or directory)
access(“/etc/ld.so.preload“, R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/etc/ld.so.cache“, O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=104673, …}) = 0
mmap(NULL, 104673, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff91b2ed000
close(3)                                = 0
access(“/etc/ld.so.nohwcap“, F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linuxgnu/tls/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linuxgnu/tls/haswell/x86_64″, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linuxgnu/tls/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linuxgnu/tls/haswell“, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linuxgnu/tls/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linuxgnu/tls/x86_64″, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linuxgnu/tls/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linux-gnu/tls“, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linux-gnu/haswell/x86_64″, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linux-gnu/haswell”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linux-gnu/x86_64″, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64-linux-gnu”, {st_mode=S_IFDIR|0755, st_size=16384, …}) = 0
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64″, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/tls/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu/tls/haswell”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/tls/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu/tls/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/tls/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu/tls”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu/haswell/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu/haswell”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64-linux-gnu/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64-linux-gnu”, {st_mode=S_IFDIR|0755, st_size=81920, …}) = 0
openat(AT_FDCWD, “/lib/tls/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/tls/haswell/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/tls/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/tls/haswell”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/tls/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/tls/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/tls/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/tls”, 0x7fffcd77f4e0)        = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/haswell/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/haswell”, 0x7fffcd77f4e0)    = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib/x86_64”, 0x7fffcd77f4e0)     = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/lib/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/lib”, {st_mode=S_IFDIR|0755, st_size=4096, …}) = 0
openat(AT_FDCWD, “/usr/lib/tls/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/tls/haswell/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/tls/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/tls/haswell”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/tls/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/tls/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/tls/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/tls”, 0x7fffcd77f4e0)    = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/haswell/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/haswell/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/haswell/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/haswell”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/x86_64/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib/x86_64”, 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/usr/lib/liba.so”, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat(“/usr/lib”, {st_mode=S_IFDIR|0755, st_size=4096, …}) = 0
writev(2, [{iov_base=”./bin/main”, iov_len=10}, {iov_base=”: “, iov_len=2}, {iov_base=”error while loading shared libra”…, iov_len=36}, {iov_base=”: “, iov_len=2}, {iov_base=”liba.so”, iov_len=7}, {iov_base=”: “, iov_len=2}, {iov_base=”cannot open shared object file”, iov_len=30}, {iov_base=”: “, iov_len=2}, {iov_base=”No such file or directory”, iov_len=25}, {iov_base=”n”, iov_len=1}], 10./bin/main: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory
) = 117
exit_group(127)                         = ?
+++ exited with 127 +++

        我们从输出结果,可以知道,动态链接器只加载/etc/ld.so.cache配置文件,并没有加载/etc/ld.so.conf配置文件。因此,我们需要通过ldconfig命令更新ld.so.cache文件内容。

如下:

完结撒花~~~

系统默认路径

        系统默认路径即系统存放动态库的地方,一般为/lib、/usr/lib。我们只需要将动态库放到对应的路径下即可。

        重新打开一个窗口执行如下命令运行

        完结撒花~~~

总结

        综上所述,我们知道了动态链接库寻找动态库的四种方式,其中:

        系统默认路径和/etc/ld.so.conf需要系统权限,大部分情况是不能进行修改的。若有相应权限,可以优先使用该方式。

        环境变量LD_LIBRARY_PATH可以解决我们大部分场景比如引文中的第一个场景。但是它并不解决动态库重名的问题比如引文中的第二个场景。这时,我们就可以采用-Wl,-rpath 编译选项chrpath修改可执行程序的rpath参数。

        希望本文能够对您有所帮助,还请三连表示支持哦~~~

原文地址:https://blog.csdn.net/xieyihua1994/article/details/134782826

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_38538.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注