zoukankan      html  css  js  c++  java
  • 【Linux】-GCC

    1. 拆解编译过程

    1.1 Windows 编译

      在 Windows 上开发,通常会使用 Visual Studio 等 IDE。代码写完后直接点编译按钮,就能生成可执行文件。这种方式虽然方便,但隐藏了编译过程中更为细分的步骤。

    1.2 Linux 编译

      在 Linux 上开发,通常使用 GCC 编译。我们可以更为细致地观察编译过程。hello.c 代码如下:

     1 /*
     2 * hello.c
     3 */
     4 #include <stdio.h>
     5 
     6 #define MAX 20
     7 #define MIN 10
     8 
     9 #define _DEBUG
    10 #define SetBit(x) (1<<x)
    11 
    12 int main(int argc, char *argv[])
    13 {
    14     printf("Hello world!
    ");
    15     printf("MAX = %d, MIN = %d, MAX+MIN = %d
    ", MAX, MIN, MAX+MIN);
    16 
    17 #ifdef _DEBUG
    18     printf("SetBit(5) = %d, SetBit(6) = %d
    ", SetBit(5), SetBit(6));
    19     printf("SetBit(SetBit(2)) = %d
    ", SetBit(SetBit(2)));
    20 #endif
    21 
    22     return 0;
    23 }

      编译过程由以下几个步骤组成:

    编译步骤拆分示例

    2. GCC 简介

    2.1 什么是 GCC

      GCC(GNU Compiler Collection,GNU 编译器套件)是 Linux 下主要的编译工具。GCC 可以编译 C/C++、Java 等多个语言的程序。

    2.2 使用 GCC 的原因

      功能强大且稳定,开源免费。

    3. GCC 常用选项

      GCC 命令格式通常如下:

    1 gcc [选项] 文件名

    3.1 gcc --help

      查看帮助信息,其中列出了各选项的具体用途。

     1 albert@AlbertUltra:~$ gcc --help
     2 Usage: gcc [options] file...
     3 Options:
     4   -pass-exit-codes         Exit with highest error code from a phase
     5   --help                   Display this information
     6   --target-help            Display target specific command line options
     7   --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]
     8                            Display specific types of command line options
     9   (Use '-v --help' to display command line options of sub-processes)
    10   --version                Display compiler version information
    11   -dumpspecs               Display all of the built in spec strings
    12   -dumpversion             Display the version of the compiler
    13   -dumpmachine             Display the compiler's target processor
    14   -print-search-dirs       Display the directories in the compiler's search path
    15   -print-libgcc-file-name  Display the name of the compiler's companion library
    16   -print-file-name=<lib>   Display the full path to library <lib>
    17   -print-prog-name=<prog>  Display the full path to compiler component <prog>
    18   -print-multiarch         Display the target's normalized GNU triplet, used as
    19                            a component in the library path
    20   -print-multi-directory   Display the root directory for versions of libgcc
    21   -print-multi-lib         Display the mapping between command line options and
    22                            multiple library search directories
    23   -print-multi-os-directory Display the relative path to OS libraries
    24   -print-sysroot           Display the target libraries directory
    25   -print-sysroot-headers-suffix Display the sysroot suffix used to find headers
    26   -Wa,<options>            Pass comma-separated <options> on to the assembler
    27   -Wp,<options>            Pass comma-separated <options> on to the preprocessor
    28   -Wl,<options>            Pass comma-separated <options> on to the linker
    29   -Xassembler <arg>        Pass <arg> on to the assembler
    30   -Xpreprocessor <arg>     Pass <arg> on to the preprocessor
    31   -Xlinker <arg>           Pass <arg> on to the linker
    32   -save-temps              Do not delete intermediate files
    33   -save-temps=<arg>        Do not delete intermediate files
    34   -no-canonical-prefixes   Do not canonicalize paths when building relative
    35                            prefixes to other gcc components
    36   -pipe                    Use pipes rather than intermediate files
    37   -time                    Time the execution of each subprocess
    38   -specs=<file>            Override built-in specs with the contents of <file>
    39   -std=<standard>          Assume that the input sources are for <standard>
    40   --sysroot=<directory>    Use <directory> as the root directory for headers
    41                            and libraries
    42   -B <directory>           Add <directory> to the compiler's search paths
    43   -v                       Display the programs invoked by the compiler
    44   -###                     Like -v but options quoted and commands not executed
    45   -E                       Preprocess only; do not compile, assemble or link
    46   -S                       Compile only; do not assemble or link
    47   -c                       Compile and assemble, but do not link
    48   -o <file>                Place the output into <file>
    49   -pie                     Create a position independent executable
    50   -shared                  Create a shared library
    51   -x <language>            Specify the language of the following input files
    52                            Permissible languages include: c c++ assembler none
    53                            'none' means revert to the default behavior of
    54                            guessing the language based on the file's extension
    55 
    56 Options starting with -g, -f, -m, -O, -W, or --param are automatically
    57  passed on to the various sub-processes invoked by gcc.  In order to pass
    58  other options on to these processes the -W<letter> options must be used.
    59 
    60 For bug reporting instructions, please see:
    61 <file:///usr/share/doc/gcc-4.8/README.Bugs>.
    62 albert@AlbertUltra:~$ 

      从以上 help 信息可以看到,gcc 有很多功能选项。但在实际使用中,常用的选项并不多,重点掌握这些选项即可。

    3.2 -v 选项

    3.2.1 显示版本信息

      单独输入 gcc -v,屏幕上会显示 gcc 的版本信息。我们也可以用此命令来检测 gcc 是否工作正常。

    1 albert@AlbertUltra:~$ gcc -v
    2 Using built-in specs.
    3 COLLECT_GCC=gcc
    4 COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
    5 Target: x86_64-linux-gnu
    6 Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04.4' 
      --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8
      --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8
      --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object
      --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo
      --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64
      --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar
      --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic
      --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
    7 Thread model: posix 8 gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4) 9 albert@AlbertUltra:~$

    3.2.2 显示 gcc 执行时的详细过程

      -v 选项和其他选项配合使用,将显示 gcc 命令执行的详细过程。

    3.3 -o 选项

      -o 选项在 help 信息中描述如下:-o <file>:Place the output into <file>。也就是指定生成文件的名称。-o 选项可以和其他选项搭配使用。

    3.4 -E 选项

      -E 选项在 help 信息中描述如下:Preprocess only; do not compile, assamble or link。简而言之,-E 的功能就是预编译,譬如展开头文件,替换宏定义,根据条件编译选择编译哪部分代码等。

      我们使用以下命令生成 hello.i,这是 hello.c 经过预处理的结果。

    1 gcc -E -o hello.i hello.c

      对比 hello.c 和 hello.i,可以更形象地了解预编译的效果。限于篇幅,不将具体差异贴在此处。

    3.5 -S 选项

      -S 选项在 help 信息中描述如下:compile only; do not assemble or link。这个选项用于编译阶段,将预处理生成的 .i 编译成 .s。

    1 gcc -S -o hello.s hello.i

      hello.s 中的内容如下:

     1 albert@AlbertUltra:~/coding/gcc$ cat hello.s
     2     .file    "hello.c"
     3     .section    .rodata
     4 .LC0:
     5     .string    "Hello world!"
     6     .align 8
     7 .LC1:
     8     .string    "MAX = %d, MIN = %d, MAX+MIN = %d
    "
     9     .align 8
    10 .LC2:
    11     .string    "SetBit(5) = %d, SetBit(6) = %d
    "
    12 .LC3:
    13     .string    "SetBit(SetBit(2)) = %d
    "
    14     .text
    15     .globl    main
    16     .type    main, @function
    17 main:
    18 .LFB0:
    19     .cfi_startproc
    20     pushq    %rbp
    21     .cfi_def_cfa_offset 16
    22     .cfi_offset 6, -16
    23     movq    %rsp, %rbp
    24     .cfi_def_cfa_register 6
    25     subq    $16, %rsp
    26     movl    %edi, -4(%rbp)
    27     movq    %rsi, -16(%rbp)
    28     movl    $.LC0, %edi
    29     call    puts
    30     movl    $30, %ecx
    31     movl    $10, %edx
    32     movl    $20, %esi
    33     movl    $.LC1, %edi
    34     movl    $0, %eax
    35     call    printf
    36     movl    $64, %edx
    37     movl    $32, %esi
    38     movl    $.LC2, %edi
    39     movl    $0, %eax
    40     call    printf
    41     movl    $16, %esi
    42     movl    $.LC3, %edi
    43     movl    $0, %eax
    44     call    printf
    45     movl    $0, %eax
    46     leave
    47     .cfi_def_cfa 7, 8
    48     ret
    49     .cfi_endproc
    50 .LFE0:
    51     .size    main, .-main
    52     .ident    "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4"
    53     .section    .note.GNU-stack,"",@progbits
    54 albert@AlbertUltra:~/coding/gcc$

      可以看到,经过编译处理,已经将源文件中的 C 语言代码转换成汇编代码。

    3.6 -c 选项

      -c 选项在 help 信息中描述如下:Compile and assemble, but do not link。也就是说,这个步骤是编译和汇编的,但不做链接。这一步生成 .o 文件,即 object file,也称 OBJ 文件。

    1 gcc -c -o hello.o hello.s

      通过 file 命令查看 hello.o,可以看到,它是一个 ELF(Exacutable and Linkable File Format,可执行与可链接文件格式)文件。也就是说,这一步生成的文件其实已经是机器码。但我们不能直接运行 .o 文件,因为 .o 不是可执行文件,而是目标文件。还要通过链接器把多个目标文件以及函数库链接起来,才能生成可执行文件。

    3.7 链接

      链接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,最终生成可以在特定平台运行的可执行程序。

      通过以下命令链接,查看具体信息:

     1 albert@AlbertUltra:~/coding/gcc$ gcc -v -o hello hello.o
     2 Using built-in specs.
     3 COLLECT_GCC=gcc
     4 COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
     5 Target: x86_64-linux-gnu
     6 Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04.4' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs 
      --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id
      --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib
      --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object
      --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo
      --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64
      --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar
      --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic
      --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
    7 Thread model: posix 8 gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4) 9 COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
      :/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/ 10 LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/:
      /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/:/lib/x86_64-linux-gnu/
      :/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/ 11 COLLECT_GCC_OPTIONS='-v' '-o' 'hello' '-mtune=generic' '-march=x86-64' 12 /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 --sysroot=/ --build-id --eh-frame-hdr
      -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2
      -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o
      /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crti.o
      /usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.8
      -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib
      -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib
      -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../.. hello.o -lgcc --as-needed -lgcc_s --no-as-needed
      -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o
      /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o 13 albert@AlbertUltra:~/coding/gcc$

      crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o 是 gcc 加入的系统标准启动文件,对于一般应用程序,这些启动文件是必需的。

      -lc:链接 libc 库文件,其中 libc 库文件中就实现了 printf 等函数。

    3.8 -nostdlib 选项

      -nostdlib 不链接系统标准启动文件和标准库文件,只把指定的文件传递给链接器。

      -nostdlib 选项常用于裸机、bootloader、Linux 内核等程序,因为它们不需要启动文件、标准库文件。对于普通应用程序,如果添加 -nostdlib 选项,会提示没有链接系统标准启动文件和标准库文件,导致链接失败。

    3.9 文件类型对于编译的影响

      在上述小节中,我们专注于解释各个选项的作用。现在我们要返回来,看看文件类型对于编译的影响。

      在编译过程中,除非使用了 -E、-S、-c 选项或者编译出错,否则最后的步骤都是链接。输入文件的后缀名和选项共同决定 gcc 执行哪些操作。

      这样说可能还不是很直观。我们举个例子来看看。

    1 # 示例一
    2 gcc -o hello hello.i
    3 
    4 # 示例二
    5 gcc -o hello hello.s
    6 
    7 # 示例三
    8 gcc -o hello hello.o

      以上三种方式,都会生成可执行文件 hello,并且能够成功运行,但给出的文件类型不同。

      示例一中,给出的是 .i 文件,因此 gcc 对其进行编译、汇编、链接,生成可执行文件。

      示例二中,给出的是 .s 文件,因此 gcc 对其进行汇编、链接,生成可执行文件。

      示例三中,给出的是 .o 文件,因此 gcc 对其进行链接,生成可执行文件。

      简而言之,对于不同文件类型,能进行的操作是不同的。这也会对编译产生影响。

    3.10 库文件

      关于库文件的使用,前面已经写过一篇《【Linux】-库文件》,此处不再赘述。

  • 相关阅读:
    网站测试自动化系统—在测试代码中硬编码测试数据
    在WPF里面显示DIB图片格式的图片
    网站测试自动化系统—系统应该有的功能
    Windbg 教程调试非托管程序的基本命令下
    Windbg教程调试非托管程序的基本命令上
    网站测试自动化系统—基于Selenium和VSTT
    WPF中一个通用的BoolToXXX转换类
    使用MPLex实现语法高亮显示的功能代码解释
    网站测试自动化系统—数据驱动测试
    如何控制float类型小数点后的位数
  • 原文地址:https://www.cnblogs.com/murongmochen/p/14321820.html
Copyright © 2011-2022 走看看