gcc编译流程

gcc编译器在编译一个C语言程序时需要经过以下4步:

  • 将C语言源程序预处理,生成.i文件;
  • 预处理后的.i文件编译成为汇编语言,生成.s文件;
  • 将汇编语言文件经过汇编,生成目标文件.o文件;
  • 将各个模块的.o文件链接起来生成一个可执行程序文件。

gcc的帮助信息

我们可以使用gcc --help查看gcc的帮助信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
gcc --help
Usage: gcc-4.9 [options] file...
Options:
    -pass-exit-codes         Exit with highest error code from a phase
    --help                   Display this information
    --target-help            Display target specific command line options
    --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]
                             Display specific types of command line options
    -Wa,<options>            Pass comma-separated <options> on to the assembler
    -Wp,<options>            Pass comma-separated <options> on to the preprocessor
    -Wl,<options>            Pass comma-separated <options> on to the linker
    -Xassembler <arg>        Pass <arg> on to the assembler
    -Xpreprocessor <arg>     Pass <arg> on to the preprocessor
    -pipe                    Use pipes rather than intermediate files
    -time                    Time the execution of each subprocess
    -specs=<file>            Override built-in specs with the contents of <file>
    -std=<standard>          Assume that the input sources are for <standard>
    --sysroot=<directory>    Use <directory> as the root directory for headers
                             and libraries
    ......                   ......

    -B <directory>           Add <directory> to the compiler's search paths
    -v                       Display the programs invoked by the compiler
    -###                     Like -v but options quoted and commands not executed
    -E                       Preprocess only; do not compile, assemble or link
    -S                       Compile only; do not assemble or link
    -c                       Compile and assemble, but do not link
    -o <file>                Place the output into <file>
    -pie                     Create a position independent executable
    -shared                  Create a shared library

Options starting with -g, -f, -m, -O, -W, or --param are automatically
 passed on to the various sub-processes invoked by gcc-4.9.  In order to pass
 other options on to these processes the -W<letter> options must be used.

重要的gcc编译选项

gcc -v选项

我们可以使用gcc -v参数查看编译过程中的的一些详细信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ gcc -v -DDEBUG gdb_finish.c -std=c99
Using built-in specs.
COLLECT_GCC=/usr/local/Cellar/gcc49/4.9.3/bin/gcc-4.9
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc@4.9/4.9.3/bin/../libexec/gcc/x86_64-apple-darwin15.2.0/4.9.3/lto-wrapper
Target: x86_64-apple-darwin15.2.0
Configured with: ../configure --build=x86_64-apple-darwin15.2.0
                 --prefix=/usr/local/Cellar/gcc49/4.9.3
                 --libdir=/usr/local/Cellar/gcc49/4.9.3/lib/gcc/4.9
                 --enable-languages=c,c++,objc,obj-c++
                 --program-suffix=-4.9
                  ......
                 --enable-stage1-checking
                 --enable-checking=release
                 --with-build-config=bootstrap-debug
                 --disable-werror
                 --with-pkgversion='Homebrew gcc49 4.9.3'
Thread model: posix
gcc version 4.9.3 (Homebrew gcc49 4.9.3)
    COLLECT_GCC_OPTIONS='-mmacosx-version-min=10.12.7' '-v' '-D' 'DEBUG' '-std=c99' '-mtune=core2'
    GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072

#include "..." search starts here:
#include <...> search starts here:
    /usr/local/Cellar/gcc@4.9/4.9.3/bin/../lib/gcc/4.9/gcc/x86_64-apple-darwin15.2.0/4.9.3/include
    /usr/local/Cellar/gcc@4.9/4.9.3/bin/../lib/gcc/4.9/gcc/x86_64-apple-darwin15.2.0/4.9.3/include-fixed
    /usr/local/include
    /usr/local/Cellar/gcc@4.9/4.9.3/bin/../lib/gcc/4.9/gcc/../../../../include
    /usr/include
    /System/Library/Frameworks
    /Library/Frameworks
    
    as -arch x86_64 -force_cpusubtype_ALL -o /var/folders/yz/kwcrlzrn6yzdjy4_h_zfpqqc0000gn/T//ccHrUOR3.o /var/folders/yz/kwcrlzrn6yzdjy4_h_zfpqqc0000gn/T//ccjNRfjo.s

