【转】GCC的编译流程详解

 

 

    一、首先,先对Linux下C语言开发的流程有个大体的了解。简单的说,就是:

    1、编写C程序源代码*.c
    2、预处理(Pre-Processing)
    3、编译(Compiling)
    4、汇编(Assembling)-->生成目标代码*.o
    5、链接(Linking)-->生成可执行文件
    6、调试
    其中,第一步工作用编辑器来实现,用Emacs、Vi(m)都可以。我现在习惯用Vim。在RedHat9.0中,Vim版本为6.1,系统采用alias用vi作为vim的别名,因此在shell提示符下输入vi实际上使用的编辑器vim(Vi IMproved)。你可以使用which查看一下:
[armlinux@lqm program]$ which vi
alias vi='vim'
        /usr/bin/vim
    除去第6步的工作由GDB之类的调试器来完成,中间的四步工作都是由GCC来完成的。
    二、下面大体了解一下GCC。
---------------------------------------
标准
  • ANSI C:这一标准是 ANSI(美国国家标准局)于 1989 年制定的 C 语言标准。后来被 ISO(国际标准化组织)接受为标准,因此也称为 ISO C。
    ANSI C 的目标是为各种操作系统上的 C 程序提供可移植性保证,而不仅仅限于 UNIX。 该标准不仅定义了 C 编程语言的语发和语义,而且还定义了一个标准库。这个库可以根据 头文件划分为 15 个部分,其中包括:字符类型 (<ctype.h>)、错误码 (<errno.h>)、 浮点常数 (<float.h>)、数学常数 (<math.h>)、标准定义 (<stddef.h>)、 标准 I/O (<stdio.h>)、工具函数 (<stdlib.h>)、字符串操作 (<string.h>)、 时间和日期 (<time.h>)、可变参数表 (<stdarg.h>)、信号 (<signal.h>)、 非局部跳转 (<setjmp.h>)、本地信息 (<local.h>)、程序断言 (<assert.h>) 等等。
  • POSIX:该标准最初由 IEEE 开发的标准族,部分已经被 ISO 接受为国际标准。该标准的具体内容 见 1.1.3。POSIX.1 和 POSIX.2 分别定义了 POSIX 兼容操作系统的 C 语言系统接口 以及 shell 和工具标准。这两个标准是通常提到的标准。
  • SVID:System V 的接口描述。System V 接口描述(SVID)是描述 AT&&;T Unix System V 操作 系统的文档,是对 POSIX 标准的扩展超集。
  • XPG:X/Open 可移植性指南。X/Open 可移植性指南(由 X/Open Company, Ltd.出版), 是比 POSIX 更为一般的标准。X/Open 拥有 Unix 的版权,而 XPG 则指定成为 Unix 操作系统必须满足的要求。
    ---------------------------------------
        GCC是GNU CC的简称,它是符合上述ANSI C标准的编译系统,能够编译C、C++、Object C等语言编写的程序。GCC还是一个交叉平台编译器,能够在当前CPU平台为多种不同架构的硬件平台开发软件,因此适合嵌入式领域的开发编译。
    GCC所支持的后缀名的解释
    .c C原始程序
    .C/.cc/.cxx C++原始程序
    .m Object C原始程序
    .i 已经过预处理的C原始程序
    .ii 已经过预处理的C++原始程序
    .s/.S 汇编语言原始程序
    .h 预处理文件(头文件)
    .o 目标文件
    .a/.so 编译后的库文件
        三、详解GCC编译流程
        使用vi编写源文件hello.c。
    #include <stdio.h>
    int main()
    {
            int i;
            for(i=1;i<9;i++)
                    printf("Hello World %d times!/n",i);
            return 0;
    }
        1、预处理阶段
         该阶段的作用是把预处理文件,也就是头文件编译进来。在此例中,就是要把stdio.h编译进来。可使用-E选项查看,作用是让gcc在预处理结束后停止编译过程。
    [armlinux@lqm program]$ gcc -E hello.c -o hello.i
    [armlinux@lqm program]$ cat hello.i | less
    # 1 "hello.c"
    # 1 "<built-in>"
    # 1 "<command line>"
    # 1 "hello.c"
    # 1 "/usr/include/stdio.h" 1 3
    # 28 "/usr/include/stdio.h" 3
    # 1 "/usr/include/features.h" 1 3
    # 291 "/usr/include/features.h" 3
    # 1 "/usr/include/sys/cdefs.h" 1 3
    # 292 "/usr/include/features.h" 2 3
    # 314 "/usr/include/features.h" 3
    # 1 "/usr/include/gnu/stubs.h" 1 3
    # 315 "/usr/include/features.h" 2 3
    # 29 "/usr/include/stdio.h" 2 3
    ...
    extern void funlockfile (FILE *__stream) ;
    # 679 "/usr/include/stdio.h" 3
    # 2 "hello.c" 2
    int main()
    {
            int i;
            for(i=1;i<9;i++)
                    printf("Hello World %d times!/n",i);
            return 0;
    }
        由此可见,stdio.h的内容已经插入到hello.c中,即GCC完成了预处理过程。
        2、编译阶段
        GCC的工作是首先检查代码的规范性、是否有语法错误,以确定代码实际要做的工作。检查无误后,将之翻译为汇编语言。可用-S来查看,即只编译而不进入汇编阶段。
    [armlinux@lqm program]$ gcc -S hello.i -o hello.s
    [armlinux@lqm program]$ cat hello.s

            .file   "hello.c"
            .section        .rodata
    .LC0:
            .string "Hello World %d times!/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
            movl    $1, -4(%ebp)
    .L2:
            cmpl    $8, -4(%ebp)
            jle     .L5
            jmp     .L3
    .L5:
            subl    $8, %esp
            pushl   -4(%ebp)
            pushl   $.LC0
            call    printf
            addl    $16, %esp
            leal    -4(%ebp), %eax
            incl    (%eax)
            jmp     .L2
    .L3:
            movl    $0, %eax
            leave
            ret
    .Lfe1:
            .size   main,.Lfe1-main
            .ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
        3、汇编阶段
        GCC把编译生成的.s文件转换为目标文件.o。这时使用-c选项就可以看到汇编代码已经转换成.o的目标代码了。
    [armlinux@lqm program]$ gcc -c hello.s -o hello.o
        4、链接阶段
        成功编译之后,就进入了链接阶段。这里首先要明白“库”的概念。这个程序中没有“printf”的函数实现,且在预处理阶段包含进来的“stdio.h”中只有该函数的声明,而没有定义函数的实现。如何实现“printf”?答案是:系统把这些函数的实现都做到名位libc.so.6的库文件里了,没有特别指定时,GCC会到默认的搜索路径“/usr/lib”下进行查找。也就是链接到libc.so.6库函数中去来实现函数“printf”,这就是链接的作用。
        在RedHat 9下的函数库如下:
        /lib:系统必备共享库
        /usr/lib:标准共享库和静态库
        /usr/X11R6/lib:X11R6的函数库
        /usr/local/lib:本地函数库
        头文件:
        /usr/include:系统头文件
        /usr/local/include:本地头文件
        在/etc/ld.so.conf中包含着共享库的搜索位置。我的libc.so.6在/lib下面,它是一个link,指向libc-2.3.2.so。这是因为我用的C库--glibc版本是2.3.2。
        函数库一般可分为静态库和动态库两种。静态库是指链接时把库文件的代码全部加到可执行文件中,因此生成的文件比较大,但是在运行时就不再需要库文件了。其后缀名一般为“.a”。动态库在链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”。GCC在编译时默认使用动态库。
    [armlinux@lqm program]$ gcc hello.o -o hello1
    [armlinux@lqm program]$ ls -l hello1
    -rwxrwxr-x    1 armlinux armlinux    11582 8月 28 17:42 hello1
    [armlinux@lqm program]$ file hello1
    hello1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not sd
    [armlinux@lqm program]$ gcc -static hello.o -o hello2
    [armlinux@lqm program]$ ls -l hello2
    -rwxrwxr-x    1 armlinux armlinux   423442 8月 28 17:43 hello2
    [armlinux@lqm program]$ file hello2
    hello2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, statically linked, not stripped
    [armlinux@lqm program]$ ./hello1
    Hello World 1 times!
    Hello World 2 times!
    Hello World 3 times!
    Hello World 4 times!
    Hello World 5 times!
    Hello World 6 times!
    Hello World 7 times!
    Hello World 8 times!
    [armlinux@lqm program]$ ./hello2
    Hello World 1 times!
    Hello World 2 times!
    Hello World 3 times!
    Hello World 4 times!
    Hello World 5 times!
    Hello World 6 times!
    Hello World 7 times!
    Hello World 8 times!
        hello2是静态编译,大小423442,是动态编译的36.56倍。利用file可以看出此文件statically linked,还是dynamically linked (uses shared libs)。
        至此,GCC的整个编译链接过程就完成了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值