嵌入式程序的编译、链接、运行

1.程序的编译、链接、运行

1.1从源程序到可执行文件

首先,通过readelf命令可以查看可执行文件的信息,了解可执行文件的结构

-h可以查看文件头部信息,包括该文件运行的平台、软件版本、程序入口地址等信息,section header的个数,

-S可以查看节头表section header table,包括不同段头section header(用于描述section相关信息)的相关信息,section header table 自身也是以一个 section 的形式进行存储的, 

一个可执行文件通常由代码段、数据段、BSS段、只读段等构成。

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

ELF header:文件头用来描述文件类型、要运行处理器平台、入口地址等。

Program header table

.init  初始化c程序运行的环境依赖,堆栈的初始化等

.text   代码段: 函数会被翻译成二进制指令放入其中、

.rodata 只读数据段:存放程序中定义的一些字符串,printf打印的字符串常量

.data  数据段:初始化的全局变量、静态局部变量(在静态存储区分配存储单元,在整个程序运行期间都不释放,但是其他函数都无法使用)

.bss  BSS段:未初始化的全局变量和静态变量,默认为0,因此该段是不占用空间的,但是该段的大小和起始地址是存在与节头表中的。各个变量的地址信息在symtab符号表中存储,加载器会紧挨着数据段的后面为BSS开辟一段内存空间。

.symtab

.debug 如果程序处于debug模式,则产生该段记录每一条二进制指令的对应的源码位置。

.line,.strtab

section header table :包括不同段头section header的相关信息

表1-1 可执行文件的段结构

1.2预处理、编译、汇编、链接

对于c程序到可执行文件的编译过程,可分为:预处理、编译、汇编、链接四个阶段

对应这四个编译阶段的工具分别是预处理器、编译器、汇编器、链接器。

目标文件一般分为:可重定位的、可执行的、可共享的,汇编生成的是可重定位的,链接生成的是可执行的目标文件,而可共享的文件一般以共享库的形式存在,在程序运行时需要动态加载如内存中。

1.2.1预处理(处理掉预处理命令,编译器不认识的)

   通常会为开发者提供一些预处理的命令,使用#进行标识,例如#include模块化编程,#define定义常量提高程序可读性,#if,#else,#endif令代码兼容不同处理器的架构和平台,编译控制#pragma可以制定编译器的状态,让编译器完成一些特定的动作如有选择的改变编译器的警告信息等。

   那么,预处理过程到底做了些什么哪?主要包括下面这些操作:

头文件展开:将#include包含的内容展开到当前位置

宏展开:展开所有的宏定义,并删除#define

条件编译:选择要参与编译的分支,将其余的分支丢弃

删除注释。

添加行号和文件名标识:编译时要使用

保留编译控制命令,编译时也有用

1.2.2编译(从c文件变为汇编文件)

编译过程将高级语言转换为低级语言,汇编文件是以段为单位的,包括代码段、数据段、bss等,各个段之间 彼此独立,与二进制目标文件已经很接近了。

c源文件到汇编文件的转换,是将c文件中的程序代码块、函数转换为代码段,全局变量、静态变量、常量转换到数据段、只读数据段,这是基本逻辑,实际分为五个步骤:

词义分析:对token进行检查看最小单元是否正确。token是字符流解析中有意义的最小记号单位,常见的token有c语言的关键字,各种标识符如函数名、变量名等,运算符、分隔符等,

语法分析:对上一阶段产生的token进行解析,看是否能够构成一个语法上正确的语法短句。如果少了个分号什么的,就是简单的看是否能构成一个句子,主谓宾一样,不考虑实际效用,就会报syntax error的字眼

语义分析:如果生成的合适的语法短句,那就要结合程序语义进行分析了,看语法短句中的实参形参是否匹配,使用的变量是否声明(无声明则判定程序员无使用该变量的意思,不符合实际语义,就会报错),又或者是除数为0则说明该语句无实际意义,break在循环或switch语句之外出现了。

中间代码生成:将语法分析输出的程序语句(仍以语法树形式存储)转换为中间代码(临时代码,三地址码,P-代码),

汇编代码

中间代码一般和平台是无关的,如果要在X86平台下,就根据X86指令集生成X86汇编程序,ARM平台生成ARM汇编程序、

1.2.3汇编(将汇编文件转换为目标文件)

 汇编器的主要工作是参考ISA指令集,将汇编代码翻译成对应的二进制指令。

通过编译与汇编,生成了可重定位的目标文件,在链接过程中,将各个起始地址为零的目标文件组装了起来,则该文件中的参考起始地址不再为0,我们的如果通过函数名对该函数进行调用也会受到影响,因此在链接过程中需要重新修改变量和函数地址,这个过程称为重定位,而我们汇编生成的文件是可重定位的文件,那么这个文件有什么特别的哪?在该文件中,我们将需要重定位的文件收集起来,生成一个重定位表,以section的形式进行保存就可以啦。

汇编过程中,汇编器会分析汇编语言中各个section的信息,收集各种符号,生成符号表(保存源程序中各种符号的信息,可以辅助编译器进行语法语义检查,也可以辅助地址空间的分配,重定位等,其本质上是一个结构体数组)。

我们调用一个符号和函数,如果在调用前声明了,在编译阶段不会报错,到链接时如果找不到才会报错。

编译器在给目标文件生成符号表的时候,如果没有找到符号的定义,会将该符号搜集到一个单独的符号便中,这个表就是重定位符号表。

1.2.4链接

链接主要分为三个过程,分段组装、符号决议、重定位。

分段组装:将各个目标文件分段组装,将相同的section组装到一起。另外要注意,链接器会在可执行文件中创造一个全局符号表,将各个文件符号表中的符号放入。

符号决议:_attribute可以声明符号的属性,例如强符号和弱符号

(1)强符号只能定义一次

(2)强符号与弱符号可以共存,当共存时强符号会覆盖掉若符号,未初始化的全局变量是弱符号

(3)多个弱符号共存时选择空间较大的     

重定位:在符号决议后,每个符号的地址还是原来的地址,需要根据重定位表进行修正各个符号的偏移offset,加上新的端基地址即可,生成新的符号表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值