【gcc编译器经历的4个步骤】

来源:互联网 发布:windows 媒体播放器 编辑:程序博客网 时间:2024/05/22 16:57

一个c语言程序从源文件到生成可执行文件,编译器需要共经历4个步骤:
1) 预处理:把c文件中预处理命令扫描处理完毕,即对源代码文件中的文件包含(#include)、预编译语句(如宏定义#define等)进行分析,此时生成的文件仍然是可读的。
2) 编译:把预处理后的结果编译成汇编或者目标模块,即生成汇编语言文件,此时生成的文件仍然是可读的汇编文件。
3) 汇编:把编译出来的结果汇编成具体CPU上的目标代码模块,也即此时转换成具体的机器语言代码,此时生成的文件是不可读的非文本文件。
4) 连接:把多个目标代码模块连接生成一个大的目标模块,即将多个上面产生的机器代码文件(与其它的机器代码文件和库文件)汇集成一个可执行的二进制代码文件。

    gcc作为c语言在linux下很著名的编译软件,分别有如下option来支持4个步骤:

  名称  gcc选项  英文名称         gcc调用的程序  示例
预处理  -E       Pre-Processing  cpp            gcc -E test.c -o test.i
  编译  -S       Compiling       ccl            gcc -S test.i -o test.s 
  汇编  -c       Assembling      as             gcc -c test.s -o test.o
  连接  无       Linking         ld             gcc test.o -o test

