编译链接原理——总述+编译阶段+链接阶段+运行原理

本文深入剖析了程序从源代码到可执行文件的全过程,包括编译、预编译、汇编和链接四个阶段,详细解释了每个阶段的功能与作用,帮助理解程序运行的本质。

总述:

        在平常的应用程序开发过程中,我们很少需要关注编译和链接的过程,因为通常都是在集成的开发环境下运行,因此一般编译和链接都是一步完成,通常将这种编译和连接合并到一起的过程称为构建。这样虽然简便,但是在这整个过程中,有时出现问题时,我们只能看到问题的表现,而很难看清本质性问题,所以对于这些一步完成的操作背后到底是怎样的,我们需要深入了解,方便在以后遇到问题能后看清本质,快速解决。为什么要对源文件进行编译链接生成最后的可执行文件呢?一是机器只识别0/1代码,二是源文件在磁盘上存储,要运行源文件就必须将源文件转为机器可识别的二进制文件并将转换后的文件加载到内存中才可以。那么首先是对于编译链接原理的背后所作的事情的一个简单了解;

       程序的运行过程分为两大阶段,编译阶段和链接阶段,同时编译阶段又划分为三步预编译、编译和汇编,再逼那一阶段完成时,进行链接,那么具体每部所做的内容如下:

