VC++彻底理解链接器:四,重定位

news2025/1/10 21:04:25

重定位

程序的运行过程就是CPU不断的从内存中取出指令然后执行执行的过程,对于函数调用来说比如我们在C/C++语言中调用简单的加法函数add,其对应的汇编指令可能是这样的:

 call 0x4004fd

其中0x4004fd即为函数add在内存中的地址,当CPU执行这条语句的时候就会跳转到0x4004fd这个位置开始执行函数add对应的机器指令。
再比如我们在C语言中对一个全局变量g_num不断加一来进行计数,其对应的汇编指令可能是这样的:

 mov 0x400fda %eax
 add $0x1 %eax

这里的意思是把内存中 0x400fda 这个地址的数据放到寄存器当中,然后将寄存器中的数据加一,在这里g_num这个全局变量的内存地址就是0x400fda。

好奇的同学可能会问,那这些函数以及数据的内存地址是怎么来的呢?

确定程序运行时的内存地址就是接下来我们要讲解的重点内容,这里先给出答案,可执行文件中代码以及数据的运行时内存地址是链接器指定的,也就是上面示例中add的内存地址0x4004fd其是链接器指定的。确定程序运行时地址的过程就是这里重定位(Relocation)。

为什么这个过程叫做重定位呢,之所以叫做重定位是因为确定可执行文件中代码和数据的运行时地址是分为两个阶段的,在第一个阶段中无法确定这些地址,只有在第二个阶段才可以确定,因此就叫做重定位。接下来让我们来看看这两个阶段,合并同类型段以及引用符号的重定位。

编译器的工作

让我们回忆一下前几节的内容,源文件首先被编译器编译生成目标文件,目标文件种有三段内容:数据段、代码段以及符号表,所有的函数定义被放在了代码段,全局变量的定义放在了数据段,对外部变量的引用放到了符号表。

编译器在将源文件编译生成目标文件时可以确定一下两件事:
定义在该源文件中函数的内存地址
定义在该源文件中全局变量的内存地址
注意这里的内存地址其实只是相对地址,相对于谁的呢,相对于自己的。为什么只是一个相对地址呢?因为在生成一个目标文件时编译器并不知道这个目标文件要和哪些目标文件进行链接生成最后的可执行文件,而链接器是知道要链接哪些目标文件的。因此编译器仅仅生成一个相对地址。

而对于引用类的变量,也就是在当前代码中引用而定义是在其它源文件中的变量,对于这样的变量编译器是无法确定其内存地址的,这不是编译器需要关心的,确定引用类变量的内存地址是链接器的任务,链接器在进行链接时能够确定这类变量的内存地址。因此当编译器在遇到这样的变量时,比如使用了外部定义的函数时,其在目标文件中对应的机器指令可能是这样的:

call 0x000000

也就是说对于编译器不能确定的地址都这设置为空(0x000000),同时编译器还会生成一条记录,该记录告诉链接器在进行链接时要修正这条指令中函数的内存地址,这个记录就放在了目标文件的.rel.text段中。相应的如果是对外部定义的全局变量的使用,则该记录放在了目标文件的.rel.data段中。即链接器需要在链接过程中根据.rel.data以及.rel.text来填好编译器留下的空白位置(0x000000)。因此在这里我们进一步丰富目标文件中的内容,如图所示:

clipboard.png

生成目标文件后,编译器完成任务,编译器确定了定义在该源文件中函数以及全局变量的相对地址。对于编译器不能确定的引用类变量,编译器在目标文件的.rel.text以及.rel.data段中生成相应的记录告诉链接器要修正这些变量的地址。

接下来就是链接器的工作了。

链接器的工作

我们在静态库下可执行文件的生成一节中知道,链接器会将所有的目标文件进行合并,所有目标文件的数据段合并到可执行文件的数据段,所有目标文件的代码段合并到可执行文件的代码段。当所有合并完成后,各个目标文件中的相对地址也就确定了。因此在这个阶段,链接器需要修正目标文件中的相对地址。

