GCC之旅(1)--- GCC的编译流程

2019-07-13 02:49发布

转自:http://blog.csdn.net/elevenxjz/article/details/5951366   博主:记得以前考过这么一题,说汇编是一个什么过程。。。。好吧,当时正处于远去了汇编语言,黯淡了编译原理的阶段,脑海中一阵晃动纠结~~~ 不扎实呀呀~~~ 参考华清远见《嵌入式linux应用程序开发详解》第三章 后缀名的说明: .i  已经过预处理的C原始程序 .ii  已经过预处理的C++原始程序 .s/.S  汇编语言原始程序 .h  预处理文件(头文件) .o  目标文件 .a/.so  编译后的库文件   GCC编译流程分为4个步骤 1.       预处理(Preprocessing) 2.       编译(Compiling) 3.       汇编(Assembling) 4.       链接(Linking)   test.c   1 #include   2 int main(void)   3 {   4         printf("Hello World!/n");   5         return 0;   6 }      1.       预处理(Preprocessing):在这个阶段GCC将头文件包含进test.c文件,并生成文件test.i。   执行下面命令生成test.i: [root@localhost gccinfo]# gcc -E test.c -o test.i   test.i的文件内容:   1 # 1 "test.c"   2 # 1 ""   3 # 1 ""   4 # 1 "test.c"   5 # 1 "/usr/include/stdio.h" 1 3 4   6 # 28 "/usr/include/stdio.h" 3 4   7 # 1 "/usr/include/features.h" 1 3 4   8 # 335 "/usr/include/features.h" 3 4   9 # 1 "/usr/include/sys/cdefs.h" 1 3 4  10 # 360 "/usr/include/sys/cdefs.h" 3 4  …………. 750 # 2 "test.c" 2 751 int main(void) 752 { 753  printf("Hello World!/n"); 754  return 0; 755 }   2.       编译(Compiling):在这个阶段GCC检查test.i代码的规范性,确定有没有语法错误。确认无误后,则把test.i转化为汇编代码文件test.s。 执行下面命令生成test.s: [root@localhost gccinfo]# gcc -S test.i -o test.s test.s的文件内容:   1         .file   "test.c"   2         .section        .rodata   3 .LC0:   4         .string "Hello World!"   5         .text   6 .globl main   7         .type   main, @function   8 main:   9         leal    4(%esp), %ecx  10         andl    $-16, %esp  11         pushl   -4(%ecx)  12         pushl   %ebp  13         movl    %esp, %ebp  14         pushl   %ecx  15         subl    $4, %esp  16         movl    $.LC0, (%esp)  17         call    puts  18         movl    $0, %eax  19         addl    $4, %esp  20         popl    %ecx  21         popl    %ebp  22         leal    -4(%ecx), %esp  23         ret  24         .size   main, .-main  25         .ident  "GCC: (GNU) 4.3.0 20080428 (Red Hat 4.3.0-8)"  26         .section        .note.GNU-stack,"",@progbits   3.       汇编阶段(Assembling):在这个阶段GCC把test.s文件中的汇编代码转化为目标机器的机器代码文件test.o,为二进制文件。   执行下面命令生成test.o: [root@localhost gccinfo]# gcc -c test.s -o test.o   执行以下命令产看test.o [root@localhost gccinfo]# od -t c test.o   test.o文件内容: 0000000 177   E   L   F 001 001 001  /0  /0  /0  /0  /0  /0  /0  /0  /0 0000020 001  /0 003  /0 001  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0000040 354  /0  /0  /0  /0  /0  /0  /0   4  /0  /0  /0  /0  /0   (  /0 0000060  /v  /0  /b  /0 215   L   $ 004 203 344 360 377   q 374   U 211 0000100 345   Q 203 354 004 307 004   $  /0  /0  /0  /0 350 374 377 377 0000120 377 270  /0  /0  /0  /0 203 304 004   Y   ] 215   a 374 303  /0 0000140   H   e   l   l   o       W   o   r   l   d   !  /0  /0   G   C 0000160   C   :       (   G   N   U   )       4   .   3   .   0       2 0000200   0   0   8   0   4   2   8       (   R   e   d       H   a   t 0000220       4   .   3   .   0   -   8   )  /0  /0   .   s   y   m   t 0000240   a   b  /0   .   s   t   r   t   a   b  /0   .   s   h   s   t 0000260   r   t   a   b  /0   .   r   e   l   .   t   e   x   t  /0   . 0000300   d   a   t   a  /0   .   b   s   s  /0   .   r   o   d   a   t 0000320   a  /0   .   c   o   m   m   e   n   t  /0   .   n   o   t   e 0000340   .   G   N   U   -   s   t   a   c   k  /0  /0  /0  /0  /0  /0 0000360  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 * 0000420  /0  /0  /0  /0 037  /0  /0  /0 001  /0  /0  /0 006  /0  /0  /0 0000440  /0  /0  /0  /0   4  /0  /0  /0   +  /0  /0  /0  /0  /0  /0  /0 0000460  /0  /0  /0  /0 004  /0  /0  /0  /0  /0  /0  /0 033  /0  /0  /0 0000500  /t  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0   X 003  /0  /0 0000520 020  /0  /0  /0  /t  /0  /0  /0 001  /0  /0  /0 004  /0  /0  /0 0000540  /b  /0  /0  /0   %  /0  /0  /0 001  /0  /0  /0 003  /0  /0  /0 0000560  /0  /0  /0  /0   `  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0000600  /0  /0  /0  /0 004  /0  /0  /0  /0  /0  /0  /0   +  /0  /0  /0 0000620  /b  /0  /0  /0 003  /0  /0  /0  /0  /0  /0  /0   `  /0  /0  /0 0000640  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 004  /0  /0  /0 0000660  /0  /0  /0  /0   0  /0  /0  /0 001  /0  /0  /0 002  /0  /0  /0 0000700  /0  /0  /0  /0   `  /0  /0  /0  /r  /0  /0  /0  /0  /0  /0  /0 0000720  /0  /0  /0  /0 001  /0  /0  /0  /0  /0  /0  /0   8  /0  /0  /0 0000740 001  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0   m  /0  /0  /0 0000760   -  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 001  /0  /0  /0 0001000  /0  /0  /0  /0   A  /0  /0  /0 001  /0  /0  /0  /0  /0  /0  /0 0001020  /0  /0  /0  /0 232  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001040  /0  /0  /0  /0 001  /0  /0  /0  /0  /0  /0  /0 021  /0  /0  /0 0001060 003  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 232  /0  /0  /0 0001100   Q  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 001  /0  /0  /0 0001120  /0  /0  /0  /0 001  /0  /0  /0 002  /0  /0  /0  /0  /0  /0  /0 0001140  /0  /0  /0  /0 244 002  /0  /0 240  /0  /0  /0  /n  /0  /0  /0 0001160  /b  /0  /0  /0 004  /0  /0  /0 020  /0  /0  /0  /t  /0  /0  /0 0001200 003  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0   D 003  /0  /0 0001220 022  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 001  /0  /0  /0 0001240  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001260  /0  /0  /0  /0 001  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001300 004  /0 361 377  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001320 003  /0 001  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001340 003  /0 003  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001360 003  /0 004  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001400 003  /0 005  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001420 003  /0  /a  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001440 003  /0 006  /0  /b  /0  /0  /0  /0  /0  /0  /0   +  /0  /0  /0 0001460 022  /0 001  /0  /r  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0  /0 0001500 020  /0  /0  /0  /0   t   e   s   t   .   c  /0   m   a   i   n 0001520  /0   p   u   t   s  /0  /0  /0 024  /0  /0  /0 001 005  /0  /0 0001540 031  /0  /0  /0 002  /t  /0  /0  /n 0001551   4.       链接(Linking):在成功编译之后,就进入了链接阶段。在这里涉及到一个重要的概念:函数库。重新查看这个小程序时,在这个程序中并没有定义“printf”的函数实现,且在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现“printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,Gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。 函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a” 。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。GCC 在编译时默认使用动态库。完成了链接之后,GCC 就可以生成可执行文件。   执行以下命令生成可执行文件test [root@localhost gccinfo]# gcc test.o -o test 同时也可以通过od命令产看其内容。   以上4个编译步骤可以一次完成,执行下面命令即可: [root@localhost gccinfo]# gcc test.c -o test 编译程序时,gcc会自动帮你连接所有编译的文件名及标准函数库. 首先GCC会把所有的原始文件都转成目标文件,然后会自动调用linker 来连接相关文件名(事实上linker是个文件名为ld的程序,而不是gcc 本身提供的功能,我们可以说gcc和ld的关系是相当密切的),GG同样 也知道标准函数库的位置,并且在调用ld时传入相关信息。