gcc编译过程
gcc 编译过程
gcc 编译器可以将代码生成可执行文件,主要流程如下:
计算机只认识机器码,任何高级编程语言编写的程序运行都需要将代码转换为机器码,最后通过链接生成可执行文件
预处理
主要是处理源代码中以
"#"开头的预处理指令,比如#include、#define等,将其置换后直接插入程序文本中,得到另一个 C 程序,通常以.i作为扩展名
- 递归处理
#include预处理指令,将对应文件的内容复制到该指令的位置 - 删除所有的
#define指令,并在其被引用的位置递归地展开所有的宏 - 处理所有的条件预处理指令,如:
#if、#ifdef、#elif、#else、#endif等 - 删除所有的注释
- 添加行号和文件名标识
编译
将预处理文件进行一系列的词法分析、语法分析、语义分析以及优化,最终生成汇编代码,通常以
.s作为扩展名
- gcc 默认使用
AT&T格式的汇编语言,添加编译选项-masm = intel可以指定为intel格式 - 编译选项
-fno-asynchronous-unwind-tables用于生成没有 cfi 宏的汇编指令,提高可读性 - 若
printf()只有单一参数,gcc 的优化策略会将其替换成puts()以提高性能
汇编
汇编器根据汇编指令与机器指令对照表进行翻译,通常以
.o作为扩展名
- 此时的
.o文件是一个可重定位文件,可以使用objdump -sd .o文件名 -M intel查看其内容 - 由于此时还未进行链接,文件符号中的虚拟地址无法确定
链接
将目标文件及其依赖库进行链接,生成可执行文件。包括:地址和空间分配、符号绑定、重定位等
- gcc 默认为动态链接(添加编译选项
- static可指定使用静态编译) - 链接操作由链接器(
ld.so)完成,然后就会得到一个可执行文件,其包含了大量的库文件 - 链接完成后,上一步无法确定的虚拟地址就被修正为实际的符号地址,可以被加载到内存中正常执行
可执行文件
广义的可执行文件:文件中的数据是可执行代码的文件,例如
.out、.exe、.sh、.py狭义的可执行文件:文件中的数据是机器码的文件,例如
.out、.exe、.dll、.so
Windows PE
- 可执行程序
.exe - 动态链接库
.dll - 静态链接库
.lib
Linux ELF
- 可执行程序
.out - 动态链接库
.so - 静态链接库
.a
gcc 编译指令
以 C 语言编写一个简单的 hello.c 程序为例:
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}处理过程:
- 编译
hello.c,一步到位,默认生成a.out可执行文件
gcc hello.c- 编译
hello.c,一步到位,并指定生成文件名为hello可执行文件
gcc hello.c -o hello- 只执行预处理,生成
hello.i源文件
gcc -E hello.c -o hello.i- 只执行预处理和编译,生成
hello.s汇编文件
gcc -S hello.c- 也可以由
hello.i文件生成hello.s汇编文件,即:对预处理文件进行编译
gcc -S hello.i -o hello.s- 只执行预处理、编译和汇编,生成
hello.o目标文件
gcc -c hello.c- 也可以由
hello.i或hello.s生成hello.o目标文件
gcc -c hello.i -o hello.o
gcc -c hello.s -o hello.o- 由
hello.o目标文件链接成可执行文件hello
gcc hello.o -o hello按照上述方法生成的可执行文件
hello,在使用 gdb 调试时会显示:(No debugging symbols found in hello)因为 gcc 希望可执行文件更小、更快,默认是不加调试信息的
如果想要在编译时产生调试信息,需加上参数 -g
gcc -g hello.c -o hello
gcc 编译 32 位
gcc 可以指定编译为 32 位程序和 64 位程序,在 64 位机器上默认编译为 64 位程序
在 64 位系统下,安装下列库使 gcc 支持编译 32 位程序:
sudo apt install gcc-multilib g++-multilib module-assistant在上述 gcc 编译指令中,加上参数 -m32 即可指定编译为 32 位,加上 -m64 即可指定编译为 64 位
评论