在这里我们以合并目标文件中的数据段为例来说明链接器是如何修正目标文件的相对地址的,合并代码段时修正相对位置的原理是一样的。

我们假设链接器需要链接三个目标文件:

  • 目标文件一:该文件数据段定义了两个变量apple和banana,apple的长度为2字节,banana的长度4字节,因此目标文件一的数据段长度为6字节。从图中也可以看出apple的内存地址为0,也就是相对地址,即apple这个变量在目标文件一的地址是0,banana的地址为2。
  • 目标文件二:该文件的数据段比较简单,只定义了一个变量orange,其长度为2,因此该目标文件的数据段长度为2。
  • 目标文件三:该文件的数据段定义了三个变量grape、mango以及limo,其长度分别为4字节、2字节以及2字节,因此该目标文件的数据段长度为8字节。

clipboard.png

链接器在链接三个目标文件时其顺序是依次链接的,链接完成后:

  • 目标文件一:该数据段的起始地址为0,因此该数据段中的变量的最终地址不变。
  • 目标文件二:由于目标文件一的数据段长度为6,因此链接完成后该数据段的起始地址为6(这里的起始地址其实就是偏移offset),相应的orange的最终内存地址为0+offset即6。
  • 目标文件三:由于前两个数据段的长度为8,因此该数据段的起始地址为8(即offset为8),因此所有该数据段中的变量其地址都要加上该offset,即grape的最终地址为8,即0+offset,mango的最终地址为4+offset即12,limo的最终地址为6+offset即14。

从这个过程中可以看到,数据段中的相对地址是通过这个公式来修正的,即:

  相对地址 + offset(偏移) = 最终内存地址

而每个段的偏移只有在链接完成后才能确定,因此对相对地址的修正只能由链接器来完成,编译器无法完成这项任务。

当所有目标文件的同类型段合并完毕后,数据段和代码段中的相对地址都被链接器修正为最终的内存位置,这样所有的变量以及函数都确定了其各自位置。

至此,重定位的第一阶段完成。接下来是重定位的第二阶段,即引用符号的重定位。

相对地址是编译器在编译过程中确定了,在链接器完成后被链接器修正为最终地址,而对于编译器没有确定的所引用的外部函数以及变量的地址,编译器将其记录在了.rel.text和.rel.data中。

由于在第一阶段中,所有函数以及数据都有了最终地址,因此重定位的第二阶段就相对简单了。我们知道编译器引用外部变量时将机器指令中的引用地址设置为空(比如call 0x000000),并将该信息记录在了目标文件的.rel.text以及.rel.data段中。因此在这个阶段链接器依次扫描所有的.rel.text以及.rel.data段并找到相应变量的最终地址(这些位置都已在第一阶段确定),并将机器指令中的0x000000修正为所引用变量的最终地址就可以了。

到这里链接器的重定位就讲解的这里,作为程序员一般很少会有问题出现在重定位阶段,因此这个阶段对程序员相对透明。请同学们注意一点,这里的分析仅限于目标文件的静态链接。我们知道静态链接下,链接器会将需要的代码和数据都合并到可执行文件当中,因此需要确定代码和数据的最终位置。而对于动态链接库来说情况则有所不同,动态链接库可以同时被多个进程使用,如果动态链接库的机器指令中不可以存在引用变量的最终位置,否则在被多个进程使用时会出现一个进程中使用的数据被其它进程修改。因此动态库下的机器指令都是PIC代码,即位置无关代码(Position-Independent Code)。关于PIC的机制原理就不在这里阐述了,对此感兴趣的同学可以关注微信公众号,码农的荒岛求生,我会在那里来讲解。

问题:为什么链接器能确定运行时地址

