科百科
当前位置: 首页 范文大全

动态链接库可以跨平台吗(动态链接的相关结构)

时间:2023-07-31 作者: 小编 阅读量: 1 栏目名: 范文大全

实际上,动态链接器的位置既不是由系统配置指定,也不是由环境参数决定,而是由ELF可执行文件决定。动态链接的符号表示实际上它跟静态链接十分相似,比如前面例子中的Program1程序依赖于Lib.so,引用到了里面的foobar()函数。把这种导入导出关系放到静态链接的情形下,我们可以把它们看作普通的函数定义和引用。为了表示动态链接这些模块之间的符号导入导出关系,ELF专门有一个叫做动态符号表的段用来保存这些信息,这个

在了解了共享对象的绝对地址的引用问题后,我们基本上对动态链接的原理有了初步的了解,接下来的问题是整个动态链接具体的实现过程了。动态链接在不同的系统上有不同的实现方式。ELF的动态链接的实现方式会比PE的简单一点,在这里我们先介绍ELF的动态链接过程在LINUX下的实现,最后我们会专门的章节中介绍PE在Windows下的动态链接过程和它们的区别

我们在前面的章节已经看到,动态链接情况下,可执行文件的装载与静态链接情况基本样。首先操作系统会读取可执行文件的头部检查文件的合法性,然后从头部中的“ Program Header”中读取每个“Segment”的虚拟地址、文件地址和属性,并将它们映射到进程虚拟空间的相应位置,这些步骤跟前面的静态链接情况下的装载基本无异。在静态链接情况下,操作系统接着就可以把控制权转交给可执行文件的入口地址,然后程序开始执行,一切看起来非常直观。

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

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

1. ".interp"段

那么系统中哪个才是动态链接器呢,它的位置由谁决定?是不是所有的*NX系统的动态链接器都位于 /lib/ld.so呢?实际上,动态链接器的位置既不是由系统配置指定,也不是由环境参数决定,而是由ELF可执行文件决定。在动态链接的ELF可执行文件中,有一个专门的段叫做“ Interp”段(“ Interp”是“ Interpreter”(解释器)的缩写)。如果我们使用 objdump工具来查看,可以看到“ .interp”内容:

".interp"的内容很简单,里面保存的就是一个字符串,这个字符串就是可执行的所需要的动态链接器的路径。在LINUX下,可执行文件所需要的动态链接器的路径几乎都是 “/lib/ld-linux.so.2”,其他的*.nix操作系统可能会有不同路径。我们在后面还会介绍到各种环境下的动态链接器的路径。在LINUX系统中,/lib/ld-linux.so.2通常是一个软链接,比如在我的机器中,它指向/lib/ld-linux.so.2,这个才是真正的动态链接器,在linux中,操作系统在对可执行文件所需要的相应动态连接器,即“.interp”段指定的路径的共享对象;

动态链接器在linux下是Glibc的一部分,也就是属于系统库级别的,它的版本号往往跟系统的Glibc库版本号是一样的,比如我的系统中安装的是glibc 2.6.1,那么相应的动态链接库就是/lib/ld-2.6.1.so。当系统中的Glibc库更新或者安装其他版本的时候,/lib/ld-linux.so.2 这个软链接就会指向新的动态链接器,而可执行文件本身不需要修改 ".interp" 中的动态链接器路径来适应系统的升级

我们往往可以用这个命令来查看一个可执行文件所需要的动态链接器的路径,在linux下,往往是这个结果:

readelf -l a.out | grep interpreter[Requeseting program interpreter: /lib/ld-linux.so.2]

而当我们在FreeBSD 4.6.2 下执行这个命令时,结果是:

readelf -l a.out | grep interpreter

64 位的linux下的可执行文件是:

readelf -l a.out | grep interpreter

2. “.dynamic”段

类似于“.interp”这样的段,ELF中还有几个段也是专门用于动态链接的,比如 ".dynamic" 段和 ".dynsym"段等。要了解动态链接器如何完成链接过程,跟前面一样,从了解ELF文件中跟动态链接相关的结构入手将会是一个很好的途径。ELF文件中跟动态链接相关的段有好几个,相互之间的关系也比较复杂,我们先从 ".dynamic" 段入手

动态链接ELF中最重要的结构应该是“ .dynamic”段,这个段里面保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等。“ .dynamic”段的结构很经典,就是我们已经碰到过的ELF中眼熟的结构数组,结构定义在“elf.h”中:

typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un;} Elf32_Dyn;

Elf32_Dyn 结构由一个类型值加上一个附加的数值或指针,对于不同类型,后面附加的数值或者指针有着不同含义。我们这里列举几个比较常见的类型值(这些值都是定义在“elf.h”里面的宏),如表7-2所示:

表7-2中只列出了一部分定义,还有一些不太常用的定义我们就暂且忽略,具体可以参考LSB手册和elf.h的定义。从上面给出的定义来看,“.dynamic”段里面保存的信息有点像elf文件头,只是我们看到的elf文件头中保存的是静态链接时的相关信息,比如静态链接时使用到的符号表、重定位表等,这里换成了动态链接下所使用的相应信息了。所以,“.dynamic”段可以看成动态链接下的ELF文件的“文件头”。使用readelf工具可以查看 ".dynamic" 段的内容

$ readelf -d lib.so

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

动态符号表

为了完成动态链接,最关键的还是所依赖的符号和相关文件的信息。我们知道在静态链接中,有一个专门的段叫做符号表“.symtab”( Symbol Table),里面保存了所有关于该目标文件的符号的定义和引用。动态链接的符号表示实际上它跟静态链接十分相似,比如前面例子中的 Program1程序依赖于 Lib.so,引用到了里面的 foobar()函数。那么对于 Program1来说,我们往往称 ProgramI导入( Import)了 foobar函数, foobar是 Program1的导入函数(import Function):而站在Lib.so的角度来看,它实际上定义了 foobar函数,并且提供给其他模块使用,我们往往称Lib.so导出( Export)了 foobar函数, foobar是Lbso的导出函数(Export Function)。把这种导入导出关系放到静态链接的情形下,我们可以把它们看作普通的函数定义和引用。

为了表示动态链接这些模块之间的符号导入导出关系,ELF专门有一个叫做动态符号表(Dynamic symbol table)的段用来保存这些信息,这个段的段名通常叫做“.dynsym”(Dynamic Symbol)。与 “.sysmtab”不同的是,".dynsym" 只保存了与动态链接相关的符号,对于那些模块内部的符号,比如模块私有变量则不保存。很多时候动态链接的模块同时拥有“.dynsym” 和 ".sysmtab"中往往保存了所有符号,包括 “.dynsym” 中的符号

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

readelf -sD lib.so

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

3. 动态链接重定位表

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

我们在前面地址无关章节中也提到过,动态链接的可执行文件使用的是PIC方法,但这不能改变它需要重定位的本质。对于动态链接来说,如果一个共享对象不是以PIC模式编译的,那么毫无疑问,它是需要在装载时被重定位的:如果一个共享对象是PIC模式编译的,那么它还需要再装载时进行重定位吗? 是的,PIC的共享对象也是需要重定位的;

对于使用PIC技术的可执行文件或共享对象来说,虽然它们的代码段不需要重定位(因为地址无关),但是数据段还包含了绝对地址的引用,因为代码段中绝对地址相关的部分被分离了出来,变成了GOT,而GOT实际上是数据段的一部分。除了GOT以外,数据段还可能包含绝对地址引用,我们在前面的章节中已经举例过了。

动态链接重定位的相关结构

共享对象的重定位与我们在前面“静态链接”中分析过的目标文件的重定位十分类似,唯一有区别的是目标文件的重定位是在静态链接时完成的,而共享对象的重定位是在装载时完成的。在静态链接中,目标文件里面包含有专门用于表示重定位信息的重定位表,比如“rel.text”表示是代码段的重定位表,“rel.data”是数据段的重定位表。

动态链接的文件中,也有类似的重定位表分别叫做“ .rel.dyn”和“.rel. plt”,它们分别相当于“.rel.text”和“.rel.data”“.rel .dyn”实际上是对数据引用的修正,它所修正的位置位于“.got”以及数据段;而“ .rel.plt”是对函数引用的修正,它所修正的位置位于“.got.plt”。我们可以使用 readelf来查看一个动态链接的文件的重定位表

可以看到Lib.c中两个导入函数“printf” 和“sleep” 从“.rel.plt”到了“.rel.dyn”,并且类型也从R_386_JUMP_SLOT变成了R_386_PC32

