一个源文件生成最终的可执行文件的过程可以分为四步:
- 预编译
- 编译
- 汇编
- 链接
如下图运行过程:
使用gcc命令不跟任何的选项的话,会默认执行预编译、编译、汇编、链接这整个过程,就会得到一个可执行文件,默认为a.out
1.预编译
gcc -E hello.c/ *-o hello.i */
-E::提示编译器只执行完预编译处理就停下来,对应生成一个 .i文件
- 编译
gcc -S hello.i /* -o hello.s*/ 生成汇编指令代码
-S:提示编译器执行完编译就停下,后续不再进行
- 汇编
gcc -c hello.s /* -o hello.o*/ 生成可重定位的二进制文件
-C:提示编译器执行完汇编就停下,后续不再进行。
- 链接
gcc -o hello hello.o 生成最终的可执行文件(hello)
四步分别完成的工作:
一、预编译阶段: .i 文件
- 删除所有的“#define”,并且展开所有的宏定义。(做文本替换 ,M10)
- 处理所有的条件预编译指令 例如“#if”、“#ifdef”、“#endif”等。
- 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。(这个过程是递归展开的,因为被包含的文件可能还包含了其他文件。)
- 删除所有的注释 “//”“/* */”
- 添加行号和文件标示名,以便于编译器产生调试用的符号信息及编译时产生编译错误和警告的时候能够显示行号;
- 保留所有的 #program 编译器指令,因为编译器需要它们。
此处引用别人一个举例说明:
二、.编译阶段: .s文件
词法分析、语法分析、语义分析、代码优化、汇总符号、
编译过程是整个程序构建的核心部分,编译成功,会将源代码由文本形式转换为机器语言,编译过程就是把预处理的文件进行上述一系列操作最终生成相应的汇编代码文件。
三、 汇编阶段
将汇编指令翻译成二进制格式,生成各个section,生成符号表
汇编过程调用汇编器 as 来完成,是用于 将汇编代码转换成机器可以执行的指令,每一条汇编代码都对应着一条机器指令。
四、 链接阶段
链接的主要内容就是把各个模块之间相互引用的部分 正确的衔接起来。他的工作就是把一些指令对其他符号地址的引用加以修正。主要包括地址和空间分配、符号决议、重定位。
- 合并各个section,调整section的起始位移和段大小
- 合并符号表,进行符号解析
- 符号重定位
符号决议:用符号来标示一个地址。
重定位:重新计算各个目标的地址过程。
最基本的链接叫做静态链接,就是把每个模块的源代码文件编译成目标文件(linux下:.o /windows下:.obj),然后将目标文件和库(库其实就是一组目标文件的包)一起链接形成最终的可执行文件。
相关命令:
objdump: 查看目标文件 或者可执行的目标文件的构成
readelf : 显示一个或者多个elf文件格式的目标文件的信息
ldd : 查看可执行程序用到哪些共享库
nm : 查看程序中函数 和变量的逻辑地址