通过COLLECT_GCC_OPTIONS='-mmacosx-version-min=10.12.7' '-v' '-D' 'DEBUG' '-std=c99' '-mtune=core2看出gcc会切分出编译参数;通过#include可以看出gcc的搜索头文件的路径。

gcc -Wall选项

使用man gcc可以查看-Wall选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-Wall
    This enables all the warnings about constructions that some users consider
    questionable, and that are easy to avoid (or modify to prevent the warning), even in
    conjunction with macros.  This also enables some language-specific warnings
    described in C++ Dialect Options and Objective-C and Objective-C++ Dialect Options.

    -Wall turns on the following warning flags:
        -Waddress
        -Warray-bounds=1 (only with -O2)
        -Wformat
        -Wimplicit-int
        -Wimplicit-function-declaration
        -Wunknown-pragmas
        -Wunused-function
        ......
        -Wunused-label
        -Wunused-value
        -Wunused-variable

    Note that some warning flags are not implied by -Wall.  Some of them warn about
    constructions that users generally do not consider questionable, but which
    occasionally you might wish to check for; others warn about constructions that are
    necessary or hard to avoid in some cases, and there is no simple way to modify the
    code to suppress the warning. Some of them are enabled by -Wextra but many of them
    must be enabled individually.

使用gcc --help=warning可以进一步查看-Wall选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ gcc --help=warning
The following options control compiler warning messages:
    -Wabi                       Warn about things that will change when compiling
                                with an ABI-compliant compiler
    -Wabi-tag                   Warn if a subobject has an abi_tag attribute that
                                the complete object type does not have
    -Waddress                   Warn about suspicious uses of memory addresses
    -Waggregate-return          Warn about returning structures, unions or arrays
    -Waliasing                  Warn about possible aliasing of dummy arguments
    -Walign-commons             Warn about alignment of COMMON blocks
    -Wall                       Enable most warning messages
    ......
    -Warray-bounds              Warn if an array is accessed out of bounds
    -Wunused-value              Warn when an expression value is unused
    -Wunused-variable           Warn when a variable is unused

需要注意的是,各警告选项既然能使之生效,当然也能使之关闭。比如假设我们想要使用-Wall来启用个选项,同时又要关闭unused警告,可以通过新增类似Wno-*的选项来达到目的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
$ cat gcc_warning.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    char ch = 'a';
    int num = 2028;

    printf("num = %d\n", num);
    return 0;
}

$ gcc -g -Wall gcc_warning.c -o gcc_warning
gcc_warning.c: In function 'main':
gcc_warning.c:5:10: warning: unused variable 'ch' [-Wunused-variable]
     char ch = 'a';
          ^

$ gcc -g -Wall -Wno-unused gcc_warning.c -o gcc_warning

gcc -Werror选项

1
    -Werror                      Make all warnings into errors.

例如给上述的程序新增-Werror编译选项:

1
2
3
4
5
6
$ gcc -Wall -Werror gcc_warning.c
gcc_warning.c: In function 'main':
gcc_warning.c:5:10: error: unused variable 'ch' [-Werror=unused-variable]
     char ch = 'a';
          ^
cc1: all warnings being treated as errors

gcc -E选项

C语言代码在交给编译器之前,会先由预处理器进行一些文本替换方面的操作,例如宏展开、文件包含、删除部分代码等。在正常的情况下,gcc不会保留预处理阶段的输出文件,也即.i文件。可以利用-E选项保留预处理器的输出文件,以用于诊断代码。-E选项指示gcc在预处理完毕之后即可停止。默认情况下,预处理器的输出会被导入到标准输出流。

1
$ gcc -E gcc_warning.c -o gcc_warning.i

因为头文件可能相当大,如果源文件包括了多个头文件,那么它的预处理器输出可能会庞杂难读。使用-C选项会很有帮助,这个选项可以阻止预处理器删除源文件和头文件中的注释:

