关于从源代码到可执行目标文件的那些事

***关于从源代码到可执行目标文件的那些事

  在常规的软件开发工作中,我们只需对项目文件单击一下build或run便可生成可执行文件或者让应用程序运行起来;但是编译器在后台采取哪些措施、经历了哪些过程,我们都一无所知,尤其是程序越大,越容易出现问题,各模块间相互独立又相互耦合;问题排查的难度也越大。

   在Linux环境下用gcc命令对.c文件进行编译,生成可执行文件。经历了四个步骤:预处理、编译、汇编、链接。首先在预处理阶段,头文件和源文件被编译成.i文件,同时展开所有的宏定义、处理所有条件预编译指令、引入头文件、删除文件中注释、添加行号和文件名标识(以便发生错误时能定位到指定的文件中)。

   其次是编译阶段,整体来说该阶段包含:源代码扫描、语法分析、语义分析、源代码优化、代码生成和目标代码优化;源代码扫描,顾名思义就是通过一种有限状态机的算法将源代码的字符串分割成一系列的记号(Token),以便进行语法分析,分辨出关键字、标识符等;接着根据上下文无关语法生成以一个个语句为节点的语法树,并对语句中涉及的声明、类型匹配及转换进行检查,如有错误,则提示相应的编译错误,该过程便是语义分析;为了提高编译的速度和效率,编译器会对一些已知的结果进行优化,亦或删除一些临时变量,如此便完成源代码的优化;最后代码生成器将中间代码转换成目标机器代码,最后代码优化器对目标机器代码进行优化,比如:使用移位操作代替乘法或除法运算。

 最后是链接的过程,也是最为关键的过程,主要包括地址和空间分配、符号决议及重定位;程序中的变量和语句都占据了内存空间,编译器需要为他们分配相应的地址和存储空间;从汇编语言诞生的那一刻起,符号被广泛地用于表示变量或者函数的地址,因此符号存储了对应机器指令的地址,这便是符号决议;一个程序由由很多模块组成,每个模块由若干条指令组成;每当程序中某一模块被修改,修改位置后面的指令的位置也发生了相应的变化,这时需重新计算相应指令的地址,这便是重定位,这些过程都由编译器完成,程序员无需参与机器指令地址重新计算的过程。

  假如在一个项目中,main文件对另外一个.c文件中的foo()函数进行调用,在链接前无需知道foo()函数对应的内存地址,并将其地址置为0x00,进入链接阶段时,链接器会自动寻找foo()函数对应的地址,并将之前置为0x00的地址值替换掉,使得目标地址变成foo()函数真正的地址。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值