程序员的自我修养第七章读书笔记-动态链接

动态链接的确有很多优势,比静态链接要灵活得多,但它是以牺牲一部分性能为代价的。据统计ELF程序在静态链接下要比动态库稍微快点,大约为1%-%5,当然这取决于程序本身的特性及运行环境等。我们知道动态链接比静态链接慢的主要原因是动态链接下对于全局和静态的数据访问都要进行复杂的GOT定位,然后间接寻址;对于模块间的调用也要先定位GOT,然后再进行间接跳转,如此一来,程序的运行速度必定会减慢。另外一个减慢运行速度的原因是动态库的链接工作在运行时完成,即程序开始执行时,动态链接器都要进行一次链接工作,动态链接器会寻找并转载所需要的共享对象,然后进行符号查找地址重定位等工作,这些工作势必减慢程序的启动速度。这是影响动态链接性能的两个主要问题,现主要介绍优化动态链接性能的一些方法

延迟绑定实现

ELF采用了一种延迟绑定的做法,基本思想就是当函数第一次被调用到时才进行绑定(符号查找、重定位等),如果没有用到则不进行绑定。

ELF使用PLT(Procedure Linkage Table)的方法来实现,这种方法使用了一些很精巧的指令序列来完成。

动态链接相关结构

动态链接情况下,可执行文件的装载和静态链接情况基本一样。首先操作系统会读取可执行文件的头部,检查文件的合法性,然后从头部中的“Program Header”中的每个”Segment”的虚拟地址、文件地址和属性,并将它们映射到进程虚拟空间的相应位置,这些步骤跟静态链接情况下的装载基本无异。

在静态链接情况下,操作系统接着就可以把控制权转交给可执行文件的入口地址,然后程序开始执行,一切看起来非常直观。但是在动态链接情况下,操作系统还不能在装载完可执行文件之后就把控制权转交给可执行文件,因为我们知道可执行文件依赖于很多共享对象。这时候,可执行文件里对于很多外部符号的引用还处于无效地址的状态,即还没有跟相应的共享对象中的实际位置链接起来。所以在映射可执行文件之后,操作系统会先启动一个动态链接器

在Linux下,动态链接器ld.so实际上是一个共享对象,操作系统同样通过映射的方式将它加载到进程的地址空间中。操作系统在加载完动态链接器之后,就将控制权交给动态链接器的入口地址(与可执行文件一样,共享对象也有入口地址)。当动态链接器得到控制权之后,它开始执行一系列自身的初始化操作,然后根据当前的环境参数,开始对可执行文件进行动态链接工作。当所有动态链接工作完成之后,动态链接器会将控制权交给可执行文件的入口地址,程序开始正式执行。

1.“.interp”段

“.interp”(interpreter)段是ELF可执行文件中的一个段。里边保存的可执行文件所需要的动态链接器的路径,一般为”/lib/ld-linux.so.2”.这个路径一般是个软链接。指向“/lib/ld-2.6.1.so”。动态链接器在Linux下是Glibc的一部分。

2.“.dynamic”段

动态链接ELF中最重要的结构应该是”.dynamic“段,这个段里面保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化的地址等。”.dynamic”段可以看成是动态链接下ELF文件的“文件头”

另外Linux还提供了一个命令用来查看一个程序主模块或一个共享库依赖于哪些共享库:ldd.

3.动态符号表

动态符号表“.dynsym”与静态符号表”.symtab”不同的是,它只保存了与动态链接相关的符号,对于那些模块内部的符号,比如模块私有变量则不保存。很多时候动态链接的模块同时拥有“.dynsym”和”.symtab”两个表,“.symtab”中保存了所有符号,包括”.dynsym”中的符号。

与“.symtab”类似,动态符号表也需要一些辅助的表,比如用于保存符号名的字符串表。静态链接时叫做符号字符串表”.strtab”(String Table)
,在这里就是动态符号字符串表“.dynstr”(Dynamic String Table);由于动态链接下,我们需要在程序运行时查找符号,为了加快符号的查找过程,往往还有辅助的符号哈希表

动态符号表的结构与静态链接的符号表几乎一样,我们可以简单地将导入函数看作是对其他目标文件中函数的引用,把导出函数看作是本目标文件定义的函数就可以了。

4.动态链接重定位表

共享对象需要重定位的主要原因是导入符号的存在。动态链接下,无论是可执行文件或共享对象,一旦它依赖于其他共享对象,也就是说有导入的符号时,那么它的代码或数据中就会有对于导入符号的引用。在编译时这些导入符号的地址未知,在静态链接中,这些位置的地址引用在最终链接时被修正。但是在动态链接中,导入符号的地址在运行时才确定,所以需要在运行时将这些导入符号的引用修正,即需要重定位。

动态链接重定位表–动态链接重定位相关结构

共享对象的重定位与我们在前面“静态链接”中分析过的目标文件的重定位十分类似。唯一的区别是目标文件的重定位是在静态链接时完成的,而共享对象的重定位是在装载时完成的。

在静态链接中,目标文件里面包含有专门用于表示重定位信息的重定位表,比如“.rel.text”表示时代码段的重定位表,”.rel.data”时数据段的重定位表。动态链接的文件中,也有类似的重定位表分别叫做“.rel.dyn”和”.rel.plt”,它们分别相当于“.rel.text”,和”.rel.data”。”.rel.dyn”实际上时对数据引用的修正,它所修正的位置位于“.got”以及数据段;而”.rel.plt”是对函数引用的修正,它所修正的位置位于“.got.plt”。

5.动态链接时进程堆栈初始化信息

站在动态链接器的角度看,当操作系统把控制权交给它的时候,它将开始链接工作,那么至少它需要知道关于可执行文件和本进程的一些信息,比如可执行文件有几个段、每个段的属性、程序的入口地址(因为动态链接器到时候需要把控制权交给可执行文件)等。这些信息往往由操作系统传递给动态链接器,保存在进程的堆栈里面。另外,进程初始化的时候,堆栈里面保存了关于进程执行环境和命令行参数等信息。事实上,堆栈里面还保存了动态链接器所需要的一些辅助信息数组(Auxiliary Vector)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tingzhushaohua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值