原文链接:http://blog.csdn.net/jmy5945hh/article/details/7435234
在linux下使用gcc命令编译程序时,整个过程实际上在底层处理分为四个步骤--预处理/编译/汇编/连接。
下面通过gcc的不同命令参数来拆解这四个步骤。
源代码:hello.c
#include <stdio.h>
#define PP printf
int main(int argc, char **argv) {
int a = 5;
PP("a = %d\n", a);
return 0;
}
1、预处理(C预处理器)
gcc -E hello.c -o hello.i//使用cpp命令
打开预处理后的文件hello.i,发现已经与源文件大有不同。主要区别我在注释处说明。
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
....
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__));
# 936 "/usr/include/stdio.h" 3 4
# 2 "hello.c" 2 //以上是插入的stdio.h头文件内容
int main(int argc, char **argv) {
int a = 5;
printf("a = %d\n", a); //此处,宏定义PP被替换为printf
return 0;
}
2、编译(C编译器)
gcc -S hello.i -o hello.s//使用cc1命令
打开编译后的文件hello.s,所有语句均已替换为汇编语言,这对于嵌入式开发非常有用。
.file "hello.c"
.section .rodata
.LC0:
.string "a = %d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
movl $5, 28(%esp)
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
3、汇编(汇编器)
gcc -c hello.s -o hello.o//使用as命令
此时hello.s已经被编译为机器码hello.o,可以在vim中以二进制形式打开:
0000000: 7f45 4c46 0101 0100 0000 0000 0000 0000 .ELF............
0000010: 0100 0300 0100 0000 0000 0000 0000 0000 ................
0000020: 3001 0000 0000 0000 3400 0000 0000 2800 0.......4.....(.
0000030: 0d00 0a00 553f 3f3f 3f3f 3f3f 203f 4424 ....U??????? ?D$
0000040: 1c05 0000 003f 0000 0000 3f54 241c 3f54 .....?....?T$.?T
0000050: 2404 3f04 243f 3f3f 3f3f 3f00 0000 003f $.?.$??????....?
0000060: 3f00 0000 6120 3d20 2564 0a00 0047 4343 ?...a = %d...GCC
0000070: 3a20 2855 6275 6e74 752f 4c69 6e61 726f : (Ubuntu/Linaro
0000080: 2034 2e36 2e31 2d39 7562 756e 7475 3329 4.6.1-9ubuntu3)
0000090: 2034 2e36 2e31 0000 1400 0000 0000 0000 4.6.1..........
00000a0: 017a 5200 017c 0801 1b0c 0404 3f01 0000 .zR..|......?...
00000b0: 1c00 0000 1c00 0000 0000 0000 2d00 0000 ............-...
00000c0: 0041 0e08 3f02 420d 0569 3f0c 0404 0000 .A..?.B..i?.....
00000d0: 002e 7379 6d74 6162 002e 7374 7274 6162 ..symtab..strtab
00000e0: 002e 7368 7374 7274 6162 002e 7265 6c2e ..shstrtab..rel.
00000f0: 7465 7874 002e 6461 7461 002e 6273 7300 text..data..bss.
0000100: 2e72 6f64 6174 6100 2e63 6f6d 6d65 6e74 .rodata..comment
0000110: 002e 6e6f 7465 2e47 4e55 2d73 7461 636b ..note.GNU-stack
0000120: 002e 7265 6c2e 6568 5f66 7261 6d65 0000 ..rel.eh_frame..
0000130: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000140: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000150: 0000 0000 0000 0000 1f00 0000 0100 0000 ................
0000160: 0600 0000 0000 0000 3400 0000 2d00 0000 ........4...-...
0000170: 0000 0000 0000 0000 0400 0000 0000 0000 ................
0000180: 1b00 0000 0900 0000 0000 0000 0000 0000 ................
0000190: 0004 0000 1000 0000 0b00 0000 0100 0000 ................
00001a0: 0400 0000 0800 0000 2500 0000 0100 0000 ........%.......
00001b0: 0300 0000 0000 0000 6400 0000 0000 0000 ........d.......
00001c0: 0000 0000 0000 0000 0400 0000 0000 0000 ................
00001d0: 2b00 0000 0800 0000 0300 0000 0000 0000 +...............
00001e0: 6400 0000 0000 0000 0000 0000 0000 0000 d...............
00001f0: 0400 0000 0000 0000 3000 0000 0100 0000 ........0.......
0000200: 0200 0000 0000 0000 6400 0000 0800 0000 ........d.......
0000210: 0000 0000 0000 0000 0100 0000 0000 0000 ................
0000220: 3800 0000 0100 0000 3000 0000 0000 0000 8.......0.......
0000230: 6c00 0000 2b00 0000 0000 0000 0000 0000 l...+...........
0000240: 0100 0000 0100 0000 4100 0000 0100 0000 ........A.......
0000250: 0000 0000 0000 0000 3f00 0000 0000 0000 ........?.......
0000260: 0000 0000 0000 0000 0100 0000 0000 0000 ................
0000270: 5500 0000 0100 0000 0200 0000 0000 0000 U...............
0000280: 3f00 0000 3800 0000 0000 0000 0000 0000 ?...8...........
0000290: 0400 0000 0000 0000 5100 0000 0900 0000 ........Q.......
00002a0: 0000 0000 0000 0000 1004 0000 0800 0000 ................
00002b0: 0b00 0000 0800 0000 0400 0000 0800 0000 ................
00002c0: 1100 0000 0300 0000 0000 0000 0000 0000 ................
00002d0: 3f00 0000 5f00 0000 0000 0000 0000 0000 ?..._...........
00002e0: 0100 0000 0000 0000 0100 0000 0200 0000 ................
00002f0: 0000 0000 0000 0000 3803 0000 3f00 0000 ........8...?...
0000300: 0c00 0000 0900 0000 0400 0000 1000 0000 ................
0000310: 0900 0000 0300 0000 0000 0000 0000 0000 ................
0000320: 3f03 0000 1500 0000 0000 0000 0000 0000 ?...............
0000330: 0100 0000 0000 0000 0000 0000 0000 0000 ................
0000340: 0000 0000 0000 0000 0100 0000 0000 0000 ................
0000350: 0000 0000 0400 3f3f 0000 0000 0000 0000 ......??........
0000360: 0000 0000 0300 0100 0000 0000 0000 0000 ................
0000370: 0000 0000 0300 0300 0000 0000 0000 0000 ................
0000380: 0000 0000 0300 0400 0000 0000 0000 0000 ................
0000390: 0000 0000 0300 0500 0000 0000 0000 0000 ................
00003a0: 0000 0000 0300 0700 0000 0000 0000 0000 ................
00003b0: 0000 0000 0300 0800 0000 0000 0000 0000 ................
00003c0: 0000 0000 0300 0600 0900 0000 0000 0000 ................
00003d0: 2d00 0000 1200 0100 0e00 0000 0000 0000 -...............
00003e0: 0000 0000 1000 0000 0068 656c 6c6f 2e63 .........hello.c
00003f0: 006d 6169 6e00 7072 696e 7466 0000 0000 .main.printf....
0000400: 1200 0000 0105 0000 2200 0000 020a 0000 ........".......
0000410: 2000 0000 0202 0000 0a ........
也可以在终端使用命令查看.o文件中包括的函数:
jimmy@MyPet:~/code/learnc$ nm hello.o
00000000 T main
U printf
4、链接(连接器)
gcc hello.o -o hello//使用ld命令
这样就最终生成了可执行文件hello。
jimmy@MyPet:~$ ./hello
a = 5
上面四步就是gcc生成可执行文件的全部步骤啦!
虽然我们常用的方式是直接让gcc帮我们一次性完成这四步。
但是偶尔需要查看预处理过程或者汇编代码,这些命令还是十分有用的。