gcc源码阅读01

gcc源码阅读01

exdoubled Lv4

gcc 是一个很庞大的项目,包含了很多模块和组件,我是第一次阅读这么大的项目,所以我决定跟着 GCC for New Contributors — GCC Contributors Guide 0.1 documentation 先来了解一个大概

我使用的 gcc 版本如下

1
gcc.exe (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r5) 15.2.0

wsl 中下载了两个

1
2
gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
gcc-15 (Ubuntu 15.2.0-14ubuntu1~24~ppa1) 15.2.0

一般后面的程序运行我都会使用 wsl 上的 gcc-15 进行编译

二进制和程序

简单的 c 程序:

1
2
3
4
5
6
#include <stdio.h>
int main ()
{
printf ("hello world\n");
return 0;
}

使用 gcc 编译时

1
2
3
4
5
gcc hello.c -o hello
ls
hello hello.c
./hello
hello world

看起来很简单,但是其实发生了很多操作,gcc 的 C 编译器将 ,c 文件编译到汇编器中,生成 .cc1 的二进制文件,然后汇编器将 .cc1 文件编译成 .s 的汇编文件,最后链接器将 .s 文件链接成可执行文件 hello(ELF文件)

这个二过程可以通过 -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
33
34
35
36
37
# gcc-15 hello.c -o hello -v
Using built-in specs.
COLLECT_GCC=gcc-15
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/15/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 15.2.0-14ubuntu1~24~ppa1' --with-bugurl=file:///usr/share/doc/gcc-15/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust,cobol,algol68 --prefix=/usr --with-gcc-major-version-only --program-suffix=-15 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-15-9rFPkZ/gcc-15-15.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-15-9rFPkZ/gcc-15-15.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 15.2.0 (Ubuntu 15.2.0-14ubuntu1~24~ppa1)
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'
/usr/libexec/gcc/x86_64-linux-gnu/15/cc1 -quiet -v -imultiarch x86_64-linux-gnu hello.c -quiet -dumpbase hello.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccazwD5q.s
GNU C23 (Ubuntu 15.2.0-14ubuntu1~24~ppa1) version 15.2.0 (x86_64-linux-gnu)
compiled by GNU C version 15.2.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/include-fixed/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/15/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
Compiler executable checksum: 9f19bc9cfc443ddd3de419fa42b46591
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'
/usr/bin/x86_64-linux-gnu-as -v --64 -o /tmp/ccBZEtx5.o /tmp/ccazwD5q.s
GNU assembler version 2.42 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.42
COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/15/:/usr/libexec/gcc/x86_64-linux-gnu/15/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/15/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/15/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'hello.'
/usr/libexec/gcc/x86_64-linux-gnu/15/collect2 -plugin /usr/libexec/gcc/x86_64-linux-gnu/15/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/15/lto-wrapper -plugin-opt=-fresolution=/tmp/cceF2KPL.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/15/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/15 -L/usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/15/../../../../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/15/../../.. -L/lib -L/usr/lib /tmp/ccBZEtx5.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/15/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'hello.'

一步一步拆解这个输出

1
2
3
4
5
6
7
8
9
10
11
Using built-in specs.
COLLECT_GCC=gcc-15
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/15/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 15.2.0-14ubuntu1~24~ppa1' --with-bugurl=file:///usr/share/doc/gcc-15/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust,cobol,algol68 --prefix=/usr --with-gcc-major-version-only --program-suffix=-15 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-15-9rFPkZ/gcc-15-15.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-15-9rFPkZ/gcc-15-15.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 15.2.0 (Ubuntu 15.2.0-14ubuntu1~24~ppa1)
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64'

这一部分是 gcc 编译器的配置信息和版本信息,这一大串其实包含了下面的信息

  • gcc 的版本是 15.2.0
  • 系统和架构是 x86_64-linux-gnu
  • 支持的语言有 c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust,cobol,algol68
  • 支持的线程模型是 posix
  • 链接时优化

然后调用 cc1

1
/usr/libexec/gcc/x86_64-linux-gnu/15/cc1 -quiet -v -imultiarch x86_64-linux-gnu hello.c -quiet -dumpbase hello.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccazwD5q.s

这个其实是一个命令,GCC 编译器内部组件 cc1 的命令行,cc1 是 GCC 的 C 语言编译器的前端,拆开来看这个命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/usr/libexec/gcc/x86_64-linux-gnu/15/cc1 \
-quiet \
-v \
-imultiarch x86_64-linux-gnu hello.c \
-quiet \
-dumpbase hello.c \
-dumpbase-ext .c \
-mtune=generic \
-march=x86-64 \
-version \
-fasynchronous-unwind-tables \
-fstack-protector-strong \
-Wformat \
-Wformat-security \
-fstack-clash-protection \
-fcf-protection \
-o /tmp/ccazwD5q.s

来拆解一下:

  • /usr/libexec/gcc/x86_64-linux-gnu/15/cc1:这是 cc1,它不在系统的标准 PATH 路径中,而是被 gcc 驱动程序调用,用于将 C 代码编译为汇编代码

  • -quite :这个选项被提供了两次。它的作用是抑制编译器在处理过程中输出常规的调试信息到 stderr。如果没有该选项,cc1 会输出更多的内部处理信息

  • -v 启用详细输出模式,cc1 会向 stderr 输出大量信息,例如搜索路径、版本信息以及编译过程中的具体步骤

  • -imultiarch x86_64-linux-gnu:这个选项告诉 cc1 在编译过程中使用特定的多架构搜索路径,适用于 x86_64-linux-gnu 架构。这对于支持多架构的系统非常重要

  • hello.c 这个是输入文件,cc1 将编译这个 C 源文件

  • -dumpbase hello.c 告诉 cc1 在创建任何转储文件时,应该使用 hello.c 作为文件名的基础。默认情况下它不会创建任何转储文件

  • -dumpbase-ext .c 指定了转储文件的扩展名为 .c,其实意思是指定从 -dumpbase 设置的基础名称中移除的文件扩展名

  • -mtune=generic 特定于 x86 架构的调优选项,告诉编译器生成适合广泛 x86 处理器的代码,而不是针对特定处理器进行优化

  • -march=x86-64 指定目标架构为 x86-64,这会启用该架构特有的指令集和功能

  • -version 使 cc1 在标准错误输出(stderr)中打印详细的版本信息和配置说明

  • -fasynchronous-unwind-tables 启用异步展开表的生成,这对于支持异常处理和栈展开非常重要

  • -fstack-protector-strong 启用启用堆栈保护机制以防止缓冲区溢出攻击,比普通的 -fstack-protector 更严格,插入了更多的保护代码

  • -Wformat-Wformat-security 这两个选项启用格式字符串相关的警告,前者启用对 printfscanf 等格式字符串函数的参数类型检查,确保格式说明符与传入的参数类型匹配;后者增加对可能不安全的格式字符串使用的警告

  • -fstack-clash-protection 启用堆栈冲突保护机制,防止攻击者通过大量分配堆栈空间来触发堆栈溢出

  • -fcf-protection 启用控制流保护机制,防止攻击者通过修改返回地址或函数指针来改变程序的控制流,一种硬件辅助的安全特性

  • -o /tmp/ccazwD5q.s 指定输出文件为 /tmp/ccazwD5q.s,这是 cc1 生成的汇编代码文件,是一个临时文件,gcc 会在完成后续步骤(汇编和链接)后删除

可以在 Invoking GCC (Using the GNU Compiler Collection (GCC)) 中查看所有的选项,或者在命令行中使用 man gcc 来查看 gcc 的手册页,其中也包含了这些选项的说明

gcc 使用 -v 将 cc1 的输出直接发送到 stderr,所以我们可以看到 cc1 的版本信息和配置说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GNU C23 (Ubuntu 15.2.0-14ubuntu1~24~ppa1) version 15.2.0 (x86_64-linux-gnu)
compiled by GNU C version 15.2.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/include-fixed/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/15/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
Compiler executable checksum: 9f19bc9cfc443ddd3de419fa42b46591

然后是对汇编器 as 的调用

1
/usr/bin/x86_64-linux-gnu-as -v --64 -o /tmp/ccBZEtx5.o /tmp/ccazwD5q.s

这只是说明输入 .s 文件,输出 .o 文件,并且启用详细输出

然后是链接器

1
2
3
4
5
COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/15/:/usr/libexec/gcc/x86_64-linux-gnu/15/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/15/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/15/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/15/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'hello.'
/usr/libexec/gcc/x86_64-linux-gnu/15/collect2 -plugin /usr/libexec/gcc/x86_64-linux-gnu/15/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/15/lto-wrapper -plugin-opt=-fresolution=/tmp/cceF2KPL.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/15/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/15 -L/usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/15/../../../../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/15/../../.. -L/lib -L/usr/lib /tmp/ccBZEtx5.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/15/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/15/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-o' 'hello' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'hello.'

在这种情况下,gcc 调用了 collect2 来链接生成最终的可执行文件 hello,collect2 是一个 GCC 的链接器驱动程序,它负责调用系统的链接器(如 ld)来完成链接过程,同时处理一些 GCC 特有的选项和功能,例如 LTO 插件和库的链接

这玩意比 cc1 还长,就不拆解命令是什么意思了,总之就是链接器的调用,链接器会将之前生成的 .o 文件和一些库文件链接成最终的可执行文件 hello

查看生成的汇编

我们可以使用 -S 选项来让 gcc 直接输出汇编代码,而不是生成可执行文件

1
2
3
gcc-15 -S hello.c
ls
hello hello.c hello.s

这里的步骤是这样的

1
2
         cc1
hello.c -----> hello.s

查看一下汇编

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
        .file   "hello.c"
.text
.section .rodata
.LC0:
.string "hello world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rax
movq %rax, %rdi
call puts@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 15.2.0-14ubuntu1~24~ppa1) 15.2.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:

可以看到,顶部有一些汇编指令和数据段的定义,.file 指令指定了源文件的名称,.text 指令表示接下来的代码段,.section .rodata 定义了一个只读数据段,其中包含了字符串 “hello world”,.globl main 声明了 main 函数是全局可见的,.type main, @function 指定了 main 是一个函数

main 函数的汇编代码从 main: 标签开始,首先是一些函数前置代码,例如保存栈帧和设置基指针,然后是加载字符串地址到寄存器并调用 puts 函数,最后是返回 0 并结束函数

最后还有一些元信息,例如 .ident 指令包含了编译器的版本信息,.section .note.GNU-stack.section .note.gnu.property 定义了一些特殊的段,这些段通常用于指定栈的属性和其他元数据

可以使用 -fverbose-asm 选项来让 gcc 生成更详细的汇编注释,这样可以更容易理解每条指令的作用

1
gcc-15 -S hello.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
        .file   "hello.c"
# GNU C23 (Ubuntu 15.2.0-14ubuntu1~24~ppa1) version 15.2.0 (x86_64-linux-gnu)
# compiled by GNU C version 15.2.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP

# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
# options passed: -mtune=generic -march=x86-64 -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection
.text
.section .rodata
.LC0:
.string "hello world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #,
.cfi_def_cfa_register 6
# hello.c:4: printf ("hello world\n");
leaq .LC0(%rip), %rax #, tmp100
movq %rax, %rdi # tmp100,
call puts@PLT #
# hello.c:5: return 0;
movl $0, %eax #, _3
# hello.c:6: }
popq %rbp #
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 15.2.0-14ubuntu1~24~ppa1) 15.2.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:

这下就可以关注 main() 的部分了

1
2
3
4
5
6
7
8
9
10
# hello.c:4:   printf ("hello world\n");
leaq .LC0(%rip), %rax #, tmp100
movq %rax, %rdi # tmp100,
call puts@PLT #
# hello.c:5: return 0;
movl $0, %eax #, _3
# hello.c:6: }
popq %rbp #
.cfi_def_cfa 7, 8
ret

cc1

gcc 是一个针对不同编程语言的编译器集合,支持 10 中源语言和 55 种 CPU 架构,由于不想编写 550 个编译器,所以会把 gcc 结构化为三个部分:

  • 源语言的前端:负责解析源代码,进行语义分析和优化,生成中间表示(IR),例如 cc1 就是 C 语言的前端
  • 特定目标的后端:负责将中间表示转换为特定目标架构的机器代码,例如 x86 后端
  • 介于两者的中间端:负责在前端和后端之间进行优化和转换,例如 GIMPLE 和 RTL

因此,为 x86_64 构建的 cc1 包含了 C 前端、共享的优化代码和 x86_64 后端的代码

1
2
3
4
5
6
7
8
               +-----+
+-| cc1 |--------------------------------------------------+
| +-----+ |
| |
| C 前端 优化器 x86_64 后端 |
C 源代码 ====+==============> IR =============> IR ===================+==> x86_64 汇编
| |
+----------------------------------------------------------+
Comments
On this page
gcc源码阅读01