1.preprocess
$gcc -E helloworld.c -o helloworld.i
预编译:可以先简单理解为处理“#”开头的行。
比如需要include的头文件,宏定义等。
2.compile
$gcc -S helloworld.i -o helloworld.s
编译:处理c语言(或者别的语言),变成汇编代码。
3.assemble
$gcc -c helloworld.s -o helloworld.o
汇编:处理汇编代码,变成二进制文件。
以上1~3步骤的作用范围都在一个单独的.c内部,要有相互之间的访问或者调用,就要由第4步 链接来完成。
先查看gcc默认的编译过程
$gcc helloworld.c --verbose 2>&1 |tee 1.log
打开1.log,有3处需要注意:
1. cc1 helloworld.c -o /tmp/xxxxxx.s //cc1,compiler
2. as xxxxxx.s -o /tmp/yyyyyy.o //as,assembler 3. collect2 //collect2是gcc与ld的一个中间工具 -dynamic-linker /lib64/ld-linux-x86-64.so.2 //指定动态链接器的地址 collect2是gcc与ld的一个中间工具 -L/...... //-L/dir 指定dir为查找路径
/usr/lib/....../crt1.o /usr/lib/....../crti.o /usr/lib/....../crtbegin.o /tmp/yyyyyy.o //helloworld.c生成的.o文件 -lc //-lx 指定libx.a 为需要链接到的文件 eg: -lc为链接时去libc.a中查找需要用到的.o
/usr/lib/....../crtend.o // crtbegin.o与crtend.o是用于C++在跑程序前构造和析构使用。 /usr/lib/....../crtn.o
/***********CRT简介*****************************************************************************************/
/* C RunTime */
/* 相关性: 与操作系统有关,与编译器有关 */
/* 作用: 协助库文件在操作系统上运行,把运行前后的工作(如初始化堆和栈,main返回后的处理)抽离出来,只需要在链接时组装进去就好。 */
/* 实现方式: 有的实现方式只需要链接crt0.o,新的实现方式需要链接crt1.o,crti.o,crtn.o。 */
/* ps:不同架构不同OS的实现方式都不一样,android则有自己的crt库。 */
/**********************************************************************************************************/
4.link
工具:ld
有了上面的默认collect2的链接信息,可以手动同样的命令使用ld进行链接,只需要ld “以上挑出来的关键选项” -o a.out就可以得到运行库了。
<1.静态链接
按顺序组合。
填充当前obj文件中使用到的别处定义的变量和函数的地址。有了组装顺序,也就有了相对地址,最终只是汇编语言中地址的跳转。
由于每个obj文件都有不同的section组成,最终的目标文件会进行section合并,否则逻辑组织庞大,也不便查找。这个过程是由linker script控制,$ld --verbose查看默认使用的linker script。
<2.动态链接
动态链接器与操作系统相关,不在此处总结。
GCC:GNU Compiler Collection
-E | 用(.c) 生成预编译后的文件 (.i) |
-S | 用(.c .i) 生成编译后的文件 (.s) |
-c | 用(.c .i .s)生成汇编后的文件 (.o) |
-verbose | 打印出整个编译链接过程的详细信息 |