当前位置: 首页 > news >正文

【Linux】理解链接过程

1.objdump命令

功能:用于显示目标文件(如可执行文件、对象文件、库文件)的详细信息。

objdump  [选项]  文件

该命令可以用于反汇编

# 反汇编可执行文件,显示代码段
objdump -d program

# 反汇编并显示源代码(需要编译时包含调试信息)
objdump -S program

2. 静态链接

研究静态链接,本质就是研究 .o 是如何链接的。我们通过代码来观察一下:

我们可以看到这里的main函数里用到了run方法,但是run的具体实现在另一个.c文件中,我们把这两个文件编译一下:

编译过程没有什么问题,说明我们在编译过程是没有函数的具体实现方法也可以编过的。此时我们来看一下这两个.o文件的反汇编内容:

这里的两次call就是指我们调用了两次函数(printf 和 run),前面的数字e8其实就是用十六进制代表call命令,后面跟着的一串零其实应该是调用的函数地址(暂时为0),因为此时编译器是不知道函数地址的,和其他的模块链接后才知道函数地址。接下来我们就将这两个.o文件链接起来,在链接过程中也会链接我们包含的C标准库。

我们再来查看myexe中run函数和main函数的反汇编内容:

我们可以看到两个函数的地址都填上了具体的值,我们还可以看到我们call的是printf函数(printf底层调用的就是puts)和run函数。所以我们知道了:链接过程会把我们要调用的函数地址从0重定位到目标函数地址。这个过程就是链接时地址重定位,所以.o/.obj文件叫做可重定位目标文件!所以在链接之前,文件之间都是独立的,甚至不知道对方的存在,链接后才建立的文件之间的关系。

最后main函数成功调用了run函数,所以说明这两个.o文件(以及C标准库)的.text部分(代码区)合并了,并进行了统一的编址。链接过程中会修改.o中没有确定的函数地址,代码合并之后就可以通过call函数地址来进行函数调用。

3. 平坦模式

Linux系统编译形成可执行程序的时候,需要对代码和数据进行编址;当代计算机工作时都采用“平坦模式”进行编址,对ELF编址时也是如此。

平坦模式的核心思想是:让操作系统和应用程序能够访问一个连续的、无段的线性地址空间。也就是说它的编址方法就是从全0到全F进行编址(0000...0000~FFFF...FFFF),这样按照线性地址进行统一编址的。我们仔细看一段myexe的反汇编就可以发现地址是连续的:这种全0到全F的编址方法得到的地址其实就是虚拟地址!虚拟地址不只是进程中的概念,Linux系统中还有很多地方都会用到虚拟地址这个概念。

磁盘可执行文件采用“起始地址+偏移量”的编址方法得到的地址叫做逻辑地址。其实今天我们说的虚拟地址可以理解为逻辑地址的偏移量,或者是起始地址为零的逻辑地址。程序内部互相调用、互相访问时使用的地址是虚拟地址。

4. 程序从加载到执行

一个可执行程序在磁盘里时,其内部代码和数据的布局就已经按照平坦模式编址好了,这个地址被称为逻辑地址。当程序被加载到内存中运行时,操作系统会为它创建一个独立的虚拟地址空间,并将指令和数据放入真实的物理内存中,这样就有了对应的物理地址。此时,操作系统通过填写页表,建立起程序使用的虚拟地址(根据逻辑地址得到)与内存中物理地址之间的映射关系,从而让程序能够正常运行。

在总结代码执行的整个过程前,我们先来大致了解一下程序执行过程中CPU里主要用到的几个模块:CR3寄存器存储着页表起始位置,MMU是负责将虚拟地址转换为物理地址并实施内存访问,EIP寄存器存储着PC指针——CPU接下来要执行的那一条指令地址,IR寄存器对当前指令译码。

所以页表建立好后,代码执行的过程就是:CPU根据CR3寄存器找到当前进程的页表。执行程序时,CPU通过EIP寄存器获取下一条指令的虚拟地址,该地址由MMU单元利用CR3指向的页表即时转换为物理地址,然后从内存中取出指令。取出的指令被送入IR进行译码,同时EIP自动指向后续指令。CPU执行完当前指令后,又继续根据EIP获取下一条指令,如此循环,直到程序正常结束或遇到终止指令。

5. 重新理解虚拟地址空间

ELF在被编译好之后,会把自己程序的入口地址记录在“Entry point address”字段里:

我们可以查到这个地址对应的segment是_start,所以其实在操作系统中程序的入口是_start,它负责搭建C/C++程序运行的初始化环境,可以看到_start里面调用了__libc_start_main函数间接的调用了main函数,所以我们的代码都是从main函数开始执行的:

ELF Segment是磁盘上程序内存布局的"蓝图",它规定了哪些部分需要被加载以及以何种权限加载,我们可以来看一下ELF Segment的内容:

ELF文件中的每个LOAD类型Segment(需要加载的段)在程序加载时,都会在内核中创建一个对应vm_area_struct结构体,该结构体继承了Segment的虚拟地址范围、访问权限和文件偏移等关键信息,成为该段内存在内核中的运行时管理单元。所有这些由Segment初始化的vm_area_struct共同构成了mm_struct所管理的完整虚拟地址空间,而mm_struct又被包含在进程的顶级描述符task_struct中,从而完成了从静态文件到动态进程的内存组织,操作系统正是通过这些vm_area_struct对象来建立虚拟地址到物理内存的映射并管理整个进程地址空间。

这张图非常形象的表现出来了我们总结的内容。

http://www.dtcms.com/a/474645.html

相关文章:

  • 广州做网站多少钱怎么做简单的网站首页
  • 【机器人学中的状态估计】7.5.2习题证明:(Cu)^=(2cos(phi)+1)u^-u^C-C^Tu^公式证明
  • Flask、Nginx 与 Docker 的分工与协作
  • 怎么建立一个公司的网站吗ui界面设计作品模板
  • 网站浮动广告怎么做qq开放平台网站开发申请不通过的原因
  • redis中的list命令
  • 对网站建设课程的心得体会北京旅游网页设计
  • 碎片化知识整理利器:NoteGen——AI驱动的免费开源笔记工具使用指南
  • 网站的建设方法包括什么问题高端网站建设大概多少费用
  • RabbitMQ Exchange类型与绑定规则详解
  • 太平洋建设官方网站wordpress 显示分类
  • 比特币私钥位数范围动态估计源代码
  • 随机游走:从布朗运动到PageRank算法的数学之旅
  • 机器学习周报十七
  • DeepCode:从论文到完整软件开发的全自动AI工具
  • 深入探索现代前端开发:从基础到架构的完整指南
  • Sora2高级玩法:超越基础生成的创意新世界(FL去水印送邀请码)
  • 自己怎样优化网站wordpress博客位置
  • 大型购物网站服务器h5页面制作工具易企秀
  • ESP32 + Arduino IDE 开发的 MQTT 通信程序
  • 网站策划哪里找WordPress访问确认
  • Kubernetes YAML配置入门
  • 淘宝网站官网东莞微网站建设多少钱
  • leetcode 118. 杨辉三角 python
  • 中级软件设计师考试选择题——计算机网络典型真题
  • 互联网个人用户网站WordPress移动站
  • ArrayList和LinkedList的区别是什么?(高频)
  • 建设网站的费用属于资产吗广州百度快速排名优化
  • 将 GPU 级性能带到企业级 Java:CUDA 集成实用指南
  • 模型训练中GRPO概念理解