第八章:Linux共享库的组织

1.共享库版本不同,可能引起ABI发生变化,从而影响程序的正常运行。因此各个系统会有自己的一套共享库版本命名规则。Linux的为
libname.so.x.y.z
x:主版本号,y:次版本号,z:发布版本号
不同主版本之间共享库差异大,互不兼容。
次版本之间仅是添加了新的符号,互相兼容。
发布版本之间修改了接口的bug,互相兼容。

2.程序依赖的共享库由.dynamic段指出,若指出完整版本(主次发布都有)的共享库名字,则每次更新共享库必须保存之前的版本,会造成磁盘和内存(会同时装载不同版本的共享库)的压力巨大,因此Linux制订了SO-NAME命名规则。
libname.so.x
x为共享库的主版本号。
系统会给每个共享库所在的目录创建一个与SO-NAME同名的软连接,指向最新版本的共享库(主版本号相同),可见,一个共享库若安装了多个主版本,则会有多个对应的软连接。

Linux中有一个工具“ldconfig”,当系统安装或更新一个共享库时,会遍历默认共享库目录,更新或创建相应的软连接。

gcc -lXXX 表示链接XXX共享库,系统会查找最新版本的XXX库(往往由-L指定)。其可以是动态的或是静态的,使用-lc 会根据输出文件的情况选择合适版本(静态或是动态)的库。

3.SO-NAME引发的次版本号交会问题及符号版本机制解决方案

若应用程序依赖lib.so的2.5版本而系统中只有2.3版,因为SO-NAME相同程序会正常运行,但是若引用到了2.5添加的符号则会异常退出,这就是次版本号交会问题。

符号版本就是在这个前提下引入的。共享库构建的链接过程可以链接编写好的符号版本脚本。脚本里就是一些集合,集合有自己的名称如VERS_1.1,集合里包含共享库定义的符号,这样共享库的符号都有了自己的版本(所属集合名称)。同时集合之间可以有继承关系,如VERS_1.2继承于VERS_1.1,这样共享库构建完成的.so文件就会包含集合的相关内容。
当应用程序在链接lib.so这个共享库时就会在程序的最终输出文件(往往是可执行文件)记录下该应用程序所依赖的共享库最低符号集合版本,注意不是系统拥有的该共享库最高版本。
在程序运行时,动态连接器就可以通过程序记录的它所依赖的所有共享库符号集合版本判断系统安装的对应共享库是否符合要求,若不符合,则阻止程序运行。
符号版本方法是对SO-NAME机制的一种补充。

----Linux的符号版本
Linux下符号版本机制并没有广泛应用,主要使用的是glibc下的一部分共享库。
Linux下对Solaris的符号版本有两种扩展。

除了可以在符号版本脚本中指定符号的版本外,可以在c/c++源代码中通过
asm(".symver symbolInCode,symbolInScript@VERS_1.1")
添加符号到符号标签。

gcc允许同一个符号存在于多个版本的共享库中,在链接层面的一种符号重载机制。
这种重载机制的好处是,可以在少量已有接口的接口或含义修改时,不用升级主版本,即可保证不影响旧版的功能。
asm(".symver newprintf,printf@VERS_1.1")
asm(".symver oldprintf,printf@VERS_1.1")
void oldprintf(){}
void newprintf(){}

4.共享库系统路径
/usr/lib
/lib 下一般是系统本身所需要的库;
/usr/local/lib一般是非系统所需的第三方程序的共享库。

5.共享库的查找过程
共享库的查找是由动态链接器(/lib/ld-linux.so.x)依据任何一个动态链接的模块的“.dynamic”段里由DT_NEED类型的项指出的。
而其值若为绝对路径则依据绝对路径查找,相对路径的话则会在/lib,/usr/lib和/etc/ld.so.conf配置文件指定的目录中查找。而为了共享库的可移植性,其路径往往是相对的。
若ld每次都遍历这些目录,则会非常耗时。因此前文提到的有一个叫ldconfig程序,除了前文说的负责共享库的SO-NAME软链接,它还会收集这些SO-NAME已特殊数据结构存储在/etc/ld.so.cache,从而加快查找速度。若ld在cache里没有找到,则会遍历/lib,/usr/lib,无则失败。
因此理论上每次增删更新共享库都应该运行一次ldconfig,许多软件包安装好共享库后都会调用它。

