Linux下so动态库查看与运行时搜索路径的设置(1)

最全的Linux教程,Linux从入门到精通

======================

  1. linux从入门到精通(第2版)

  2. Linux系统移植

  3. Linux驱动开发入门与实战

  4. LINUX 系统移植 第2版

  5. Linux开源网络全栈详解 从DPDK到OpenFlow

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。

需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

注:下文用so代替动态链接库文件。


// 举个例子

~$ ldd /bin/ls

    linux-vdso.so.1 =>  (0x00007ffe06386000)

    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fd686b54000)

    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd68678b000)

    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007fd68651a000)

    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd686316000)

    /lib64/ld-linux-x86-64.so.2 (0x0000563411315000)

    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd6860f9000)

问题

如果ldd命令没有找到对应的共享库文件和其具体位置?

可能是两种情况引起的:

1)共享库没有安装在该系统中;

2)共享库保存在/etc/ld.so.conf文件列出的搜索路径之外的位置。

通常情况下,许多开源代码的程序或函数库都会默认将在即安装到/usr/local目录下的相应位置(如:/usr/local/bin 或 /usr/local/lib)以便于系统自身的程序或函数库相区别。而许多linux系统的/ect/ld.so.conf 文件中默认又不包含 /usr/local/lib 。因此出现安装了共享库,但是却无法找到共享库的情况。

解决办法:

检查/etc/ld.so.conf文件,如果其中缺少/usr/local/lib 目录,就添加进去;

注意:在修改了/etc/ld.so.conf 文件或者在系统中安装了新的函数库之后,需要运行命令 ldconfig ,该命令用来刷新系统的共享库缓存,即 /etc/ld.so.cache 文件。为了减少共享库系统的库搜索时间,共享库系统维护了一个共享库so名称的缓存文件 /etc/ld.so.cache 。 因此,在安装新的共享库之后,一定要运行 ldconfig刷新该缓存。


strace命令

========

strace命令可以跟踪进程执行过程


#include <iostream>

using namespace std;

int main() 

{

    cout << "test" << endl;

}

$ g++ -o main main.cpp


$ strace main

strace: Can't stat 'main': No such file or directory

$ strace ./main

execve("./main", ["./main"], [/* 30 vars */]) = 0

brk(NULL)                               = 0x20c6000

access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f195056b000

access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)

open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

fstat(3, {st_mode=S_IFREG|0644, st_size=85573, ...}) = 0