1
gcc -E -C gcc_warning.c -o gcc_warning.i

gcc -S选项

通常情况下,gcc把汇编语言输出存储到临时文件中,并且在汇编器执行完后立刻删除它们。但是可以使用-S选项,让编译程序在生成汇编语言输出之后立刻停止。如果没有指定输出文件名,那么采用-S选项的编译过程会为每个被编译的输入文件生成以.s作为后缀的汇编语言文件。如下例所示:

1
$ gcc -S gcc_warning.c

如果想把C语言变量的名称作为汇编语言语句中的注释,可以加上-fverbose-asm选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
-fverbose-asm
	Put extra commentary information in the generated assembly code to make it more
	readable.  This option is generally only of use to those who actually need to read
	the generated assembly code (perhaps while debugging the compiler itself).

$ gcc -S -fverbose-asm gcc_warning.c
$ cat gcc_warning.s
# GNU C (Homebrew gcc49 4.9.3) version 4.9.3 (x86_64-apple-darwin15.2.0)
#	compiled by GNU C version 4.9.3, GMP version 4.3.2, MPFR version 2.4.2, MPC version 0.8.1
# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
# options passed:
# -iprefix /usr/local/Cellar/gcc@4.9/4.9.3/bin/../lib/gcc/4.9/gcc/x86_64-apple-darwin15.2.0/4.9.3/
# -D__DYNAMIC__ gcc_warning.c -fPIC -mmacosx-version-min=10.12.7
# -mtune=core2 -fverbose-asm
# options enabled:  -Wnonportable-cfstrings -fPIC
# -faggressive-loop-optimizations -fasynchronous-unwind-tables
# -fauto-inc-dec -fcommon -fdelete-null-pointer-checks -fearly-inlining
# -feliminate-unused-debug-types -ffunction-cse -fgcse-lm -fgnu-unique
# -fident -finline-atomics -fira-hoist-pressure -fira-share-save-slots
# -fira-share-spill-slots -fivopts -fkeep-static-consts
# -fleading-underscore -flifetime-dse -fmath-errno -fmerge-debug-strings
# -fnext-runtime -fobjc-abi-version= -fpeephole -fprefetch-loop-arrays
# -freg-struct-return -fsched-critical-path-heuristic
# -fsched-dep-count-heuristic -fsched-group-heuristic -fsched-interblock
# -fsched-last-insn-heuristic -fsched-rank-heuristic -fsched-spec
# -fsched-spec-insn-heuristic -fsched-stalled-insns-dep -fshow-column
# -fsigned-zeros -fsplit-ivs-in-unroller -fstrict-volatile-bitfields
# -fsync-libcalls -ftrapping-math -ftree-coalesce-vars -ftree-cselim
# -ftree-forwprop -ftree-loop-if-convert -ftree-loop-im -ftree-loop-ivcanon
# -ftree-loop-optimize -ftree-parallelize-loops= -ftree-phiprop
# -ftree-reassoc -ftree-scev-cprop -funit-at-a-time -funwind-tables
# -fverbose-asm -fzero-initialized-in-bss -gstrict-dwarf
# -m128bit-long-double -m64 -m80387 -malign-stringops -matt-stubs
# -mconstant-cfstrings -mfancy-math-387 -mfp-ret-in-387 -mfxsr -mieee-fp
# -mlong-double-80 -mmmx -mno-sse4 -mpush-args -mred-zone -msse -msse2
# -msse3

	.cstring
LC0:
	.ascii "num = %d\12\0"
	.text
	.globl _main
_main:
LFB4:
	pushq	%rbp			#
LCFI0:
	movq	%rsp, %rbp		#,
# 汇编中的ch, num变量就是上文程序中的变量
LCFI1:
	subq	$16, %rsp		#,
	movb	$97, -1(%rbp)		#, ch
	movl	$2028, -8(%rbp)		#, num
	movl	-8(%rbp), %eax		# num, tmp85
	movl	%eax, %esi		# tmp85,
	leaq	LC0(%rip), %rdi		#,
	movl	$0, %eax		#,
	call	_printf			#
	movl	$0, %eax		#, D.3126
	leave