6.3个与动态链接相关的环境变量
LD_LIBRARY_PATH
改变ld查找共享库的过程,会先查询由该变量指定的路径在默认查找。方便共享库开放的调试和测试。不应被滥用。且会影响gcc编译查找库的路径,效果相当于gcc的-L参数。
LD_LIBRARY_PATH=/home/mary:/home/ben /bin/ls

LD_PRELOAD
会提前装载指定的共享库,比前面那个变量优先级高。由于全局符号介入机制的存在,可以方便改写标准C库的某个或某几个函数而不影响其他函数,调试和测试有用。
(/etc/ld.so.preload与该变量作用一样)

LD_DEBUG
根据值的不同,动态链接器会在运行时打印出各种有用的信息。
LD_DEBUG=files ./helloworld.out

7.共享库的创建
gcc编译时,除了的-fPIC,-shared参数,还有传给链接器的-W1,-so-name,mysoname参数以指定输出共享库的SO-NAME。
不用这个参数的话,该共享库默认没有SO-NAME,即使用ldconfig更新SO-NAME软链接,对该共享库没有效果。

注意事项:
不要将输出共享库的符号和调试信息去掉,也不要使用gcc的"-fomit-frame-pointer"去掉栈帧选项,会影响共享库调试。
测试新共享库而不想影响现有程序运行。除了使用LD_LIBRARY_PATH,还可以使用ld的"-rpath"选项指定目标程序的共享库查找路径。
默认情况下,链接器产生的可执行文件中,只会将主模块被其他共享模块引用到的符号放在动态符号表中,以减少动态符号表大小。而这会造成一种情况是,当程序事业dlopen动态加载共享模块,而它需要引用主模块的符号时,而因为该符号没有在动态符号表中从而反向引用失败。链接器提供"-export-dynamic"参数将可执行文件(主模块)中所有全局符号导出到动态符号表中,以防出现上述问题。

对于发布版本来说,符号作用不大可以使用“strip”工具清除掉共享库或可执行文件的所有符号和调试信息或ld的“-s”或“-S”参数。
strip libfoo.so