mmap(NULL, 85573, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1950556000

close(3)                                = 0

access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

open("/usr/lib/x86_64-linux-gnu/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3

read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \235\10\0\0\0\0\0"..., 832) = 832

fstat(3, {st_mode=S_IFREG|0644, st_size=1566440, ...}) = 0

mmap(NULL, 3675136, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f194ffc6000

mprotect(0x7f1950138000, 2097152, PROT_NONE) = 0

mmap(0x7f1950338000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x172000) = 0x7f1950338000

mmap(0x7f1950344000, 13312, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1950344000

close(3)                                = 0

access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3

read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832

fstat(3, {st_mode=S_IFREG|0755, st_size=1864888, ...}) = 0

mmap(NULL, 3967488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f194fbfd000

mprotect(0x7f194fdbd000, 2093056, PROT_NONE) = 0

mmap(0x7f194ffbc000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bf000) = 0x7f194ffbc000

mmap(0x7f194ffc2000, 14848, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f194ffc2000

close(3)                                = 0

access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

open("/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3

read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0V\0\0\0\0\0\0"..., 832) = 832

fstat(3, {st_mode=S_IFREG|0644, st_size=1088952, ...}) = 0

mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1950555000

mmap(NULL, 3178744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f194f8f4000

mprotect(0x7f194f9fc000, 2093056, PROT_NONE) = 0

mmap(0x7f194fbfb000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x107000) = 0x7f194fbfb000

close(3)                                = 0

access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

open("/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3

read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p*\0\0\0\0\0\0"..., 832) = 832

fstat(3, {st_mode=S_IFREG|0644, st_size=89696, ...}) = 0

mmap(NULL, 2185488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f194f6de000

mprotect(0x7f194f6f4000, 2093056, PROT_NONE) = 0

mmap(0x7f194f8f3000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7f194f8f3000

close(3)                                = 0

mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1950554000

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1950552000

arch_prctl(ARCH_SET_FS, 0x7f1950552740) = 0

mprotect(0x7f194ffbc000, 16384, PROT_READ) = 0

mprotect(0x7f194fbfb000, 4096, PROT_READ) = 0

mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1950551000

mprotect(0x7f1950338000, 40960, PROT_READ) = 0

mprotect(0x600000, 4096, PROT_READ)     = 0

mprotect(0x7f195056d000, 4096, PROT_READ) = 0

munmap(0x7f1950556000, 85573)           = 0

brk(NULL)                               = 0x20c6000

brk(0x20f8000)                          = 0x20f8000

fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0

write(1, "test\n", 5test

)                   = 5

exit_group(0)                           = ?

+++ exited with 0 +++



因此,可以明显的观察到Linux操作系统把程序(program)变成进程(process),要经过三个步骤:

  1. fork进程,在内核创建进程相关内核想,加载进程可执行文件

  2. 查找依赖的so,并把它映射到虚拟地址空间

  3. 初始化程序变量


动态库链接时搜索路径和运行时搜索路径

==================

举个例子:

编写一个简单的动态链接库文件


// add.h

#ifndef __ADD_H__

#define __ADD_H__

int add(int a, int b);

#endif

// add.cpp

int add(int a, int b)

{

    return a+b;

}

// main.cpp

#include <iostream>

using namespace std;

int main() 

{

    int sum = add(3, 2);

    cout << sum << endl;

}

$ g++ -fPIC -shared -o libadd.so add.cpp



// 注:这里-ladd不能在main.cpp之前,否则连接器在链接main.o时找不到libadd.so

$ g++ -o main -I. -L.  main.cpp -ladd

$ ./main

./main: error while loading shared libraries: libadd.so: cannot open shared object file: No such file or directory

运行时报错,找不到依赖的so文件


$ ldd main

    linux-vdso.so.1 =>  (0x00007fffaef9b000)

    libadd.so => not found

    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007feb12d01000)

    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007feb12937000)

    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007feb1262e000)

    /lib64/ld-linux-x86-64.so.2 (0x000055794a8c2000)

    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007feb12418000)




一般使用设置LD_LIBRARY_PATH这个环境变量来解决


$ LD_LIBRARY_PATH=.

$ export LD_LIBRARY_PATH

$ ./main

5

使用export只对当前shell有效,开启的其他shell必须重新设置。可以把export LD_LIBRARY_PATH=xxx写到~/.bashrc对当前用户生效或写入/etc/profile对所有用户生效。可参考:linux设置环境变量

使用-L. -ladd 是一种设置相对路径方法,也可以使用绝对路径链接方法


// libadd.so 在main.cpp 后,否则报链接错误

$ g++ -o main main.cpp /home/jack/workpace/libadd.so

$ main

$ ldd main

$ ldd main

    linux-vdso.so.1 =>  (0x00007fff3ebb7000)

    /home/jack/workspace/libadd.so (0x00007fde18144000)

    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fde17dad000)

    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fde179e3000)

    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fde176da000)

    /lib64/ld-linux-x86-64.so.2 (0x0000562f1c354000)

    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fde174c4000)




搜索路径分两种:

  • 链接时搜索路径

    使用-L. 就属于链接时搜索路径,给ld程序链接时使用的搜索动态库路径;

  • 运行时搜索路径

    而使用LD_LIBRARY_PATH属于运行时搜索路径。可以参考5.2 动态连接时执行时的搜索路径


介绍另一种运行时搜索路径的方式,使用-rpath或-R连接选项来指定运行时搜索路径,其优先级高于LD_LIBRARY_PATH。可参考5.2 动态连接时执行时的搜索路径


$ g++ -o main -L. -I. main.cpp -Wl,-rpath .

// 或

$ g++ -o main -L. -I. main.cpp -Wl,-rpath=.

// 或

$ g++ -o main -L. -I. main.cpp -Wl,-R.

$ ldd main

    linux-vdso.so.1 =>  (0x00007fff4a7f0000)

    libadd.so => ./libadd.so (0x00007fa770bbc000)

    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa770825000)

    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa77045b000)

    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa770152000)

    /lib64/ld-linux-x86-64.so.2 (0x0000557d67d4b000)

    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa76ff3c000)

由上面可知: libadd.so => ./libadd.so (0x00007fa770bbc000)


$ readelf -d main



Dynamic section at offset 0xdf8 contains 27 entries:

  Tag        Type                         Name/Value

 0x0000000000000001 (NEEDED)             Shared library: [libadd.so]

 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]

 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

 0x000000000000000f (RPATH)              Library rpath: [.]

 0x000000000000000c (INIT)               0x400778

 0x000000000000000d (FINI)               0x400a44

 0x0000000000000019 (INIT_ARRAY)         0x600dd8

 0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)

 0x000000000000001a (FINI_ARRAY)         0x600de8

 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)

 0x000000006ffffef5 (GNU_HASH)           0x400298

 0x0000000000000005 (STRTAB)             0x4004a8

 0x0000000000000006 (SYMTAB)             0x4002e0

 0x000000000000000a (STRSZ)              371 (bytes)

 0x000000000000000b (SYMENT)             24 (bytes)

 0x0000000000000015 (DEBUG)              0x0

 0x0000000000000003 (PLTGOT)             0x601000

 0x0000000000000002 (PLTRELSZ)           192 (bytes)

 0x0000000000000014 (PLTREL)             RELA

 0x0000000000000017 (JMPREL)             0x4006b8

 0x0000000000000007 (RELA)               0x400688

 0x0000000000000008 (RELASZ)             48 (bytes)

 0x0000000000000009 (RELAENT)            24 (bytes)

 0x000000006ffffffe (VERNEED)            0x400648

 0x000000006fffffff (VERNEEDNUM)         2

 0x000000006ffffff0 (VERSYM)             0x40061c

 0x0000000000000000 (NULL)               0x0



由上面可知:Library rpath: [.] 表示运行时库的搜索路径。

因此,libadd.so的路径已经被写入了可执行文件中。注意:这里写入的是libadd.so的相对路径,当然也可以写入绝对路径。


$ g++ -o main -L. -I. main.cpp -Wl,-rpath=/home/jack/workspace -ladd

$ ldd main

    linux-vdso.so.1 =>  (0x00007ffeed3b3000)

    libadd.so => /home/jack/workspace/libadd.so (0x00007ff2bc9e6000)


为了做好运维面试路上的助攻手,特整理了上百道 **【运维技术栈面试题集锦】** ,让你面试不慌心不跳,高薪offer怀里抱!

这次整理的面试题,**小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。**

![](https://img-blog.csdnimg.cn/img_convert/cc356273ae961080089ca3ffa92b7085.png)

本份面试集锦涵盖了

*   **174 道运维工程师面试题**
*   **128道k8s面试题**
*   **108道shell脚本面试题**
*   **200道Linux面试题**
*   **51道docker面试题**
*   **35道Jenkis面试题**
*   **78道MongoDB面试题**
*   **17道ansible面试题**
*   **60道dubbo面试题**
*   **53道kafka面试**
*   **18道mysql面试题**
*   **40道nginx面试题**
*   **77道redis面试题**
*   **28道zookeeper**

**总计 1000+ 道面试题, 内容 又全含金量又高**

*   **174道运维工程师面试题**

> 1、什么是运维?

> 2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?

> 3、现在给你三百台服务器,你怎么对他们进行管理?

> 4、简述raid0 raid1raid5二种工作模式的工作原理及特点

> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?

> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?

> 7、Tomcat和Resin有什么区别,工作中你怎么选择?

> 8、什么是中间件?什么是jdk?

> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?

> 10、什么叫CDN?

> 11、什么叫网站灰度发布?

> 12、简述DNS进行域名解析的过程?

> 13、RabbitMQ是什么东西?

> 14、讲一下Keepalived的工作原理?

> 15、讲述一下LVS三种模式的工作过程?

> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

> 17、如何重置mysql root密码?

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?

> 3、现在给你三百台服务器,你怎么对他们进行管理?

> 4、简述raid0 raid1raid5二种工作模式的工作原理及特点

> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?

> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?

> 7、Tomcat和Resin有什么区别,工作中你怎么选择?

> 8、什么是中间件?什么是jdk?

> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?

> 10、什么叫CDN?

> 11、什么叫网站灰度发布?

> 12、简述DNS进行域名解析的过程?

> 13、RabbitMQ是什么东西?

> 14、讲一下Keepalived的工作原理?

> 15、讲述一下LVS三种模式的工作过程?

> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

> 17、如何重置mysql root密码?

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值