您的位置:首页 > 其它

拆解GCC命令的预处理-编译-汇编-链接4个阶段

2012-04-07 15:40 387 查看
在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帮我们一次性完成这四步

但是偶尔需要查看预处理过程或者汇编代码,这些命令还是十分有用的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: