gcc的编译过程

大家都知道编译的四个步骤:预处理、编译、汇编链接。但是这些步骤都是由谁完成的呢,今天做了几个实验验证一下。

(本人对编译和链接的原理不是很精通,以下内容是摸索的,难免出错。不要被我误导了)

 

首先编写一个简单的C文件,比如"hello, world!",然后使用-v选项来编译它,这样它的编译步骤就可以显示出来了。

[zlg@localhost gdbtest]$ gcc -v   //显示gcc的版本信息
Using built-in specs.
Target: i386-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-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --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-cpu=generic --build=i386-redhat-linux
Thread model: posix
gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC)

[zlg@localhost gdbtest]$ gcc -v main.c   //使用-v选项编译main.c
Using built-in specs.
Target: i386-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-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --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-cpu=generic --build=i386-redhat-linux
Thread model: posix
gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC)

COLLECT_GCC_OPTIONS='-v' '-mtune=generic'
 /usr/libexec/gcc/i386-redhat-linux/4.3.2/cc1 -quiet -v main.c -quiet -dumpbase main.c -mtune=generic -auxbase main -version -o /tmp/ccCwWqS3.s  /*生成临时的汇编文件*/
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/../../../../i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:        /*头文件的搜索目录*/
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/4.3.2/include
 /usr/include
End of search list.
GNU C (GCC) version 4.3.2 20081105 (Red Hat 4.3.2-7) (i386-redhat-linux)
    compiled by GNU C version 4.3.2 20081105 (Red Hat 4.3.2-7), GMP version 4.2.2, MPFR version 2.3.2.
GGC heuristics: --param ggc-min-expand=59 --param ggc-min-heapsize=55617
Compiler executable checksum: 3bee52601079f736b7b63b762646f4ba
COLLECT_GCC_OPTIONS='-v' '-mtune=generic'
 as -V -Qy -o /tmp/cca2z171.o /tmp/ccCwWqS3.s  /*调用汇编器生成目标代码*/
GNU assembler version 2.18.50.0.9 (i386-redhat-linux) using BFD version version 2.18.50.0.9-7.fc10 20080822  /*汇编器版本信息*/
COMPILER_PATH=/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/:/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/4.3.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic'
/*collect2完成最后的链接工作*/
 /usr/libexec/gcc/i386-redhat-linux/4.3.2/collect2 --eh-frame-hdr --build-id -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i386-redhat-linux/4.3.2/../../../crt1.o /usr/lib/gcc/i386-redhat-linux/4.3.2/../../../crti.o /usr/lib/gcc/i386-redhat-linux/4.3.2/crtbegin.o -L/usr/lib/gcc/i386-redhat-linux/4.3.2 -L/usr/lib/gcc/i386-redhat-linux/4.3.2 -L/usr/lib/gcc/i386-redhat-linux/4.3.2/../../.. /tmp/cca2z171.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i386-redhat-linux/4.3.2/crtend.o /usr/lib/gcc/i386-redhat-linux/4.3.2/../../../crtn.o

总结一下编译过程:
启动/usr/bin/gcc
/usr/libexec/gcc/i386-redhat-linux/4.3.2/cc1 进行预处理和编译,生成的临时汇编文件放在/tmp
/usr/bin/as 将上一步的汇编文件生成临时目标文件放在/tmp
 /usr/libexec/gcc/i386-redhat-linux/4.3.2/collect2   调用连接器/usr/bin/ld生成可执行文件

注意,没有一个单独的预处理工具,预处理和编译的工作由ccl完成。ccl生成的临时汇编文件在/tmp下:/tmp/ccCwWqS3.s 
as是汇编器,负责生成目标文件。目标文件依然作为临时文件存放在/tmp下面: /tmp/cca2z171.o
最后调用collect2负责链接的工作。collect2针对C++程序进行特殊处理。

collect2的信息参考:
gnu官方collect2的文档:http://gcc.gnu.org/onlinedocs/gccint/Collect2.html   
别人的博客:http://hellogcc.blogbus.com/logs/67096640.html    ///  http://alpha-blog.wanglianghome.org/2011/04/14/collect2/

对于gcc/ccl/collect2等关系的描述参考:《gcc的组件和软件工具》http://hi.baidu.com/xuelicheng/blog/item/cd5650505ecaea658535241a.html
gcc/as/ld的相关知识参考:http://www.cppblog.com/jinglexy/archive/2007/04/19/22298.html

 

知识点补充:

[zlg@localhost gdbtest]$ ls /usr/libexec/gcc/i386-redhat-linux/4.3.2/
cc1  cc1plus  collect2  f951
[zlg@localhost gdbtest]$

 

 

 

