编译链接运行原理——(一)进程的诞生

本文详细介绍了从源文件到进程的编译链接运行过程,包括预编译、编译、汇编、链接和运行五个步骤。重点讨论了链接过程中的地址分配、符号解析和重定位,以及强弱符号规则,帮助读者深入理解程序的生成过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     编译链接一直以来都是程序员所必知必会的知识,如果你都不清楚一个程序到底是如何从一个手写的源文件变成计算机所能识别的指令且能按部就班的执行的话,那么你就不是一个合格的程序员了。这是我的一个老师告诉我的。但是学习了这么久对这块的知识仍是比较模糊,虽然反复学习了很多遍,仍是没有完全掌握,这可能与我的个人学习习惯有关,不太擅长总结,就像我的blog只有聊聊几篇,从今天起我将系统性的对我所学的这些进行一个总结,加深一下自己对这些知识的理解。

      那么言归正传,一个源文件到一个进程在这中间到底经历了些什么呢?

  1. 预编译
  2. 编译
  3. 汇编
  4. 链接
  5. 运行

      简单概括就是这五步,那么这五步具体究竟做了些什么呢?接下来我就一步一步细细回顾一下。

     1.预编译

      预编译简单来讲就是处理‘#’和标识的。

  • 删除#define,进行宏替换。
  • 处理预编译指令“#if,#endif,#ifdef,#elif”等等。
  • 处理头文件“#include”,进行递归展开。
  • 删除注释。
  • 添加行号以及文件标识,便于编译出错时,方便报错快速锁定行号。
  • 保留#program,交给编译器。这是编译器所必须的。

   经过预编译后,这里以c/c++举例,就会生成.i文件。

     2.编译

  • 词法分析
  • 语法分析
  • 语义分析
  • 代码优化(生成中间文件)
  • 生成汇编指令(目标文件,根据目标机器还要进行一次优化,选择适合的寻址方式和删除多余指令)。

    经过编译后,生成.s文件。

    3.汇编

    翻译汇编指令。就是将上步生成汇编指令翻译成机器可识别的二进制文件。

    4.链接

    这一步可谓是最精髓的一步,观察上面的那几步,我想你大概已经知道这一步是干嘛的。举个例子

movl    index,%edx
leal    32(,%edx,8),%eax
movl    %eax,arry(,%edx,4)

  很明显就单个文件编译而言,index和arry若是在当前文件中定义了还可以直接为其开辟内存空间,但若不在本文件中定义,那么在编译期是无法找到其地址的。也就是拿不到数据就和废了没啥区别,那么该怎么解决呢?链接就解决了这个问题。

  那么链接具体干了些啥呢?

  • 分配地址和空间。

        获取所有目标文件各个段的长度,属性和位置,将其按段合并起来,计算合并后的长度和位置建立映射关系并将各个文件的符号表整合成一个全局符号表。

  • 符号解析和重定位。

     首先在说符号解析与重定位前,得清楚什么是符号表。

     符号表,简单来讲就是存放符号的,那什么是符号呢?举个例子函数,变量都会生成符号,而符号的意义就是方便与地址对应,清楚每块地址所代表的意义。

    贴一个简单的代码,判断一下他的符号在中间文件的什么位置。

这其实是比较简单的,为初始化的变量和局部静态变量都是存放在.bss段的,而初始化的全局变量和局部静态变量是存放在.data段的,而局部非静态变量会生成指令存放在.text段。

.bss:gdata2,gdata3,gdata5,gdata6,data2,data3

.data:gdata1,gdata4,data1

.text:int data4=40,int data5=0,int data6=gdata;

这个就是符号表,可以对应上面的代码。不过看到这里应该注意到gdata3并未向我们所说的存放到.bss段,而是放在了这个所谓的*COM*里面,这是咋回事呢?

这是和c语言中的强弱符号有关系的。

强符号:全局已初始化的符号。

弱符号:全局为初始化的符号。

强弱符号的选取规则:

1.两强报错。

2.一强一弱选强符号。

3.两弱编译器自己选择。

一个项目可能有多个源文件。编译阶段都是每个文件单独编译的。可能在其他文件中存在强符号,所以没办法在编译期间确定具体的符号。因此将本文件的弱符号存放在*COM*块,而不是.bss段。

 了解了符号表是啥东西后,再来说下什么是符号解析。

 举个例子,细心的同学应该已经注意到了上面的gdata和sum,这两兄弟被放在了*UND*这又是个什么东东呢,这其实代表者未定义,因为编译是只针对于当前文件去进行的。那么自然是找不到gdata和sum的,所以在链接的时候就需要去全局符号表里找这两个哥们,找到了那符号解析就对了。没找到那么就是符号解析错误,编译器就要给你发牢骚了。

来看眼链接后的符号表

看到没这回这两哥们就有地址(这里指的是虚拟地址,上面的地址都是虚拟地址。只有程序在真正跑起来的时候才会被映射到真是的物理地址上,包括你自己在调试的时候都是虚拟地址)了,而且gdata也被放到了.bss段里面了。

好了那什么是重定位呢?

上图 链接前的汇编代码

注意这里gdata和main函数的地址都是无效地址,只是编译器暂时给标识一下。

链接后

这两个地址变成gdata的具体虚拟地址和Sum函数的相对位移偏移量。而符号重定向就是对.o文件中.text段指令中的无效地址给出具体的虚拟地址或者相对位移偏移量。

好了今天的回顾先到这。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值