写着玩-强撸静态链接
0x00 口胡
我写代码,以C的hello world为例,第一遍写时是用VC6,当时就傻逼的抄代码,然后F7,然后就F5,然后就输出了,当时就觉着自己好屌,也觉着VC6好屌。现在长大了,开始学着装逼了,用VS,用gcc,g++,但还是觉着VC6好用,没办法,自己太菜,而且也没写过太多代码,VC6够用了,而且也都习惯了。。。
学了编译之后,才算大体上理解从代码文本到可执行的二进制程序是怎样的一个过程,但是也只是知道了个大概,毕竟现实中真正在用的编译器那都是有好多好多功能的,是很强大的,他的具体过程也远没我想的那么简单,既然学了编译,有了大体框架,那么,现在就来丰富细节吧。。。
编程时,都是写完代码,然后一个按钮,或者一行命令,然后就生成了可执行程序,但这个按钮或命令是做了很多事的,大体分为四个步骤:预处理、编译、汇编、链接。
给张图:
没啥好说的,直接撸。。。
0x01 这条命令干了啥
gcc hello.c
1. 预编译
预编译过程主要处理那些源代码文件中的以”#”开始的预编译指令。比如”#include”、”#define”等,主要规则如下(这就不截图了,手打加深印象):
- 将所有的”#define”删除,并且展开所有的宏定义
- 处理所有条件预编译指令,比如”#if”、”#ifdef”、”#elif”、”#else”、”#endif”。
- 处理”#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其它文件
- 删除所有的注释”//“和”/**/“。
- 添加行号和文件名标识,比如#2”hello.c”2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
- 保留所有的#pragma编译器指令,因为编译器要使用
经过预编译后的.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到.i文件中。所以当我们无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定问题(小技巧get)
2. 编译
编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。该过程相当于如下命令:1
gcc -S hello.i -o hello.s
现在版本的GCC把预编译和编译两个步骤合并成一个步骤,使用一个叫做cc1的程序,位于/usr/lib/gcc/i486-linux-gnu/4.1/,所以1
cc1 hello.c
可以达到相同的效果,或1
gcc -S hello.c -o hello.s
这些都可以得到汇编文件hello.s。对于C语言,这个预编译和编译的程序是ccl,对于C++,是cclplus;Objective-C是cclobj;fortan是f771;java是jc1。所以实际上gcc这个命令只是这些后台程序的包装,它会根据不同的参数要求去调用预编译程序cc1、汇编器as、链接器ld。
3. 汇编
汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。就是根据汇编指令和机器指令的对照表意义翻译就行。对应的指令:1
as hello.s -o hello.o
或1
gcc -c hello.s -o hello.o
或
gcc -c hello.c -o hello.o
4. 链接
懒,直接上图:
下面就是一堆编译器到底干了什么的东西,具体的可以参考我以前写的blog,好像是第一篇吧。。。
先到这。。。