而R_386_RELATIVE类型多出了一个偏移为0x0000042c的入口,这个入口是什么呢?通过对Lib.so 的反汇编可以知道,这个入口用来修正给printf的第一个参数,即我们的字符串常量“Printing from Lib.so %d\n”的地址。为什么这个字符串常量的地址在PIC时不需要重定位而在非PIC时需要重定位呢? 很明显,PIC时,这个字符串可以看做是普通的全局变量,它的地址是可以通过PIC中的相对当前指令的位置加上一个固定偏移计算出来的:而在非PIC中,代码段不再使用这种相对于当前指令的PIC方法,而是采用绝对地址寻址,所以它需要重定位;

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

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

typedf struct{ uint32_t a_type; union { uint32_t a_val; } a_un;} Elf32_auxv_t;

是不是已经对这种结构很熟悉了?没错,跟前面的“.dynamic”段里面的结构如出一辙。先是一个32位类型的值,后面是一个32位的数值部分,你很可能很奇怪为什么要用一个union把后面的32位数值包装起来,事实上,这个union没啥用,只是历史遗留而已,可以当做不存在。我们摘录几个比较重要的类型值,这几个类型值都是比较常见的,而且是动态链接器在启动时所需要的,如表7-3所示:

介绍了这么多关于辅助信息数组的结构,我们还没看到它位于进程堆栈的哪一个位置呢。事实上,它位于环境变量指针后面,比如我们假设操作系统传给动态链接器的辅助信息有4个,分别是:

  • AT_PHDR,值为0x08048034,程序表头位于0x08048034
  • AT_PHENT,值为20,程序表头中每一个项大小为20个字节
  • AT_PHNUM,值为7,程序表头共有7个项
  • AT_ENTRY,0x08048320,程序入口地址为0x08048320

那么进程的初始化堆栈就如图7-11所示。

