【链接装载与库】动态链接(下)

news2024/11/28 0:54:23

动态链接

=上篇=

延迟绑定 (PLT)

动态链接的确有很多优势,比静态链接要灵活得多,但它是以牺牲一部分性能为代价的。主要原因是动态链接下对于全局和静态的数据访问都要进行复杂的GOT定位,然后间接寻址;对于模块间的调用也要先定位GOT, 然后再进行间接跳转,另外一个原因是动态链接的链接工作在运行时完成,即程序开始执行时,动态链接器都要进行一次链接工作。我们将在这一节介绍优化动态链接性能的一些方法。

  • 延迟绑定实现

在动态链接下,程序模块之间包含了大量的函数引用,会耗费不少时间用于解决模块之间的函数引用的符号查找以及重定位。
如果一开始就把所有函数都链接好实际上是一种浪费。所以ELF采用了一种叫做延迟绑定的做法,基本的思想就是当函数第一次被用到时才进行绑定

ELF 使用PLT(Procedure Linkage Table) 的方法来实现
当我们调用某个外部模块的函数时,如果按照通常的做法应该是通过GOT。 PLT为了实现延迟绑定,在这个过程中间又增加了一层间接跳转。调用函数并不直接通过GOT跳转,而是通过一个叫作PLT项的结构来进行跳转。每个外部函数在PLT中都有一个相应的项,比如bar()函数在PLT中的项的地址我们称之为 bar@plt。

bareplt:
jmp *(baraGOT)
push  n
push  moduleID
jump  _dl_runtime_resolve
  1. 第一条指令是一条通过GOT间接跳转的指令,跳转到 bar(), 实现函数正确调用。
    但是为了实现延迟绑定,链接器在初始化阶段并没有将 bar()的地址填入到该项,而是将上面代码中第二条指令 “push n”的地址填入到bar@GOT中,
  2. 第二条指令将一个数字n 压入堆栈中,这个数字是bar这个符号引用在重定位表“rel.plt” 中的下标。
  3. 接着又是一条push指令将模块的ID 压入到堆栈,然后跳转到 _dl_runtime_resolve。 这实际上就是在实现我们前面提到的 lookup(module,function)这个函数的调用:先将所需要决议符号的下标压入堆栈, 再将模块ID压入堆栈,然后调用动态链接器的_dl_runtime_resolve()函数来完成符号解析和重定位工作。_dl_runtime_resolve()在进行一系列工作以后将bar()的真正地址填入到bar@GOT中。

一旦bar()这个函数被解析完毕,当我们再次调用bar@plt 时,第一条jmp指令就能够跳 转到真正的bar()函数中

上面我们描述的是 PLT 的基本原理,PLT真正的实现要比它的结构稍微复杂一些。ELF将GOT拆分成了两个表叫做“.got”和“.got.plt”。其中“.got”用来保存全局变量引用的地址,“.got.plt”用来保存函数引用的地址,另外“.got.plt”还有一个特殊的地方是它的前三项 是有特殊意义的,分别含义如下
在这里插入图片描述

● 第一项保存的是“.dynamic” 段的地址,这个段描述了本模块动态链接相关的信息,我 们在后面还会介绍“.dynamic”段。
● 第二项保存的是本模块的 ID。
● 第三项保存的是 _dl_runtime_resolve的地址。

动态链接相关结构

动态链接情况下,可执行文件的装载与静态链接情况基本 一样。首先操作系统会读取可执行文件的头部,检查文件的合法性,然后从头部中的“Program Header”中读取每个 “Segment”的虚拟地址、文件地址和属性,并将它们映射到进程虚拟 空间的相应位置,这些步骤跟前面的静态链接情况下的装载基本无异。
但是在动态链接情况下,操作系统还不能在装载完可执行文件之后就把控制权交给可执行文件,因为我们知道可执行文件依赖于很多共享对象。这时候,可执行文件里对于很多外部符号的引用还处于无效地址的状态,即还没有跟相应的共享对象中的实际位置链接起来。 所以在映射完可执行文件之后,操作系统会先启动一个动态链接器
在Linux 下,动态链接器ld.so实际上是一个共享对象,操作系统同样通过映射的方式 将它加载到进程的地址空间中。操作系统在加载完动态链接器之后,就将控制权交给动态链接器的入口地址;当所有动态链接工作完成以后,动态链接器会将控制权转交到可执行文 件的入口地址,程序开始正式执行。

  • “.interp”段

