编译链接原理

您所在的位置:网站首页 编译的概念是什么时候提出的问题 编译链接原理

编译链接原理

2024-07-17 21:57| 来源: 网络整理| 查看: 265

总述:

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

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

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

    宏替换(删除#define,并且展开所有的宏定义)    递归展开头文件(处理#include预编译指令,将包含的文件插入到该预编译指令的位置)    删除预编译指令(处理所有的条件预编译指令,例如"#if","#endif","#ifdef","dlif","#else"的等)    删除注释(删除”//“和"/**/")    添加行号和文件标识    保留#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的默认值。所以是正确的。 

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

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

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

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

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

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

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

编译阶段:

 一、.o文件

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

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

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

        四、.bss段

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

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

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

        强弱符号的使用规则:

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

        三、指令段和数据段

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

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

链接阶段:

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

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

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

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

      3.分配地址和空间

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

运行原理:

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

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

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

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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3