gcc的编译过程及相关简介
我们知道,在平常编译程中所使用的gcc或g++只是一套工具集的外包封装,具体的编译工作并不是由gcc本身来完成的,而gcc为了完成整个编译工作,它直接或间接调用的工具主要包括预处理器/分析编译器/汇编器/连接器(preprocessor/parser-compiler/assembler/linker),比如cpp,cc1,cc1plus,as和ld等等。
如何查看gcc在编译过程的细节?这很简单,带上-v或-###参数(两者有差别,具体请看man手册:man gcc)即可,如下:
[root@www 1]# uname -a Linux www.t1.com 2.6.38.8 #2 SMP Wed Nov 2 07:52:53 CST 2011 x86_64 x86_64 x86_64 GNU/Linux [root@www 1]# vi main.c [root@www 1]# cat main.c #include <stdio.h> int main() { printf("test\n"); return 0; } [root@www 1]# gcc main.c -o test [root@www 1]# gcc main.c -o test -v Using built-in specs. Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux Thread model: posix gcc version 4.4.4 20100726 (Red Hat 4.4.4-13) (GCC) COLLECT_GCC_OPTIONS='-o' 'test' '-v' '-mtune=generic' /usr/libexec/gcc/x86_64-redhat-linux/4.4.4/cc1 -quiet -v main.c -quiet -dumpbase main.c -mtune=generic -auxbase main -version -o /tmp/ccH6Ak4I.s ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.4.4/include-fixed" ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../x86_64-redhat-linux/include" #include "..." search starts here: #include <...> search starts here: /usr/local/include /usr/lib/gcc/x86_64-redhat-linux/4.4.4/include /usr/include End of search list. GNU C (GCC) version 4.4.4 20100726 (Red Hat 4.4.4-13) (x86_64-redhat-linux) compiled by GNU C version 4.4.4 20100726 (Red Hat 4.4.4-13), GMP version 4.3.1, MPFR version 2.4.1. GGC heuristics: --param ggc-min-expand=54 --param ggc-min-heapsize=46795 Compiler executable checksum: 4c5e5ba5bee4ba181c716bf2deb3c162 COLLECT_GCC_OPTIONS='-o' 'test' '-v' '-mtune=generic' as -V -Qy -o /tmp/ccZiL2vw.o /tmp/ccH6Ak4I.s GNU assembler version 2.20.51.0.2 (x86_64-redhat-linux) using BFD version version 2.20.51.0.2-5.11.el6 20091009 COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/ LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-o' 'test' '-v' '-mtune=generic' /usr/libexec/gcc/x86_64-redhat-linux/4.4.4/collect2 --eh-frame-hdr --build-id -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o test /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../.. /tmp/ccZiL2vw.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crtn.o [root@www 1]# gcc main.c -o test -### Using built-in specs. Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux Thread model: posix gcc version 4.4.4 20100726 (Red Hat 4.4.4-13) (GCC) COLLECT_GCC_OPTIONS='-o' 'test' '-mtune=generic' "/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/cc1" "-quiet" "main.c" "-quiet" "-dumpbase" "main.c" "-mtune=generic" "-auxbase" "main" "-o" "/tmp/ccmnQGfv.s" COLLECT_GCC_OPTIONS='-o' 'test' '-mtune=generic' "as" "-Qy" "-o" "/tmp/ccL81Sw4.o" "/tmp/ccmnQGfv.s" COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/:/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/ LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-o' 'test' '-mtune=generic' "/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/collect2" "--eh-frame-hdr" "--build-id" "-m" "elf_x86_64" "--hash-style=gnu" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "test" "/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crt1.o" "/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crti.o" "/usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtbegin.o" "-L/usr/lib/gcc/x86_64-redhat-linux/4.4.4" "-L/usr/lib/gcc/x86_64-redhat-linux/4.4.4" "-L/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64" "-L/lib/../lib64" "-L/usr/lib/../lib64" "-L/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../.." "/tmp/ccL81Sw4.o" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "-lc" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "/usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtend.o" "/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crtn.o" [root@www 1]#
在上面显示的信息中,我们已经看到了gcc对cc的使用:
/usr/libexec/gcc/x86_64-redhat-linux/4.4.4/cc1 …
还有对as的使用:
as -V -Qy -o main.o /tmp/ccki08WG.s
关于cc无需多说,而这里的as也就是GNU Assembler,即gas,是GNU项目的汇编器,也是gcc默认的后端工具,属于GNU Binutils工具集的一部分。今天晚上回来,无意翻看了glibc的源代码(版本:glibc-2.17),发现glibc源代码里有大量的as汇编指令,比如这个:
asm (".type __gettimeofday, %gnu_indirect_function"); /* This is doing "libc_hidden_def (__gettimeofday)" but the compiler won't let us do it in C because it doesn't know we're defining __gettimeofday here in this file. */ asm (".globl __GI___gettimeofday\n" "__GI___gettimeofday = __gettimeofday");
这是虾米意思?额,我现在也不知道(正是因这,才有了这篇初探短文),虽然在这里都有答案,但我还没来得及仔细研看,:)。
glibc源代码里的这些都是c内联汇编,通用的语法为:
asm volatile (“汇编语句”);
其中,asm可以换为__asm__,volatile可以换为__volatile__,它们之间并没有什么差别,只不过是从各个标准兼容性上的考虑。
ld在上面gcc编译过程的信息里没有显示出来,难道gcc没有用到ld?当然不是,相比gcc对cc1和as的直接调用,它对ld的使用是间接的,即通过程序collect2来进行。不信?试试:
[root@www 1]# gcc main.c -o test -v -llenky ... COLLECT_GCC_OPTIONS='-o' 'test' '-v' '-mtune=generic' /usr/libexec/gcc/x86_64-redhat-linux/4.4.4/collect2 --eh-frame-hdr --build-id -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o test /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../.. /tmp/cc62LLGu.o -llenky -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.4.4/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../lib64/crtn.o /usr/bin/ld: cannot find -llenky collect2: ld returned 1 exit status
名为liblenky.so库当然是没有的,所以上面的编译连接出错,注意最后一句提示信息:collect2: ld returned 1 exit status
这个collect2到底为何物?我没看gcc的源代码,所以尚不得而知,不过参考资料http://alpha-blog.wanglianghome.org/2011/04/14/collect2/给出了部分解释:
collect2主要实现了两个额外的功能,都是为C++程序准备的。其一是生成代码调用全局静态对象的构造函数和析构函数;其二是支持Cfront方式生成模板代码。
C++程序的全局静态对象,需要在main函数运行前构造好,在main函数执行完成后析构掉。构造函数和析构函数是由程序员编写的,那么该由谁负责调用呢?通常来讲,调用这些函数的工作由初始化代码完成,比如,GNU ld就可以生成这些代码。如果这些代码不能由linker自动生成,就需要collect2的帮助。
collect2调用ld生成一个执行程序,然后调用nm程序,根据命名规则,查找所有的调用全局静态对象构造函数和析构函数的函数,并将这些函数保存为一张表,再生成一段代码遍历每个表项,调用对应的函数。所有这些信息被保存为一个C程序,然后编译、汇编为目标文件,在链接时作为第一个目标文件传给linker,做第二次链接。此功能实现在gcc/collect2.c文件内。通过在config.gcc文件里查找use_collect2=yes来确定哪些系统使用了该功能,PC上默认是关闭的。
Cfront支持代码实现在gcc/tlink.c文件里,这里的调用关系就更复杂了。gcc在编译C++程序时不生成任何模板代码,而是使用-frepo生成一个辅助文件,collect2调用ld做链接后,分析错误信息,提取未定义的符号,然后通过分析所有的repo文件,判断要生成哪些模板代码,并调用gcc重新编译特定文件,然后再链接,如果还有未定义的符号,则重复上述过程。如此反复,最多17次。如果还不能正确链接,则报错。
查看collect2的手册
info gccint ‘collect2′
查看Cfront模型
info gcc ‘C++ Extensions’ ‘Template Instantiation’
关于collect2的更多内容,我现在也是知之甚少,嘛,算了,有点晚了,睡觉要紧,下次有空再详研,O(∩_∩)O~
参考:
1,GCC-Inline-Assembly-HOWTO
2,Inline assembly for x86 in Linux
3,http://stackoverflow.com/questions/12584243/lets-analyse-collect2-ld-returned-1-exit-status
4,http://alpha-blog.wanglianghome.org/2011/04/14/collect2/
转载请保留地址:http://lenky.info/archives/2013/01/23/2197 或 http://lenky.info/?p=2197
备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。
法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以或书面等方式告知,本站将及时删除相关内容或链接。