系统中哪个才是动态链接器呢,它的位置由谁决定?
是由ELF可执行文件决定。在动态链接的ELF可执行文件中,有一个专 门的段叫做“interp”段,“interp”的内容很简单,里面保存的就是一个字符串,这个字符串就是可执行文件所需要的动态链接器的路径。操作系统在对可执行文件的进行加载的时候,它会去寻找装载该可执行文件所需要相应的动态链 接器,即“.interp”段指定的路径的共享对象。

Linux 下,可执行文件所需要的动态链接器的路径几乎都是 “/lib/ld-linux.S0.2”, 其他的*nix 操作系统可能会有不同的路径。在 Linux的系统中,/lib/ld-linux.so.2通常是一个软链接, 比如在我的机器上,它指向/ib/ld-2.6.1.so,这个才是真正的动态链接器。

我们也可以用这个命令来查看一个可执行文件所需要的动态链接器的路径

readelf -l a.out | grep interpreter
  • “.dynamic”段

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

  • 动态符号表

为了完成动态链接,最关键的还是所依赖的符号和相关文件的信息。
为了表示动态链接这些模块之间的符号导入导出关系,ELF专门有一个叫做动态符号表的段用来保存这些信息,这个段的段名通常叫做“.dynsym” 。与“.symtab”不同的是,“.dynsym”只保存了与动态链接相关的符号,对于那些模块内部的符号,比如模块私有变量则不保存。很多时候动态链接的模块同时拥有 “.dynsym” 和“ .symtab”两个表,“.symtab” 中往往保存了所有符号,包括“ .dynsym” 中 的符号。

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

  • 动态链接重定位表

在动态链接中,导入符号的地址在运行时才确定,所 以需要在运行时将这些导入符号的引用修正,即需要重定位。
动态链接的可执行文件使用的是PIC 方法,但这不能改变它需要重定位的本质。PIC 模式的共享对象也需要重定位。

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

动态链接的文件中,也有类似的重定位表分别叫做“.rel.dyn”和“.rel.plt”, 它们分别相当于“ .rel.text”和“.rel.data"。"rel.dyn”实际上是对数据引用的修正,它所修正的位置 位于“.got”以及数据段;而“.rel.plt”是对函数引用的修正,它所修正的位置位于“got.plt”。
在这里插入图片描述

当动态链接器需要进行重定位时,它先查找“printf”的地址,“printf”位于libc-2.6.1.s0。 假设链接器在全局符号表里面找到“printf”的地址为0x08801234, 那么链接器就会将这个 地址填入到“got.plt”中的偏移为0x000015d8 的位置中去,从而实现了地址的重定位

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

站在动态链接器的角度看,当操作系统把控制权交给它的时候,它将开始做链接工作, 那么至少它需要知道关于可执行文件和本进程的一些信息。
这些信息往往由操作系统传递给动态链接器,保存在进程的堆栈里面。我们在前面提到过,进程初始化的时候,堆栈里面保存了关于进程执行环境和命令行参数等信 息。事实上,堆栈里面还保存了动态链接器所需要的一些辅助信息数组

动态链接的步骤和实现

动态链接的步 骤基本上分为3步:

  1. 先是启动动态链接器本身
  2. 然后装载所有需要的共享对象
  3. 最后是重定位和初始化
  • 动态链接器自举

我们知道动态链接器本身也是一个共享对象,但是事实上它有一些特殊性。
动态链接器本身不可以依赖于其他任何共享对象;其次是动态链接器本身所需要的全局和静态变量的重定位工作由它本身完成。

这种具有一定限制条件的启动代码往往被称为自举,动态链接器入口地址即是自举代码的入口

  • 装载共享对象

完成基本自举以后,动态链接器将可执行文件和链接器本身的符号表都合并到一个符号表当中,我们可以称它为全局符号表。然后链接器开始寻找可执行 文件所依赖的共享对象。读取相应的ELF 文件头和“.dynamic”段,然后将它相应的代码段和数据段映射到进 程空间中。如果这个 ELF 共享对象还依赖于其他共享对象,那么将所依赖的共享对象的名 字放到装载集合中。如此循环直到所有依赖的共享对象都被装载进来为止。

  • 全局符号介入与地址无关代码

当上面的步骤完成之后,链接器开始重新遍历可执行文件和每个共享对象的重定位表, 将它们的GOT/PLT中的每个需要重定位的位置进行修正。

重定位完成之后,如果某个共享对象有 “init”段,那么动态链接器会执行 “init”段中的代码,用以实现共享对象特有的初始化过程,比如最常见的,共享对象中的C++的全局/静态对象的构造就需要通过“.init”来初始化。

当完成了重定位和初始化之后,所有的准备工作就宣告完成了,所需要的共享对象也都已经装载并且链接完成了,这时候动态链接器就如释重负,将进程的控制权转交给程序的入 口并且开始执行。

显式运行时链接

支持动态链接的系统往往都支持一种更加灵活的模块加载方式,叫做显式运行时链接, 有时候也叫做运行时加载。 也就是让程序自己在运行时控制加载指定的模块,并且可以在不需要该模块时将其卸载。这种运行时加载在理论上也是很容易实现的。而且一般的共享对象不需要进行任何修改就可以进行运行 时装载,这种共享对象往往被叫做动态装载库 其实本质上它 跟一般的共享对象没什么区别,只是程序开发者使用它的角度不同。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1188184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go RabbitMQ简介 使用

RabbitMQ简介 RabbitMQ 是一个广泛使用的开源消息队列系统,它实现了高级消息队列协议(AMQP)标准,为分布式应用程序提供了强大的消息传递功能。RabbitMQ 是 Erlang 语言编写的,具有高度的可扩展性和可靠性,…

普洱茶上市?澜沧古茶通过港股聆讯

近日,澜沧古茶成功通过港交所聆讯,随后在11月7日披露了相关资料集。该公司即将在港交所主板上市,此次上市由中信建投国际和招商证券国际担任联席保荐人。据了解,澜沧古茶或将成为内地茶企第一股,也将成为“普洱茶第一股…

el-form-item的label的长度单独改掉,用vue3样式穿透的写法,加上css选择器查找特定的id拿到元素

为了让这个会员卡号这几个字和下面的表格对齐,需要改el-form-item的label的长度 如果直接改el-form的label-width,那么所有的el-form-item的label都会改,我不希望这样 我希望只改第1个会员卡号的label长度 给这个el-form-item添加一个id :deep(.el-for…

漏洞复现--泛微E-Office信息泄露(CVE-2023-2766)

免责声明: 文章中涉及的漏洞均已修复,敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权的攻击属于非法行为!文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

xshell隧道设置

现在有远程外网地址 120.120.120.120和另一台内网地址192.168.1.110两台cvm 但是192.168.1.110 无法直接通过外网地址访问, 需要通过120.120.120.120建立隧道来连接 需要访问192.168.1.110 机器的3306端口, 可以这么做 将192.168.1.110 的3306映射到本地13306端口 1.连接外网…

YOLOv8-Seg改进:多尺度空洞注意力(MSDA),增强局部、稀疏提取特征能力

🚀🚀🚀本文改进: 新的注意力机制——多尺度空洞注意力(MSDA)。MSDA 能够模拟小范围内的局部和稀疏的图像块交互; 如何在OLOv8-seg下使用:1)作为注意力机制放在各个网络位置;2)与C2f结合替代原始的C2f 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLS…

人工智能辅助职业教育发展——开启教育新时代

人工智能辅助职业教育发展——开启教育新时代 随着科技的飞速发展,人工智能(AI)逐渐渗透到各行各业,并在许多领域发挥着重要作用。如今,AI的应用已经延伸到职业教育领域,为培养高素质人才提供了新的可能和动…

用户交互引导大模型生成内容特征,LLM-Rec框架助力个性化推荐!