说明:

  gcc在编译c语言文件时,首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(#include)、预编译语句(如宏定义#define等)进行分析;其次调用ccl进行编译工作,将文件编译成汇编语言文件,此时文件依旧是可读的;之后调用as进行汇编工作,将具体的汇编语言文件编译成cpu可执行的目标代码,此时文件不可读了;当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,链接。在链接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的库中链接到合适的地方。下面对上面4个过程做下分别的说明:

1、在预处理阶段,如果不用“-o”指定文件名,那么会默认将预处理结果输出到标准终端设备。

[root@dbbak tmp]# cat a.c
int main()
{
        printf("shengtong test!\n");
}

[root@dbbak tmp]# gcc -E a.c
# 1 "a.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "a.c"
int main()
{
        printf("shengtong test!\n");
}
[root@dbbak tmp]# gcc -E a.c -o a.i
[root@dbbak tmp]# cat a.i
# 1 "a.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "a.c"
int main()
{
        printf("shengtong test!\n");
}

2、在编译阶段,如果不用“-o”指定文件名,那么默认会生成一个“*.s”的汇编语言文件。

[root@dbbak tmp]# gcc -S a.i
[root@dbbak tmp]# ls
a.c  a.i  
a.s
[root@dbbak tmp]# 
gcc -S a.i -o a1.s
[root@dbbak tmp]# ls
a.c  a.i  a.s  a1.s

[root@dbbak tmp]# 
[root@dbbak tmp]# cat a.s
        .file   "a.c"
        .section        .rodata
.LC0:
        .string "shengtong test!\n"
        .text
.globl main
        .type   main,@function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        subl    $12, %esp
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        leave
        ret
.Lfe1:
        .size   main,.Lfe1-main
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.2.3 20030502 (Red Hat Linux 3.2.3-42)"
[root@dbbak tmp]# cat a1.s
        .file   "a.c"
        .section        .rodata
.LC0:
        .string "shengtong test!\n"
        .text
.globl main
        .type   main,@function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        subl    $12, %esp
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        leave
        ret
.Lfe1:
        .size   main,.Lfe1-main
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.2.3 20030502 (Red Hat Linux 3.2.3-42)"

3、在汇编阶段,如果不用“-o”指定文件名,那么默认会生成一个“*.o”的机器语言代码文件。

[root@dbbak tmp]# gcc -c a.s
[root@dbbak tmp]# ls
a.c  a.i  a.o  a.s  a1.s
[root@dbbak tmp]# 
gcc -c a1.s -o a1.o
[root@dbbak tmp]# ls
a.c  a.i  a.o  a.s  a1.o  a1.s
[root@dbbak tmp]# 
[root@dbbak tmp]# file a.o
a.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
[root@dbbak tmp]# file a1.o
a1.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

4、在连接阶段,不指定前面3个选项就是默认进入这个阶段,如果不用“-o”指定文件名,那么默认会生产一个“a.out”的可执行文件。

[root@dbbak tmp]# gcc a.o
[root@dbbak tmp]# ls
a.c  a.i  a.o  a.out  a.s  a1.o  a1.s  -- 可执行文件
[root@dbbak tmp]# gcc a1.o -o a1.out
[root@dbbak tmp]# ls
a.c  a.i  a.o  a.out  a.s  a1.o  a1.out  a1.s
[root@dbbak tmp]# 
[root@dbbak tmp]# ./a.out 
shengtong test!
[root@dbbak tmp]# ./a1.out 
shengtong test!

5、编译c文件的时候,可以跳过前面阶段而直接进入后面阶段,如可以不分开执行预处理命令gcc -E test.c,而直接进入编译环节gcc -S test.c -o test.s,gcc会在执行“编译”环节(后面阶段)的时候发现没有“预编译”环节(前面阶段),它会自动补上。

 

    gcc能够编译的语言文件有很多(常见的如c语言和c++语言等),它默认情况下依据文件的后缀名执行相应的编译,下面列出c和c++的:
.c 为后缀的文件,C语言源代码文件;
.i 为后缀的文件,是已经预处理过的C源代码文件;
.s 为后缀的文件,是汇编语言源代码文件;
.o 为后缀的文件,是编译后的目标文件;
.h 为后缀的文件,是程序所包含的头文件;
.a 为后缀的文件,是由目标文件构成的库文件;

.C .cc .cp .cpp .c++ .cxx 为后缀的文件,是C++源代码文件;
.ii 为后缀的文件,是已经预处理过的C++源代码文件;
.m 为后缀的文件,是Objective-C源代码文件;
.mi 是已经预处理过的Objective-C源代码文件;
.S 为后缀的文件,是经过预编译的汇编语言源代码文件。

    当然,你可以乱写自己的文件名后缀,如将c文件以.cpp结尾或者以.eng结尾,那么gcc就无法根据后缀名来做正确的编译了,此时怎么办?gcc提供了“-x”选项来指定文件类型,“-x”选项内容有(只列出c和c++的):

c  c-header  cpp-output
c++  c++-cpp-output
objective-c  objc-cpp-output
assembler  assembler-with-cpp

    当使用了“-x”选项后,那么其后面所有的文件都默认是其指定的文件类型,直到用“-x none”来指定结束。

[root@dbbak tmp]# gcc -x c -E a.sql -o a.i
[root@dbbak tmp]# cat a.i
# 1 "a.sql"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "a.sql"
int main()
{
        printf("shengtong test!\n");
}
[root@dbbak tmp]# mv c.sql c.c
[root@dbbak tmp]# 
[root@dbbak tmp]# gcc -x c -S a.sql b.sql -x none  -S c.c
[root@dbbak tmp]# ls
a.i  a.s  a.sql  b.s  b.sql  c.c  c.s

  

    下面再介绍下gcc的其他常用选项:

    -pass-exit-codes
    Normally the gcc program will exit with the code of 1 if any phase of the compiler returns a non-success return code.  If you specify -pass-exit-codes, the gcc program will instead return with numeri-cally highest error produced by any phase that returned an error indication.
通常如果编译器遇到错误,返回值1;如果你想返回具体的错误代码,请用它。

    

    -o file
    Place output in file file.  This applies regardless to whatever sort of output is being produced, whether it be an executable file,an object file, an assembler file or preprocessed C code.

    Since only one output file can be specified, it does not make sense to use -o when compiling more than one input file, unless you are producing an executable file as output.

    If -o is not specified, the default is to put an executable file in a.out, the object file for source.suffix in source.o, its assembler file in source.s, and all preprocessed C source on standard output.  -- 这段话我在上面的1/2/3/4/5中已经介绍了

    

     -v 

    Print (on standard error output) the commands executed to run the stages of compilation.  Also print the version number of the com-piler driver program and of the preprocessor and the compiler proper.

    这个选项的作用是把gcc编译c文件的过程给打印出来,如下面这个例子:

[root@dbbak tmp]# gcc -v a.c
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --host=i386-redhat-linux
Thread model: posix
gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-42)
 /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/cc1 -lang-c -v -D__GNUC__=3 -D__GNUC_MINOR__=2 -D__GNUC_PATCHLEVEL__=3 -D__GXX_ABI_VERSION=102 -D__ELF__ -Dunix -D__gnu_linux__ -Dlinux -D__ELF__ -D__unix__ -D__gnu_linux__ -D__linux__ -D__unix -D__linux -Asystem=posix -D__NO_INLINE__ -D__STDC_HOSTED__=1 -Acpu=i386 -Amachine=i386 -Di386 -D__i386 -D__i386__ -D__tune_i386__ a.c -quiet -dumpbase a.c -version -o /tmp/cclLW8Ji.s