我们知道只有把可执行文件加载到内存当中程序才可以开始运行。不同的程序会被加载到内存的不同位置。我们从前两节的过程中可以看出,链接器完全没有考虑不同的程序会被加载不同的内存位置被执行。比如对于一个可执行文件我们分别运行两次,如下图所示,因为两个程序数据段变量的地址是一样的,那么程序一的数据会不会被程序二修改呢?

如果你去试一试的话就会发现显然不会有这种问题的。而当可执行文件加载到内存的时候也不会根据程序加载的起始地址再去修改可执行文件中变量的地址(这样就启动速度就太慢了),那么操作系统又是如何能做到基于同一个可执行文件的两个程序能在各自的内存空间中运行而不相互干扰呢,链接器在可执行文件中确定的到底是不是程序最终的运行地址呢

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

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

相关文章

全国测绘资质管理信息系统测绘资质申报流程

讲解一期关于测绘资质申请如何操作,在哪操作的问题,想要知道的按以下流程操作即可 (注:以下操作为资质系统操作,想要能把资质申请下来,还需满足以下要求,后面会和系统操作一起统一讲解&#xf…

K8S如何部署ActiveMQ(单机、集群)

前言 大家好,在今天的讨论中,我们将深入研究如何将ActiveMQ迁移到云端,以便更好地利用Kubernetes的容器调度和资源管理能力,确保ActiveMQ的高可用性和可扩展性。 ActiveMQ是Apache开源组织推出的一款开源的、完全支持JMS1.1和J2…

IBM X3650M4安装ESXI6.5卡在/lsl_mr3.v00

环境:IBM X3650M4服务器双盘配置raid1,通过rufus制作启动U盘,安装VMware Vsphere 5.5系统 问题:卡在/lsi_mr3.v00界面无法往下运行(两台配置一样的机器遇到同样的问题) 解决方案: 直接在U盘根…

软件设计中如何画各类图之二深入解析数据流图(DFD):系统设计与分析的关键视觉工具

目录 1 前言2 数据流图(DFD)的重要性3 数据流图的符号说明4 清晰的数据流图步骤4.1 确定系统边界4.2 识别数据流4.3 定义处理过程4.4 确认数据存储4.5 建立数据流动的连线4.6 细化和优化 5 数据流图的用途6 使用场景7 实际应用场景举例8 结语 1 前言 当…

Eclipse 设置try-cacth 默认格式

设置面板 第一处 第二处 其中NsRuntimeException是自定义的异常处理。这样设置后,打开代码补全功能,输入try回车就会默认显示设置的代码

雅可比矩阵(Jacobian Matrix)

假设给定一个从n维欧式空间到m维欧式空间的变换: 雅可比矩阵就是将一阶偏导数排列成一个m行、n列形式的矩阵,记作: 举一个例子: 雅可比矩阵等于:

新手如何对一个web网页进行一次渗透测试

新手如何对一个web网页进行一次渗透测试 什么是渗透测试? 在获得web服务运营的公司书面授权的情况下,模拟攻击者的行为,以确定系统的脆弱性,并为保护系统提供有效的建议。 渗透测试和红蓝对抗的区别 渗透测试和红蓝对抗别再傻傻分不清楚…

ChatGPT重磅升级!集简云支持GPT4 Turbo Vision, GPT4 Turbo, Dall.E 3,Whisper等最新模型

在11月7日凌晨,OpenAI全球开发者大会宣布了 GPT-4的一次大升级,推出了 GPT-4 Turbo号称为迄今为止最强的大模型。 此次GPT-4的更新和升级在多个方面显示出强大的优势和潜力。为了让集简云用户能快速体验新模型的能力,我们第一时间整理了大会发…

Leetcode103 二叉树的锯齿形层序遍历

二叉树的锯齿形层序遍历 题解1 层序遍历双向队列 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 提示&#xff1a…

力扣每日一题-统计和小于目标的下标对数目-2023.11.24

力扣每日一题:统计和小于目标的下标对数目 开篇 今天这道力扣打卡题写得我好狼狈,一开始思路有点问题,后面就是对自己的代码到处缝缝补补,最后蒙混过关。只能分享一下大佬的代码,然后我帮大家分享代码的思路。 题目链…

迈巴赫S480升级主动式氛围灯 浪漫婉转的气氛

主动式氛围灯有263个可多色渐变的LED光源,营造出全情沉浸的动态光影氛围。结合智能驾驶辅助系统,可在转向或检测到危险时,予以红色环境光提示,令光影艺术彰显智能魅力。配件有6个氛围灯,1个电脑模块。 1、气候&#xf…

2023年亚太杯数学建模A题——深度学习苹果图像识别(思路+模型+代码+成品)

Image Recognition for Fruit-Picking Robots 水果采摘机器人的图像识别功能 问题 1:计数苹果 根据附件 1 中提供的可收获苹果的图像数据集,提取图像特征,建立数学模型,计算每幅图像中的苹果数量,并绘制附件 1 中所有…

FANUC机器人系统配置相关--系统变量介绍

FANUC机器人系统配置相关–系统变量介绍 系统配置页相关变量 1- 停电处理$SEMIPOWERFL = TRUE(有效)/FALSE(无效) 2- 停电处理中的I/O $PWF_IO = 1(不恢复)/2(仿真恢复)/3(解除仿真)/4(恢复所有) 3- 停电处理无效时自动执行的程序 $PWR_NORMAL = ‘’ 4- 停电处理有效时自动…

VINS-MONO代码解读----vins_estimator(鲁棒初始化部分)

0. 前言 整个初始化部分的pipeline如下所示,参照之前的博客,接下来根据代码一步步讲解。 1. 旋转约束标定旋转外参Rbc 上回讲了processImage中addFeatureCheckParallax完成了对KF的筛选,我们知道了2nd是否为KF,接下来是初始化…

Diffusion Model: DDPM

本文相关内容只记录看论文过程中一些难点问题,内容间逻辑性不强,甚至有点混乱,因此只作为本人“备忘”,不建议其他人阅读。 Denoising Diffusion Probabilistic Models: https://arxiv.org/abs/2006.11239 DDPM 一、基于 已知…

使用Linux JumpServer堡垒机本地部署与远程访问

🌈个人主页:聆风吟 🔥系列专栏:网络奇遇记、Cpolar杂谈 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 📋前言一. 安装Jump server二. 本地访问jump server三. 安装 cpolar内网穿透软件四. 配…

mysql索引分为哪几类,聚簇索引和非聚簇索引的区别,MySQL索引失效的情况有哪几种情况,MySQL索引优化的手段,MySQL回表

文章目录 索引分为哪几类?聚簇索引和非聚簇索引的区别什么是[聚簇索引](https://so.csdn.net/so/search?q聚簇索引&spm1001.2101.3001.7020)?(重点)非聚簇索引 聚簇索引和非聚簇索引的区别主要有以下几个:什么叫回…

vcsa6.7 5480无法登录

停电维护硬件后,发现vcsa异常,https://ip:5480无法登录,https://ip/ui正常,ssh登录页正常 kb资料 通过端口 5480 登录到 VMware vCenter Server Appliance Web 控制台失败 (2120477) 操作过程 Connecting to 192.16.20.31:22..…

LLMLingua:集成LlamaIndex,对提示进行压缩,提供大语言模型的高效推理

大型语言模型(llm)的出现刺激了多个领域的创新。但是在思维链(CoT)提示和情境学习(ICL)等策略的驱动下,提示的复杂性不断增加,这给计算带来了挑战。这些冗长的提示需要大量的资源来进行推理,因此需要高效的解决方案,本文将介绍LLM…

2023大模型安全解决方案白皮书

今天分享的是大模型系列深度研究报告:《2023大模型安全解决方案白皮书》。 (报告出品方:百度安全) 报告共计:60页 前言 在当今迅速发展的数字化时代,人工智能技术正引领着科技创新的浪潮而其中的大模型…