LCFI2:
	ret

PS: 这个选项是非常有用,可以在学习gcc生成的汇编时辅助理解汇编代码。

gcc -ggdb3选项

通过man gcc查看-ggdb选项的用途:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    -ggdb
        Produce debugging information for use by GDB.  This means to use the most
        expressive format available (DWARF, stabs, or the native format if neither of
        those are supported), including GDB extensions if at all possible.
    -ggdblevel
        Request debugging information and also use level to specify how much
        information.  The default level is 2.

        Level 0 produces no debug information at all.  Thus, -g0 negates -g.

        Level 1 produces minimal information, enough for making backtraces in parts of
        the program that you don't plan to debug.  This includes descriptions of
        functions and external variables, and line number tables, but no information
        about local variables.

        Level 3 includes extra information, such as all the macro definitions present
        in the program.  Some debuggers support macro expansion when you use -g3.

PS:后续建议在linux平台下使用-ggdb3替换-g选项。

gcc -D选项

使用gcc --help=separate可以查看-D选项的含义:

1
2
  -D<macro>[=<val>]           Define a <macro> with <val> as its value.  If
                              just <macro> is given, <val> is taken to be 1

-D选项是用来在使用gcc/g++编译的时候定义宏的。gcc -DDEBUG中的-D后面直接跟宏命,相当于定义这个宏,默认这个宏的内容是1。gcc -DKEY=VALUE表示定义KEY这个宏,它的内容是VALUE

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ cat gcc_def_macro.c
#include <stdio.h>
#include <stdlib.h>

int main() {
#ifdef DEBUG
    printf("Defined DEBUG(%d) macro\n", DEBUG);
#else
    printf("Not defined DEBUG\n");
#endif

    return 0;
}

$ gcc -ggdb3 -Wall gcc_def_macro.c -o gcc_def_macro
$ ./gcc_def_macro
Not defined DEBUG

$ gcc -ggdb3 -Wall -DDEBUG gcc_def_macro.c -o gcc_def_macro
$ ./gcc_def_macro
Defined DEBUG(1) macro

$ gcc -ggdb3 -Wall -DDEBUG=128 gcc_def_macro.c -o gcc_def_macro
$ ./gcc_def_macro
Defined DEBUG(128) macro

PS:-Dprivate=public -Dprotected=public可以用于测试环境,把privateprotected全替换为public。比如我们使用gtest去写单元测试,直接测试目标类的成员函数,但是目标类的成员函它可能是private或者protected,所以我们使用这个宏把它们全变成public

gcc -U选项

gcc -U用于取消宏(用于取消使用-D选项定义的宏,而不是源码中定义的宏)。

1
2
3
4
5
6
7
8
-U<macro>                   Undefine <macro>

$ gcc -ggdb3 -Wall -DDEBUG=512 gcc_def_macro.c -o gcc_def_macro
$ ./gcc_def_macro
Defined DEBUG(512) macro
$ gcc -ggdb3 -Wall -DDEBUG=512 -UDEBUG gcc_def_macro.c -o gcc_def_macro
$ ./gcc_def_macro
Not defined DEBUG

gcc -O选项

使用man gcc然后搜索--help=optimizers即可查看优化的选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ gcc -Q --help=optimizers
The following options control optimizations:
  -O<number>
  -Ofast
  -Og
  -Os
  -faggressive-loop-optimizations 	[enabled]
  -falign-functions           		[disabled]
  -falign-jumps               		[disabled]
  -falign-labels              		[disabled]
  -falign-loops               		[disabled]
  ......
  -fasynchronous-unwind-tables 		[enabled]
  -fbranch-count-reg          		[disabled]
  -fbranch-probabilities      		[disabled]
  -fbranch-target-load-optimize 	[disabled]
  -fbranch-target-load-optimize2 	[disabled]
  -fbtr-bb-exclusive          		[disabled]

根据man gcc的手册,可以使用如下方式查看-O2-O3的优化区别:

1
2
3
 gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
 gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
 diff /tmp/O2-opts /tmp/O3-opts | grep enabled