需要系统root权限的方法:
将共享库复制到/lib,/usr/lib等系统目录下,运行ldconfig即可。
不需要root权限的方法:
使用ldconfig建立SO-NAME(以供动态链接器查找)
ldconfig -n shared_Library_directory
且编译程序时也需要指定共享库的位置(-L和-l,或-rpath参数(静态连接器需要)

10.gcc中共享库的构造和析构函数
attribute((constructorx))
attribute((destructorx))指定,x指定优先级,可以不写。构造函数中x越小优先级越高,析构函数中x越小优先级越低,符合先申请的资源后释放原则。
这种方式下共享库的构建不可以使用gcc的“-nostartfiles”或“-nostdlib”参数 因为这些构造函数和析构函数是在系统默认的标准运行库或启动文件里被运行的。没有这些辅助结果,它们不会运行。

11.共享库脚本
可以将几个现有共享库组合起来,从用户的角度来看就是一个新的共享库。
GROUP(/lib/libc.so.6 /lib/libm.so.2)
语法命令方面跟链接脚本没有什么不同,作用也相似,即将一个或多个输入文件以一定格式经过变换后形成一个输出文件,所以共享库脚本也叫做动态链接脚本,链接过程是在运行时完成的。

程序员自我修养笔记:第八章相关推荐

  1. 程序员自我修养笔记:第九章

    第九章 Windows下的动态链接 1.相当于Linux下的共享库,Window系统大量采用dll机制,dll更加强调模块化,经常可以看到Windows平台大量的大型软件都通过升级dll的形式自我完善 ...

  2. Linux下main函数启动过程【程序员自我修养笔记】【自用】

    1. 入口函数和程序初始化 1.1 程序从main开始吗? 当程序执行到main函数的第一行时,很多事情都已经完成了: [证1]如下是一段C语言代码: 代码中可以看到,在程序刚刚执行到main的时候, ...

  3. 程序员自我修养笔记1

    Interface的概念:计算机中,每个层次之间的通信协议(这里的接口区别于Java等编程语言的接口). 内存管理 内存的分段管理: 基本思路是把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址 ...

  4. 程序员自我修养笔记:第12章

    第 12章 系统调用原理 1.普通应用程序运行在用户态下,唯一进入内核态的方式就是通过系统中断.中断分硬件中断和软件中断,软件中断一般以int 0x80号进入系统中断处理程序.中断号很好,因此一般中断 ...

  5. 《程序员自我修养》第七章读书笔记

    书还是接上回,本篇主要对第七章的相关内容进行总结.第七章主要对动态链接的相关内容进行分析. 7.1 为什么要动态链接 既然要对动态链接进行分析,首先应对动态链接出现的原因进行一个简单的分析.动态链接从 ...

  6. 程序员自我修养之链接

    我最近在看PE文件,稍后可能需要dll这些所以顺带看看链接.太久不看这些书,你问我链接是干什么的,我可能会说就是分模块时候用啊,因为一个项目有很多模块,不能写在同一个文件下,所以要把它们链接起来,链接 ...

  7. 程序员自我修养》系统调用与API

    什么是系统调用 在现代的操作系统里,程序运行的时候,本身是没有权利访问多少系统资源的.由于系统有限的资源有可能被多个不同的应用程序同时访问,因此,如果不加以保护,那么各个应用程序难免产生冲突.所以现代 ...

  8. 一个Java工程师的自我修养_程序员自我修养

    毕业N年,每个人在能力跑道上,有了或大或小的差距.有些人一直在重复的劳动,有些人却能从中总结和解决问题.通过成长日活动,我们或许可以探讨下,怎样共同成长.共同前行,跟"勤奋战术掩盖下的战略懒 ...

  9. 程序员自我修养阅读笔记——运行库

    主要关注程序的启动过程. 1 入口函数和程序初始化 1.1 程序真正的入口   通常写代码时,我们认为程序的入口是main函数,但是实际上有一些现象值得我们怀疑该结论是不是正确的.比如全局变量的初始化 ...

最新文章

  1. Mobileye采用单目摄像头做ADAS太不精确
  2. 26.Azure备份服务器(下)
  3. CTFshow 爆破 web22
  4. ZOJ - 2972 Hurdles of 110m(记忆化搜索/动态规划)
  5. python 知乎关系图谱_5000行python代码+可视化60W数据,告诉你知乎用户不为人知的事...
  6. 凸集、锥、凸锥、正常锥的辨析
  7. 51单片机中模拟IIC的代码编写
  8. 打卡项目php,Thinkphp框架早起打卡项目(深蓝引擎Z)趣步模式+完整数据+全开源源码...
  9. 批量更改文件的后缀名
  10. ***WIN2003 PHP服务器的另类技术
  11. 从孤独里找到一束光-2021
  12. 北大三日游游记(北大信科暑期课堂)
  13. android 远吗编译 刷机 小米,小米10/小米10 Pro系统源代码现已开源,能刷机才是为发烧而生...
  14. nginx日志统计分析
  15. 企业网站被黑客攻击了怎么办
  16. svchost.exe占用内存过高
  17. 6-1 求二叉树高度
  18. 关于斩波频率一点理解
  19. 门店管理系统怎么挑选?请收下这份避坑指南!
  20. 怎么才可以在jsp界面中插入图片

热门文章

  1. 数据可视化发现[吃鸡]秘密
  2. matplotlib简单应用
  3. Android动态加载布局
  4. 荣耀手机是否装鸿蒙,荣耀手机也能升鸿蒙!这5款机型用户有福了
  5. webpack入门学习手记(三)
  6. 流量卡之家:中国电信叫停不限量套餐 狂欢路上的清醒者
  7. java生成随机数组_JAVA生成随机数组10个数字并求和
  8. python alist(-1、2)_alist = list(range(2, 11, 2))
  9. openGauss数据库开发指导手册(上)
  10. android progressdialog 背景色,怎么在android中利用ProgressDialog实现一个加载效果