一、预编译(生成.i文件)

  1.     宏替换(删除#define,并且展开所有的宏定义)
  2.     递归展开头文件(处理#include预编译指令,将包含的文件插入到该预编译指令的位置)
  3.     删除预编译指令(处理所有的条件预编译指令,例如"#if","#endif","#ifdef","dlif","#else"的等)
  4.     删除注释(删除”//“和"/**/")
  5.     添加行号和文件标识
  6.     保留#progma

二、编译(生成.s文件)

       例如:int sum(int a,int b,int c = 10);

                   int sum(int a,int b = 20,int c);

                  int sum(int a,int b,int c);

     这三行代码是对sum函数的声明,在进行语法分析的时候第二行是错误的,因为函数的默认值是从左向右依次进行赋值的。但在进行到语义分析时是正确的,结合上下文进行分析,第一句已经对c进行了默认值,在第二句进行分析时,c是有默认值的,这个值是10,然后再到b的默认值。所以是正确的。 

  1.     词法分析 ;例如:int 1a = 1;(这里是1不是小写的’L‘)//错误。定义变量只能以字母或下划线开头
  2.     语法分析;例如:int a = 10;delete a;  //错误 在编译阶段,会识别到delete后必须是指针,而这里并不是指针,
  3.     语义分析(结合上下文进行分析);     
  4.     代码优化

三、汇编(生成.o文件,称为可重定位的二进制文件)

 在汇编阶段,将代码翻译为二进制指令后,通过在Linux操作系统下,对汇编后的文件进行查看,得出,在汇编完成后还有以下事情未进行处理: 

     (1)弱符号位置未进行处理(2)虚拟地址以及虚拟位移未进行处理(3)符号表中的外部符号进行处理

  1.    将 指令代码翻译成二进制指令

四、链接(生成.exe文件,称为可执行的二进制文件)

  1.     合并段(相同段之间)和符号表
  2.     进行符号解析:在符号引用的地方找到符号定义的地方
  3.     分配地址和空间
  4.     符号的重定位

编译阶段:

 一、.o文件

        编译阶段经过预编译、编译和汇编处理后生成一个.o文件(以Linux系统为例),又编译器编译源代码后生成的文件叫做目标文件。则目标文件就是源代码编译后但未进行连接的那些中间文件(windows下的.obj和Linux下的.o),它跟可执行文件的内容和结构很相似,所以一般和可执行文件采用同一种格式存储。也就是从结构上来说,目标文件是已经变异后的可执行文件,知识没有经过链接阶段,其中有些符号或者地址没有被调整。

       二、目标文件的内容以及存放

        那么目标文件中至少有编译后的机器指令代码、数据。当然,除了这些内容外,目标文件中还包含了链接时所需要的一些信息,例如符号表、调试信息、字符串等。一般目标文件将这些信息按照不同的属性按“段”的形式进行存储。程序源代码编译后的机器指令经常被放在代码段里,即“.text”中。全局变量和局部静态变量数据经常放在数据段,即".data"中。未初始化的全局变量和局部静态变量放在.bss段中。

        四、.bss段

        .bss段存储的是未定义的全局变量和局部静态变量。但.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,他并没有内容,所以他在文件中也不占据空间。在这里我们引入强弱符号(c语言中,只关心全局变量)的概念:

        强符号:已初始化的全局变量;

        弱符号:未初始化的全局变量;

        强弱符号的使用规则:

  1. 两强符号:重定义错误
  2. 一强一弱符号:选择强符号
  3. 两弱符号:选取字节数较大的

        三、指令段和数据段

        在编译阶段结束会,生成可重定位的二进制文件即目标文件,将文件中的指令数据等信息分别按照属性存储在虚拟地址空间中,数据区域对进程来说是可读写的,而指令段对与进程而言只是可读的,所以这两个区域的权限是可读写和只读。这样就会防止指令被有意无意的篡改,同时当程序运行多个该程序的副本时,它们的指令是相同的,所以内存中只须要保存一份该程序的指令部分。并且分开存储有利于提高CPU的缓存命中率。 

        4G的虚拟地址空间如图所示:

链接阶段:

       一、 在编译阶段完成后生成.o的目标文件,进入到链接阶段,对于链接器来说,整个链接过程就是将几个输入目标文件加工合并成一个输出文件。这里的输入文件是目标文件即.o文件,输出文件是可执行的二进制文件。链接后的文件存储和目标文件的存储一样,都是将不同的信息属性存放到对应的段中,唯一不同的是可执行的二进制文件会对一些符号进行解析,调整一些地址等;

        那么,在链接阶段具体都要做些什么呢?

       1.符号表和段合并:将相同性质的段合并到一起

      2.符号解析:在符号引用的地方找到符号定义的地方

      3.分配地址和空间

      4.符号的重定位: 处理虚假偏移量

运行原理:

        在编译链接阶段结束后,也就是生成了 可执行的二进制文件;但该文件并不能直接进行运行,因为此时的文件并未在内存中,也就是说,操作系统在运行一个程序时,需要指令和数据,并且必须将所要执行的程序加载到内存上;

那么,在运行时,需要做以下的事情:

  1. 创建虚拟地址和物理内的映射结构体;按照段页式进行映射,以4K大小对齐;
  2. LOAD加载器,将指令和数据加载到内存中;
  3. 将第一行指令的地址写入PC寄存器中;

       ps:本文中的测试,均在Linux下进行;

编译程序通常分为前端和后端两个部分,它们在编译过程中承担不同的职责并包含不同的阶段。前端处理主要负责从源程序到中间代码的转换,包括以下几个阶段: 参考资源链接:[编译原理课后习题答案详解](https://wenku.youkuaiyun.com/doc/3iaf2wcvkc?spm=1055.2569.3001.10343) 1. 词法分析:将源程序文本分解为一系列的标记(tokens),每个标记代表了程序中的一个符号,如关键字、标识符、运算符等。 2. 语法分析:根据语言的语法规则,将标记组织成语法结构,通常是抽象语法树(AST)的形式。 3. 语义分析:检查语法结构的语义正确性,并处理类型检查和符号表的管理等任务。 4. 中间代码生成:将AST转换成中间代码,这是一种低级的、与机器无关的代码表示形式,便于进一步优化和转换。 后端处理则负责将中间代码转换成目标代码,包括以下几个阶段: 1. 中间代码优化:对中间代码进行优化以提高运行效率。 2. 目标代码生成:根据特定的机器语言规范,将优化后的中间代码转换成目标代码。 3. 符号表管理:在编译的各个阶段对符号表进行管理,包括变量和函数的存储分配。 4. 出错处理:在编译过程中检测错误并给出相应的错误信息。 总的来说,前端处理关注的是源代码的解析和语义的正确性验证,而后端处理关注的是中间代码到目标代码的转换以及代码的优化。前端和后端的主要区别在于它们处理的代码层面和依赖的关系不同。前端与源程序的具体语法和语义密切相关,而与目标机器语言的依赖较小;后端则相反,与目标机器语言紧密相关,而与源程序的语言特性关联较少。 参考资源链接:[编译原理课后习题答案详解](https://wenku.youkuaiyun.com/doc/3iaf2wcvkc?spm=1055.2569.3001.10343)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值