我们可以写一个小程序来把堆栈中初始化信息全部打印出来,源代码如下

    推荐阅读
  • 家里养花不能超过几盆(家里养花不能超过多少盆)

    家里养花不能超过几盆?在家里养花的时候,要根据空间的大小养殖花卉,空间大的话可以多养几盆,没有严格的说养多少盆好,我来为大家讲解一下关于家里养花不能超过几盆?跟着小编一起来看一看吧!在养殖时需要注意,每个花盆间不能太紧密,要有一定的空间植物生长。要是其中的一盆花卉感染了病害,就要及时将其搬离,以免感染给更多健康的花卉,还应保证养护环境的通风性。

  • 微波炉怎么正确烤红薯(微波炉烤红薯10分钟就搞定)

    像这个大小的红薯有164g,大概不算皮有个150g吧,那么热量可以算245kcal,相当于大概215g的米饭。那你要是买的是街头那大个的烤红薯,那估计得有一顿饭的热量了吧!如果你没有把烤红薯当主食吃,而且一次吃的量很多,那自然就变成了增肥食物,所以量很重要。你看不能以为红薯是粗粮就多吃,扛不住,它是个粮食啊!但明显,烤红薯的个头都不小。所以,把烤红薯当主食,和杂豆粗粮换着吃,既营养又健康,妙啊!

  • 芝麻汤圆的功效和作用(黄米黑芝麻汤圆的功效和作用)

    黑芝麻粉具备降低血脂、延缓衰老功效,3、注意:芝麻汤圆含糖量比较高,血糖高的人不宜多吃。

  • 名人名言伤感(有哪些名人表达伤感的句子)

    名人名言伤感有一种默契叫做心照不宣;有一种感觉叫做妙不可言;有一种幸福叫做有你相伴;有一种思念叫做望眼欲穿。再怎么坚固的爱情,是无法承受忙碌的侵蚀。你忙得天荒地乱、忙得忘记关心、忙得身心疲惫!财富是一种寄存,钱再多,你也不能带到棺材里去;权位是一种寄存,无论你怎样叱咤风云,却不能逃出最终的交替。在你孤独、悲伤的日子里,请你悄悄地念一念我的名字。

  • 南京工程学院是几本(资料内容介绍)

    南京工程学院是几本南京工程学院位于江苏省南京市,是一所普通本科二本院校,以工学为主,工学、经济学、管理学、文学、理学、法学等学科协调发展的多科性普通本科大学。南京工程学院是一所具有百年办学历史、鲜明行业办学特色、应用型改革发展成效显著的江苏省属普通本科高校。具有本科学生的教育培养资格及相应的学位授予权。

  • 种植土豆不下底肥用沟施可以么 土豆种植前怎么整地施肥

    3、沟施肥的方法可提高株距拉宽点行距以提高肥料性比价值。

  • 天津20年新开业商业综合体(天津75个重大商业项目开工签约)

    据介绍,一年来,天津市全面推进114项重点任务,打造消费地标,开展促消费活动,市场持续回暖。13家大型购物中心开业运营,相继引进186个品牌首店。本次活动加快推动了一批项目开工建设。天津东丽区引入金茂览秀城、金隅金钟商业体两大商业龙头企业,西青区与红星美凯龙签订战略合作协议建设爱琴海购物公园项目,静海区将在大邱庄镇建设9万平方米的大邱庄天街项目,河东区引入中海城市广场欧式风情街区预计2023年6月开业。

  • 情境式作文教学课堂(让微作文漫步于语文教学)

    他号召党员排在后面,体现了党的先进性和表率作用。三个叹号闪亮登场,把老汉喊话的响亮、干脆、有力表现得淋漓尽致!大娘毫不犹豫把伤员“背进”防空洞。志愿军战士为了朝鲜人民负伤流血,一定要保住他的生命!在这不能两全齐美的紧急关头,大娘宁可“丢”下唯一的亲人,也要保护志愿军伤员。为了拯救村民,老汉不仅牺牲了自己,而且牺牲了儿子。

  • 电气鼠是什么梗(电气鼠介绍)

    电气鼠是什么梗?电气鼠是什么梗抖音电气鼠的意思就是电气鼠博主,这是一个博主的名字,因为是一个漂亮的小姐姐并且粉丝也很多,所以抖音中到处都是评论博主名字的,所以很多用户看不懂就很正常,因为这个美女博主是拍自己身材的博主,并且还是性感的装扮,所以一些粉丝在抖音中看到别的性感装扮博主以后都会在评论区评论:告诉电气鼠我不回去了,用来调侃或者是和其它评论区的用户交流,所以此梗就火了起来。

  • 入伏祝福语有哪些(关于入伏的祝福语文案)

    入伏祝福语有哪些初伏到,真热闹,起早饺子、中伏面,末伏烙饼炒鸡蛋,吃下幸福和美满,时刻心情快乐赞,甜甜蜜蜜永常伴,祝福牢记在心间,幸福生活到永远!小暑来正三伏,三伏天热要注意:户外避开太阳走,清晨晚间多运动;夏日炎炎水多喝,不可撑坏脾和胃;睡眠充足心情好,衣物多多翻洗晒。

热门推荐

抗衰老洗脸的正确方式(抗衰老洗脸的正确方式是什么) 歼10 海外订单(中航国际确认歼10卖出去了) 乳腺癌会乳房刺痛胀痛吗(乳房疼痛别害怕) 84岁老人绣清明上河图(沂源农家女绣出巨幅清明上河图) 羽绒服湿水后怎么处理(羽绒服湿水后的处理方法) 完美伴侣吴敏林庆昆陈珊孙磊(吴敏丧偶式育儿) 新世界成都百货折扣(新世界百货成都店将于3月份停止营业) 有效治疗白发变黑发的轻松小窍门(居然可以这么简单) 女生自然卷染什么发色好看 自然卷染发什么颜色 狗奔跑时候的样子简笔画(小狗趴着的图片简笔画) 淀粉类食物有很多种(多吃淀粉类食物有什么好处) 手机文件夹怎么弄(手机文件夹怎么弄成压缩包发给别人) 出现签名有效性未知怎么打印(这个电子签名是不是你签的) 苏州存量房2020年首批房源户型一览(苏州存量房2020年首批房源户型一览表) 穿紧身裤会引起大腿酸痛吗(女子反复大腿痛) 如何看待别人玩黑魂系列(盘点黑魂系列那些有趣的梗下篇) 学习有关知识的英语单词(英语不好出国坐飞机必须要记住33个常用的英语单词) 南京任贤齐2023演唱会公交怎么去(任贤齐演唱会门票多少钱) 复苏的魔女委派探索任务怎么做(复苏的魔女委派探索任务如何做) 比亚迪f3的动力怎么样(经济实惠比亚迪F3动力介绍)