欢迎来到魔法宝库,传递AIGC的前沿知识,做有格调的分享❗ 喜欢的话记得点个关注吧! 今天主要和大家分享一篇使用大语言模型做数据增强来提升推荐系统性能的研究 标题: LLM-Rec: Personalized Recommendation via Prompting Large …

2023软件测试必问的100个面试题【含答案】

一、测试理论 1.什么是软件测试? 答:软件测试是通过执行预定的步骤和使用指定的数据,以确定软件系统在特定条件下是否满足预期的行为。 2.测试驱动开发(TDD)是什么? 答:测试驱动开发是一种开…

基于情感分析+聚类分析+LDA主题分析对服装产品类的消费者评论分析(文末送书)

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

MKL.NET:为.NET开发者提供高性能数学计算支持的开源库

目录 01 项目简介02 主要功能03 项目结构04 项目地址 MKL是英特尔推出的一套功能强大、性能优化的数学库,主要是采用C/C编写的。今天给大家推荐一个MKL的.Net版本,让我们无需与C/C打交道,方便我们集成到应用开发中去。 01 项目简介 MKL.NET…

智能机器人云控平台

智能机器人云控平台主要是通过打造一个低速固定场景下的机器人自主完成既定作业的模式。整体平台融合的自动化驾驶感知终端,物联网通信,数据接入中心,数据管理中心,模型中心及开放中心等,以一个云平台来掌控多个多种不…

ElementUI的Dialog弹窗实现拖拽移动功能

实现ElementUI的Dialog弹窗可以拖拽移动 实现步骤: 1.创建自定义指令 在utils文件夹下新建文件夹 utils/directive/el-dragDialog/index.js import drag from ./dragconst install function(Vue) {Vue.directive(el-drag-dialog, drag) }if (window.Vue) {windo…

电机应用-步进电机

步进电机(脉冲电机) 基于最基本的电磁铁原理,是一种可以自由回转的电磁铁,其工作原理是依靠气隙磁导的变化来产生电磁转矩。 由于步进电机是一个可以把电磁脉冲转换成机械运动的装置,具有很好的数据控制特性&#xff0…

解决问题:IDEA启动微服务项目,显示Loaded classes are up to date. Nothing to reload.

先说结论,再讲相声: 开启Shorten Command Line 选择JARmanifest 今天启动微服务,就有这么寸,其他的服务启动的好好的,唯独我需要Debug的项目无法启动,只能Run运行 满世界找答案无非就是几种:…

第二证券:今日投资前瞻:PPP迎来新机制,消费电池需求有望迎来复苏

11月8日,两市股指盘中轰动回落,尾盘逐渐止跌。到收盘,沪指跌0.16%报3052.37点,深成指微跌0.04%报10052.09点,创业板指涨0.02%报2023.13点,科创50指数涨0.92%;两市估计成交10366亿元,…

图文详解 VCF 生信格式 (变异信息)

文章目录 一、vcf 格式介绍二、vcf 资源文件三、vcf 文件详解3.1 主要字段3.2 INFO 中的常见信息3.3 FORMAT 和 SAMPLEs 中的信息 四、vcf 的记录模式4.1 只记录变异本身的信息4.2 记录个体或个体组织的变异信息4.3 记录群体或家系的变异信息 五、记录标准5.1 记录多核苷酸多样…

第二证券:长期停牌一般是多久?

股票停牌不仅仅是个股的问题,它或许会影响到商场的整体运作和投资者的利益。那么,长期停牌一般是多久呢?从不同的视点分析,可以得到不同的答案。 1. 官方规则 首要,咱们需求查看相关规则。依据证监会规则&#xff0c…

经典猜数游戏(python类封装)

五次机会猜测100以内随机正整数,我用初通的python类封装了代码并清屏上一次猜测提示,难有所增加咯。 (笔记模板由python脚本于2023年11月09日 12:31:30创建,本篇笔记适合掌握python循环和条件分支语句用法,初通python类的coder翻阅…

开设自己的网站系类01购买服务器

开始建设自己的网站吧! 编者买了一个服务器打算自己构建一个网站,用于记录生活。网站大概算是一个个人博客吧。记录创建过程的一些步骤 要开设自己的网站,需要执行以下关键步骤 以下只是初步列出了建立自己的网站的大概步骤,后…