编译、链接和生成的目标文件
基础编译链接知识
gcc编译C程序经过预处理,编译,汇编,链接。
GNU编译器套装(英语:GNU Compiler Collection,缩写为GCC),指一套编程语言编译器。
预编译
预处理就是展开所有的头文件、替换程序中的宏、解析条件编译并添加到文件中。
c语言中条件编译相关的预编译指令,包括 #define、#undef、#ifdef、#ifndef、#if、#elif、#else、#endif、defined。
删除所有的注释。添加行号和文件名标识,保留所有的 #pragma 编译器指令。
gcc -E test.c -o test.i
preprocessing 显示的预处理之后的源代码。
编译
编译:编译是将经过预编译处理的代码编译成汇编代码。对预处理后的.i文件进行词法分析,语法分析,语义分析及优化等等。
这里就是编译原理会涉及的东西,扫描,词法(有限状态机)语法(语法树 yacc)语义分析(对语法树的表达式标识类型),中间语言生成(IR 三地址码等),源码优化(优化寻址等),代码生成和目标代码优化等等。
gcc -S test.i -o test.s
可直接对生成的test.i文件编译,生成汇编代码
/usr/lib/gcc/x86_64-linux-gnu/5.4.0/cc1 test.c
现在版本的gcc将预编译和编译俩步骤合并到了一起,cc1这个程序。
汇编
将汇编代码转变为机器可以执行的指令,就根据汇编指令和机器指令的对照表去一一翻译即可。
gcc -c test.s -o test.o
汇编Assembly,gas汇编器负责将其编译为目标文件,.o文件是机器代码 (即 目标文件)
as -c test.s -o test.o
直接调用汇编器来做也可以。
链接
gcc test.o -o test
将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
ld其实会将许许多多的文件链接起来,包括一些glibc里的库文件的 .o 文件等等。
链接工作无非是把一些指令对其他符号地址的引用加以修正,解决了各个模块间符号引用的问题。主要过程有地址和空间的分配,符号决议(symbol resolution)和重定位等。
其他概念
- 重定位relocation:重新计算每个子程序或跳转的目标地址,然后把所有引用到这个符号的指令修正到正确的地址。这种重新计算各个目标地址的过程是重定位。
- 静态链接的基本过程:链接器在链接的时候,会根据你所引用的符号foo,自动去相应的func.c 模块查找foo的地址,然后将main.c模块中所有引用到foo的指令重新修正,让他们的目标地址为真正的foo函数的地址。
Reference
- 《程序员的自我修养》