实验1:验证collect2会调用链接器ld.
[zlg@localhost gdbtest]$ /usr/libexec/gcc/i386-redhat-linux/4.3.2/collect2 -v
collect2 version 4.3.2 20081105 (Red Hat 4.3.2-7) (i386 Linux/ELF)
/usr/bin/ld -v
GNU ld version 2.18.50.0.9-7.fc10 20080822
[zlg@localhost gdbtest]$

 

实验2:CPP的实现依赖于cc1

http://gcc.gnu.org/projects/cpplib.html

有这么一段话:

Work recently completed

  1. Stand-alone CPP is dead. The compiler front end now handles preprocessed output if necessary.
  2. As many built-in macros as possible have been moved to the front ends, and out of SPECS and cpplib itself (some targets still in progress).
  3. CPP arithmetic is now done to the correct target precision, based upon the selected language standard.
  4. The traditional preprocessor has been integrated into cpplib. At present it is an output-only preprocessor, but it should be fairly simple to modify cpplib so that traditional preprocessing and then tokenization are performed in one invocation.

这段话的意思是CPP(也就是C语言预处理器)不再独立存在,已经被合并进了cpplib。现在由编译器前端工具负责调用。

在Linux系统(fedora10)上可用的CPP有两个,/lib/cpp和/usr/bin/cpp,其中前者是后者的符号链接。将/usr/bin/cpp改为别的名字,GCC照常编译,这说明/usr/bin/cpp并不会被GCC调用 。但是经过验证,“/usr/bin/cpp main.c”的确可以对C文件做预处理。

接下来再做实验:

[root@localhost gdbtest]# /usr/bin/cpp -v
Using built-in specs.
Target: i386-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-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --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-cpu=generic --build=i386-redhat-linux
Thread model: posix
gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC)
COLLECT_GCC_OPTIONS='-E' '-v' '-mtune=generic'
 /usr/libexec/gcc/i386-redhat-linux/4.3.2/ cc1 -E -quiet -v - -mtune=generic
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/../../../../i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/4.3.2/include
 /usr/include
End of search list.

可以看到,向cpp传递-v选项,竟然会启动cc1,这说明cpp调用了cc1。最重要的是,向cpp传入-v选项,cpp给cc1的参数中竟然有-E选项,这说明cc1才是预处理的主体,cpp的默认动作就是将-E参数传给cc1,它自己啥也做不了。

以前cpp是一个独立的预处理器,现在cpp仅仅是一个依赖于cc1的封装接口 。cc1才是做预处理的地方。

 

 

 

 

 

 

实验3:验证预处理和编译都是由cc1完成的。并且预处理不生成临时文件
[zlg@localhost gdbtest]$ gcc -v -E main.c > main.i   /*仅仅进行预处理*/
Using built-in specs.
Target: i386-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-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --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-cpu=generic --build=i386-redhat-linux
Thread model: posix
gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC)
COLLECT_GCC_OPTIONS='-v' '-E' '-mtune=generic'
 /usr/libexec/gcc/i386-redhat-linux/4.3.2/cc1 -E -quiet -v main.c -mtune=generic
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/../../../../i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/4.3.2/include
 /usr/include
End of search list.
COMPILER_PATH=/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/:/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/4.3.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-E' '-mtune=generic'
[zlg@localhost gdbtest]$ gcc main.c -S -v   /*直接生成汇编文件*/
Using built-in specs.
Target: i386-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-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --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-cpu=generic --build=i386-redhat-linux
Thread model: posix
gcc version 4.3.2 20081105 (Red Hat 4.3.2-7) (GCC)
COLLECT_GCC_OPTIONS='-S' '-v' '-mtune=generic'
 /usr/libexec/gcc/i386-redhat-linux/4.3.2/cc1 -quiet -v main.c -quiet -dumpbase main.c -mtune=generic -auxbase main -version -o main.s
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/i386-redhat-linux/4.3.2/../../../../i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/4.3.2/include
 /usr/include
End of search list.
GNU C (GCC) version 4.3.2 20081105 (Red Hat 4.3.2-7) (i386-redhat-linux)
    compiled by GNU C version 4.3.2 20081105 (Red Hat 4.3.2-7), GMP version 4.2.2, MPFR version 2.3.2.
GGC heuristics: --param ggc-min-expand=59 --param ggc-min-heapsize=55617
Compiler executable checksum: 3bee52601079f736b7b63b762646f4ba
COMPILER_PATH=/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/:/usr/libexec/gcc/i386-redhat-linux/4.3.2/:/usr/libexec/gcc/i386-redhat-linux/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/4.3.2/:/usr/lib/gcc/i386-redhat-linux/4.3.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-S' '-v' '-mtune=generic'

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值