GNU CPP version 3.2.3 20030502 (Red Hat Linux 3.2.3-42) (cpplib) (i386 Linux/ELF)
GNU C version 3.2.3 20030502 (Red Hat Linux 3.2.3-42) (i386-redhat-linux)
        compiled by GNU C version 3.2.3 20030502 (Red Hat Linux 3.2.3-42).
ignoring nonexistent directory "/usr/i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/include
 /usr/include
End of search list.
 as -V -Qy -o /tmp/ccRCof2i.o 
/tmp/cclLW8Ji.s
GNU assembler version 2.14.90.0.4 (i386-redhat-linux) using BFD version 2.14.90.0.4 20030523
 /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/collect2 --eh-frame-hdr -m elf_i386-dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crt1.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crti.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtbegin.o -L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3 -L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../.. /tmp/ccRCof2i.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtend.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crtn.o

    上面的调用堆栈,充分反应了gcc编译过程,首先调用ccl执行预处理和编译,并且在/tmp目录下生成一个临时文件/tmp/cclLW8Ji.s文件;接着调用as执行汇编功能,也生成了临时文件/tmp/ccRCof2i.o;最后执行连接工作。

    如果你只想看gcc的调用堆栈过程而不真正执行编译工作,怎么办?用“-###”选项:

    -###
    Like -v except the commands are not executed and all command argu-ments are quoted.  This is useful for shell scripts to capture the driver-generated command lines.

    从上面的过程中,我们可以看到,在各个步骤中gcc生成了中间临时文件,而如果你不想生成这些临时文件,而取而代之用管道,该怎么办?用“-pipe”选项:

    -pipe
    Use pipes rather than temporary files for communication between the various stages of compilation.  This fails to work on some systems where the assembler is unable to read from a pipe; but the GNU assembler has no trouble.

    举个例子如下,请看红色部分的管道:

[root@dbbak tmp]# gcc -### -pipe a.c
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --host=i386-redhat-linux
Thread model: posix
gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-42)
 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/cc1" "-lang-c" "-D__GNUC__=3" "-D__GNUC_MINOR__=2" "-D__GNUC_PATCHLEVEL__=3" "-D__GXX_ABI_VERSION=102" "-D__ELF__" "-Dunix" "-D__gnu_linux__" "-Dlinux" "-D__ELF__" "-D__unix__" "-D__gnu_linux__" "-D__linux__" "-D__unix" "-D__linux" "-Asystem=posix" "-D__NO_INLINE__" "-D__STDC_HOSTED__=1" "-Acpu=i386" "-Amachine=i386" "-Di386" "-D__i386" "-D__i386__" "-D__tune_i386__" "a.c" "-quiet" "-dumpbase" "a.c" "-o" "-" |
 "as" "-Qy" "-o" "/tmp/cc8lJo7Z.o" "-"
 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/collect2" "--eh-frame-hdr" "-m" "elf_i386" "-dynamic-linker" "/lib/ld-linux.so.2" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crt1.o" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crti.o" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtbegin.o" "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3" "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../.." "/tmp/cc8lJo7Z.o" "-lgcc" "-lgcc_eh" "-lc" "-lgcc" "-lgcc_eh" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtend.o" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crtn.o"

 

    --help
    Print (on the standard output) a description of the command line options understood by gcc.  If the -v option is also specified then --help will also be passed on to the various processes invoked by gcc, so that they can display the command line options they accept. If the -W option is also specified then command line options which have no documentation associated with them will also be displayed.

    --target-help
    Print (on the standard output) a description of target specific command line options for each tool.

    --version
    Display the version number and copyrights of the invoked GCC.

原创粉丝点击