Linux GCC的编译过程主要分为四个阶段
预处理(Preprocess)
编译(Compile)
汇编(Assemble)
连接(Link)
本文使用最基础的hello world程序作为演示
#include<stdio.h>
int main()
{
printf("hello, world\n");
}
预处理(Preprocess)
预处理是GCC进行编译的第一阶段,主要处理源代码中以“#”开头的预处理指令,比如“#include”,“#define”。在这个阶段中会使用编译器ccl作为工具,将其转换后直接插入到程序文本中得到另一个c程序,通常以“.i”作为文件拓展名。
如果想要仅生成生成预处理产生的 .i 文件可以使用下面的命令行
$ gcc -E hello.c -o hello.i
另注:gcc中最简单的编译命令为
$ gcc -o 指定文件名 文件
于是我们将得到一个杂乱的文件
提取其中重点内容得到以下代码
# 1 "hello.c"
# 1 "<biult-in>"
# 1 "<command-line>"
...
extern int printf (const char *__restrict __format, ...);
...
int main()
{
printf("Hello,World\n");
return 0;
}
预处理的一些处理规则如下
- 递归处理“#include”预处理指令,将对应文件的内容复制到该指令的位置;
- 删除所有的“#define”指令,并且在其被引用的位置递归的展开所有的宏定义;
- 处理所有的条件预处理指令:“#if”、“#ifdef”、“#elif”、“#else”、“endif”等
- 删除所有的注释;
- 添加行号和文件标识
编译阶段
编译阶段是GCC编译的第二阶段,该阶段进行将对预处理文件进行一系列的词法分析,语法分析,语义分析以及优化,最终进行优化,编译器的语法检测也是从此时开始的,出错后转至出错处理器进行处理。经过编译后最后会形成汇编代码,GCC默认使用AT&T格式。
在命令中添加-S可查看生成的汇编文件,-S的操作对象可以是hello.i或者hello.s
$ gcc -S hello.c -o hello.s
如果想生成符合我们使用习惯的intel格式,可以添加命令 -masm=intel,添加命令-fno-asynchronous-unwind-tables后可生成没有cfi宏的汇编指令,增加可读性
汇编阶段
汇编阶段是GCC编译的第三阶段,汇编器根据汇编指令与机器指令的对照表进行翻译,将hello.s 汇编成目标文件hello.o。可以使用-c命令生成目标文件
$ gcc -c hello.c -o hello.o
$ gcc -c hello.i -o hello.o
由于此时文件是可重定向文件,所以需要指令读取
$ objdump -sd hello.o -M intel
因为还未进行链接,所以对象文件中的虚拟地址无法确认
链接阶段
链接阶段是GCC编译的第四阶段,可分为静态链接和动态链接两种,在这一阶段中,编译器将目标文件和依赖库进行链接生成可执行文件,主要包括地址和空间分配,符号绑定,和重定位等操作,可以通过以下指令来完成
$ gcc hello.o -o hello -static
关于静态链接和动态链接可以参考以下三篇博客
https://www.cnblogs.com/tracylee/archive/2012/10/15/2723816.html
Comments 2 条评论
贾哥说pwn这种东西不手到擒来
@2362494487 这里边水太深,已经快淹死了