GCC
GCC 技术概览
前言
GCC(GNU Compiler Collection)是自由软件基金会旗下最负盛名的项目之一。自 1987 年诞生以来,它已从一个仅支持 C 语言的单一编译器,成长为支持数十种语言、数十个平台的编译器基础设施。作为 GNU 工具链的核心与 Linux 生态的基石,几乎所有 Linux 发行版上的系统软件和用户程序都由 GCC 编译生成。
第一章 历史与现状
1.1 诞生与早期发展
1987 年,Richard Stallman 启动了 GNU C Compiler 项目,为构建 GNU 操作系统提供核心编译工具。该编译器最初仅支持 C 语言,但因其性能优异、自由开放,迅速成为 Unix 世界中最重要的编译器之一。
1.2 EGCS 分叉与变革
1997 年,由于 GCC 官方开发流程过于封闭、代码合并缓慢,一群开发者发起 EGCS(Experimental/Enhanced GNU Compiler System)分叉。EGCS 采用了更开放的开发模式,积极接纳各类语言前端和优化改进。这次分叉深刻改变了 GCC 的命运——1999 年,EGCS 被正式采纳为官方版本,项目同时更名为 GNU Compiler Collection,标志着它从一个单一语言编译器转变为多语言编译器集合。此后 GCC 的发展模式持续开放至今,这直接塑造了其模块化、可扩展的架构设计。
1.3 关键版本里程碑
| 版本 | 年份 | 里程碑 |
|---|---|---|
| GCC 1.0 | 1987 | 首个公开发布版本 |
| GCC 2.0 | 1992 | 加入 C++ 支持 |
| GCC 3.0 | 2001 | 统一架构,整合所有语言前端 |
| GCC 4.0 | 2005 | 引入 SSA(静态单赋值)优化框架 |
| GCC 5.0 | 2015 | 默认 C++ ABI 切换至 C++11 标准 |
| GCC 10.0 | 2020 | 全面支持 C++20 |
| GCC 14.0 | 2024 | 持续演进中 |
1.4 版本发布策略
GCC 采用年度主版本发布策略,通常每年上半年发布一个新的主版本号(如 GCC 13、GCC 14)。每个主版本在发布后会进入维护期,持续接收 bug 修复和向后移植。版本号规则为 主版本.次版本.补丁版本,主版本号变化意味着新增语言标准支持、重大优化改进或架构适配。开发者可以通过 gcc -v 查看当前使用的 GCC 版本及其编译配置详情。
第二章 编译器家族
2.1 语言前端一览
GCC 是一个编译器集合,包含多个语言前端命令:
| 命令 | 语言 | 说明 |
|---|---|---|
gcc | C | C 语言编译器 |
g++ | C++ | C++ 语言编译器(自动链接 libstdc++) |
gfortran | Fortran | 取代旧的 g77 |
gnat | Ada | Ada 语言编译器 |
gccgo | Go | Go 语言编译器 |
gdc | D | D 语言编译器 |
cc1obj | Objective-C | Objective-C 编译器 |
cc1objplus | Objective-C++ | Objective-C++ 编译器 |
2.2 支持的语言标准
每个语言前端支持多个版本的语言标准,通过 -std= 选项指定:
| 语言 | 支持的标准 |
|---|---|
| C | C89, C99, C11, C17, C23 |
| C++ | C++98, C++11, C++14, C++17, C++20, C++23 |
| Fortran | F77, F90, F95, F2003, F2008, F2018 |
| Ada | Ada 83, Ada 95, Ada 2005, Ada 2012 |
| Go | Go 1 |
| D | D 2.0 |
第三章 支持的平台
GCC 以其广泛的跨平台支持而闻名。通过前后端分离的设计,只需更换后端即可适配新架构。
3.1 处理器架构
- x86 / x86_64:Intel 和 AMD 主流桌面/服务器处理器
- ARM (32/64 位):移动设备和嵌入式领域的主导架构
- AArch64:ARM 64 位架构
- RISC-V:开源指令集架构,新兴生态系统
- MIPS:历史悠久,广泛应用于嵌入式与网络设备
- PowerPC:IBM 体系,用于服务器及嵌入式领域
- SPARC:Sun/Oracle 服务器架构
- s390x:IBM 大型机架构
- LoongArch:国产自主指令集架构
3.2 操作系统
- GNU/Linux:主要目标平台,支持所有主流发行版
- Windows:通过 MinGW 或 Cygwin 环境运行
- macOS:在 Apple 平台上进行本地编译
- FreeBSD / OpenBSD / NetBSD:BSD 家族系统
- Solaris:Oracle Solaris 系统
3.3 嵌入式与裸机开发
GCC 可用于裸机(bare-metal)环境下的嵌入式开发,生成不依赖操作系统的固件代码。通过配置适当的 --target 和链接脚本,开发者可以为微控制器等无操作系统环境编译程序。
第四章 核心架构
GCC 采用经典的前端-中端-后端三段式设计。这种模块化分离使其既能支持多种编程语言,又能生成多种目标平台的机器代码。
4.1 整体流程
源码 (Source Code)
↓
前端: 词法分析 → 语法分析 → 语义分析 → 生成抽象语法树 (AST)
↓
GENERIC: 语言无关的 AST 表示
↓
中端优化第一阶段
↓
GIMPLE: 三地址码,控制流简化
↓
GIMPLE SSA: 静态单赋值形式,便于数据流分析
↓
中端优化第二阶段(绝大多数优化在此完成)
↓
RTL (Register Transfer Language): 低层中间表示
↓
后端: 指令选择 → 指令调度 → 寄存器分配 → 汇编生成
↓
汇编代码 (Assembly)
4.2 前端(Front End)
前端负责处理特定语言的源代码,执行词法分析、语法分析和语义分析。不同语言对应不同的前端程序:
| 前端程序 | 语言 |
|---|---|
cc1 | C |
cc1plus | C++ |
f951 | Fortran |
go1 | Go |
前端将源代码解析为 AST,再逐步降级为 GENERIC,最终输出 GIMPLE 供中端处理。
4.3 中端(Middle End)
中端是 GCC 优化能力的核心所在。它在 GIMPLE SSA 形式上运行超过 200 个独立的优化 Pass,执行语言和架构无关的变换,包括:
- 常量传播与折叠
- 死代码与不可达代码消除
- 循环优化(展开、向量化、融合、交换)
- 函数内联
- 公共子表达式消除
- 尾调用优化
- 值域传播与分支预测
4.4 后端(Back End)
后端将优化后的 RTL 映射为目标机器的指令。此阶段处理架构相关任务:
- 指令选择:从目标指令集中选取最优指令序列
- 指令调度:重排指令顺序以充分利用 CPU 流水线
- 寄存器分配:将虚拟寄存器映射到物理寄存器
- 汇编代码输出
第五章 编译原理基础概念
理解 GCC 的运作需要一些编译器理论基础知识。以下概念将在后续章节中频繁出现,此处做简要定义。
5.1 词法分析
将源代码字符流分解为有意义的 token 序列,如关键字(int、return)、标识符(变量名、函数名)、字面量(42、"hello")、运算符(+、=)。
5.2 语法分析
根据语言语法规则,将 token 流组织成树状结构——抽象语法树(AST, Abstract Syntax Tree)。AST 保留了源代码的层级结构。
5.3 语义分析
检查程序的语义正确性:类型是否匹配、变量是否声明、函数调用参数是否合法等,并对 AST 进行必要的标注。
5.4 中间代码生成
将 AST 转换为编译器内部的中间表示(IR),逐步降级到接近机器指令的形式。
5.5 代码优化
在中间表示上进行等价变换,在不改变程序语义的前提下,提升执行效率或缩小代码体积。
5.6 目标代码生成
将优化后的中间表示转换为目标机器的汇编语言或直接生成机器码。
第六章 中间表示体系
GCC 内部使用三层递进式的中间表示,这是其架构的标志性特征。
6.1 GENERIC
GENERIC 是语言无关的 AST 表示。各语言前端将各自语言的 AST 统一转换为 GENERIC,屏蔽了语言差异。它保留了源代码的高层结构,是一个过渡性的表示。
6.2 GIMPLE
GIMPLE 是 GCC 的核心 IR,中端绝大多数优化在此运行。
三地址码:每条 GIMPLE 语句最多包含三个操作数,形式如 t1 = a + b。复杂的表达式被拆解为一系列简单的三地址操作。
控制流简化:if、while、for 等高阶控制结构被分解为 goto 跳转和基本块。
查看 GIMPLE:gcc -fdump-tree-gimple main.c 可将 GIMPLE 以文本形式输出,便于调试和分析优化过程。
6.3 GIMPLE SSA(静态单赋值形式)
SSA(Static Single Assignment)是现代编译器优化的基石。其核心规则是:程序中每个变量在静态文本上只被赋值一次。当控制流汇合时,通过 φ(phi)函数 合并来自不同路径的值。
SSA 的优势:
- 定义-使用链(def-use chain)变得直接且显式
- 极大简化了死代码消除、常量传播、值域分析等优化算法的实现
- 节省内存和计算开销
GCC 先将 GIMPLE 转换为 SSA 形式运行大量优化,优化完成后再退出 SSA 形式进入 RTL 阶段。
6.4 RTL(Register Transfer Language)
RTL 是 GCC 最低层的中间表示,接近机器指令。它采用 S-expression(类 Lisp 风格)描述数据从寄存器到寄存器的传输及操作。
RTL 阶段负责:
- 指令选择(将操作映射为目标机指令)
- 指令组合(合并相邻指令)
- 寄存器分配
- 指令调度
查看 RTL:gcc -fdump-rtl-all main.c 可输出各 RTL Pass 的结果。
第七章 基本块与控制流图
7.1 基本块(Basic Block)
基本块是一段顺序执行的指令序列,只有一个入口(第一条指令)和一个出口(最后一条指令),中间不存在跳转指令进入或离开。
7.2 控制流图(CFG)
控制流图以基本块为节点,以跳转关系为有向边。GCC 绝大多数中端优化都构建在 CFG 之上,依赖它进行循环检测、支配关系分析和数据流分析。
第八章 Pass 系统
GCC 的优化不是由一个大型函数完成的,而是由超过 200 个独立的 Pass 组成的管道,按预定顺序依次在 IR 上运行。
8.1 GIMPLE Pass
运行在 GIMPLE SSA 上的优化,执行语言和架构无关的变换:
- 函数内联、过程间优化(IPA)
- 循环展开、向量化、融合
- 公共子表达式消除
- 死代码消除
- 别名分析
- 尾调用优化
8.2 RTL Pass
运行在 RTL 上的优化与准备工作:
- 指令组合(合并相邻指令以利用复杂指令)
- 寄存器分配(图着色)
- 指令调度(流水线优化)
- Peephole 优化(局部指令模式替换)
- 延迟槽填充(部分架构)
8.3 查看与调试 Pass
- 查看 Pass 执行顺序:
gcc -O2 -fdump-passes main.c - 单独关闭某个 Pass:
-fdisable-tree-vrp1、-fdisable-rtl-combine - 查看某 Pass 前后的 IR 变化:
-fdump-tree-all、-fdump-rtl-all
第九章 寄存器分配
寄存器分配是将 IR 中无限的虚拟寄存器映射到目标机有限物理寄存器的过程,是编译器后端最关键也最复杂的步骤之一。
9.1 图着色法
将寄存器分配问题建模为图着色问题:构建寄存器冲突图,两个变量若同时存活则存在一条边。用不超过物理寄存器数量的颜色对图着色,若无法完成则需溢出。
9.2 溢出(Spilling)
当物理寄存器不足时,某些变量的值被暂存到栈内存中,使用时再取出。溢出点选择直接影响代码性能。
9.3 GCC 的实现
GCC 采用了改进型的寄存器分配器:
- IRA(Integrated Register Allocator):集成寄存器分配器,使用优先图着色分配
- LRA(Local Register Allocator):局部寄存器分配器,在处理复杂约束和溢出时更加鲁棒
第十章 指令选择与调度
10.1 指令选择
将 RTL 操作模式匹配到目标机的具体机器指令。GCC 使用机器描述文件(.md) 声明式地定义指令模板,通过自动工具生成模式匹配代码。
10.2 指令调度
重排指令执行顺序,以充分利用 CPU 的指令流水线,减少因数据冒险、结构冒险导致的停顿(stall)。
10.3 Peephole 优化
在已生成的指令序列上,滑动一个小的观察窗口,识别出低效的模式并替换为更优的等价序列。
第十一章 机器描述文件
GCC 后端不是用 C 代码硬编码的,而是通过机器描述文件(.md 文件)声明式描述目标架构。
机器描述文件包含:
- 指令模板:每条机器指令对应的 RTL 模式
- 扩展模式:多条指令可合并为一条的优化规则
- 属性定义:指令延迟、功能单元占用、指令长度等信息,供调度器和优化器使用
GCC 构建过程中会自动处理这些 .md 文件,生成后端代码的绝大部分。这一设计使移植 GCC 到新架构的工作量大幅降低。
第十二章 调用约定与 ABI
GCC 生成的代码必须遵循目标平台的 ABI(Application Binary Interface)和调用约定,以确保不同编译单元、不同语言编译的代码能够正确互操作。
12.1 寄存器使用约定
- 参数传递寄存器:指定哪些寄存器用于传递函数参数
- 调用者保存寄存器(caller-saved):调用前后可能被修改,调用者需自行保存
- 被调用者保存寄存器(callee-saved):被调用者必须在使用前保存原值,返回前恢复
12.2 栈帧布局
- 参数传递顺序(通常为从右向左压栈)
- 返回值的存放位置(寄存器或栈)
- 栈对齐要求(如 x86_64 要求 16 字节对齐)
12.3 名称修饰(Name Mangling)
C++ 的函数重载、模板、命名空间等特性要求将函数名编码为唯一的符号名。GCC 遵循 Itanium C++ ABI 规范进行名称修饰。
12.4 主要支持的 ABI
- System V AMD64 ABI(x86_64 Linux)
- ARM EABI / AArch64 ABI
- 各架构的嵌入式 ABI 变体
第十三章 调试信息
GCC 可将源码级别的调试信息嵌入目标文件中,供 GDB 等调试器使用。
13.1 支持的标准
- DWARF(主流标准,版本 2/3/4/5):功能丰富,支持复杂类型、宏信息等
- STABS(老旧格式,已较少使用)
- CTF / BTF:用于 BPF 等场景的紧凑调试格式
13.2 DWARF 包含的信息
- 源代码行号到指令地址的映射
- 变量类型信息及作用域
- 栈帧描述(用于栈展开和回溯)
- 调用约定信息
13.3 使用选项
-g:生成默认格式调试信息(通常为 DWARF)-ggdb:生成包含 GDB 扩展的调试信息-gdwarf-4:指定 DWARF 版本-fno-omit-frame-pointer:保留帧指针,方便调试和分析
第十四章 异常处理机制
GCC 支持两种异常处理模型,用于 C++ 的 try/catch 及类似机制。
14.1 DWARF 零成本异常处理
正常执行路径完全无额外开销(“零成本”)。异常抛出时,通过 DWARF 的栈展开表(Call Frame Information)逐帧回溯调用栈,调用析构函数并寻找匹配的异常处理器。现代 Linux 平台默认使用此模型。
14.2 SjLj(setjmp/longjmp)异常
基于 C 标准库的 setjmp/longjmp 实现。实现简单,但在正常执行路径中也有记账开销。主要用于部分不支持 DWARF 展开的嵌入式平台。
14.3 栈展开(Stack Unwinding)
当异常被抛出时,运行时系统沿调用栈向上回溯,依次执行各栈帧中的析构函数,直到异常被某个 catch 捕获或程序终止。
第十五章 编译流程
gcc 命令实质是一个编译器驱动(Compiler Driver),它按顺序调用独立工具链组件完成四个阶段:
15.1 预处理
调用 cpp,处理 #include、#define、#ifdef 等预处理指令,展开宏和头文件。
gcc -E main.c -o main.i
输出:main.i(预处理后的源文件)
15.2 编译
调用语言前端(cc1 等),将预处理后的代码编译为汇编。
gcc -S main.c -o main.s
输出:main.s(汇编代码)
15.3 汇编
调用 as,将汇编代码转换为目标文件。
gcc -c main.c -o main.o
输出:main.o(目标文件)
15.4 链接
调用 collect2(它包装系统链接器 ld),将多个目标文件和库链接为可执行文件。
gcc main.o -o app
输出:app(可执行文件)
第十六章 输出格式
GCC 在编译各阶段可输出不同格式的中间产物:
| 选项 | 输出文件 | 内容 |
|---|---|---|
-E | .i / .ii | 预处理后的源代码(展开所有宏和头文件) |
-S | .s | 汇编代码(文本格式) |
-c | .o | 目标文件(二进制) |
-fdump-tree-gimple | .gimple | GIMPLE 中间表示 |
-fdump-rtl-all | .rtl.* | RTL 中间表示各阶段 |
此外,GCC 默认输出的可执行文件为 ELF 格式(Linux),也可配置为 PE(Windows)、Mach-O(macOS)等格式。
第十七章 目标文件结构
目标文件(.o)遵循 ELF(Executable and Linkable Format)格式(Linux 平台),内部包含:
| 段 | 用途 |
|---|---|
.text | 编译后的机器指令代码 |
.data | 已初始化的全局变量和静态变量 |
.bss | 未初始化的全局变量(只在文件头记录大小,不占磁盘空间) |
.rodata | 只读数据(字符串常量、const 全局变量) |
| 符号表 | 记录函数名、变量名及其对应的地址或偏移 |
| 重定位表 | 记录需要链接器在最终链接时修正的地址占位符 |
第十八章 静态库与动态库
GCC 支持生成和使用两种共享代码的方式。
18.1 静态库(.a)
将多个目标文件打包为一个存档文件,链接时复制所需代码到可执行文件中。
创建静态库:
gcc -c math.c -o math.o
ar rcs libmath.a math.o
使用静态库:
gcc main.c -L. -lmath -o app
18.2 动态库(.so)
代码在运行时被加载,多个程序可以共享同一份库文件,节省内存和磁盘空间。
创建动态库:
gcc -fPIC -c math.c -o math.o
gcc -shared math.o -o libmath.so
使用动态库:
gcc main.c -L. -lmath -o app
LD_LIBRARY_PATH=. ./app
18.3 位置无关代码(PIC)与 PLT/GOT
动态库需被加载到不同进程的不同地址空间,因此必须编译为位置无关代码(Position Independent Code),不依赖绝对地址。GCC 通过 -fPIC 选项生成 PIC 代码。动态链接时,PLT(过程链接表)和 GOT(全局偏移量表)配合实现延迟符号绑定。
第十九章 链接过程
19.1 符号解析
链接器将每个符号引用(函数调用、全局变量访问)与其唯一定义进行匹配。未匹配到的引用导致 undefined reference 错误。
19.2 重定位
目标文件中的地址占位符在链接时被替换为最终运行时的绝对或相对地址。
19.3 动态链接
使用动态库时,链接器并不将库代码复制到可执行文件中,而是记录所依赖的共享库名称。程序启动时,动态链接器(ld.so)负责加载所需共享库并完成最终符号绑定。
第二十章 GCC 的自举构建
GCC 采用一种特殊的**三步构建(3-stage bootstrap)**方法编译自身:
- Stage 1:用系统已有的编译器编译 GCC 源码,得到初版 GCC
- Stage 2:用 Stage 1 编译出的 GCC 重新编译自己的源码,得到 Stage 2 的 GCC
- Stage 3:用 Stage 2 的 GCC 再次编译自己,并将输出与 Stage 2 的输出进行二进制对比
三步构建可确保编译器自身被正确编译,同时也能暴露编译器潜在的 bug。两次二进制对比的一致性也是判断构建成功的标志。
第二十一章 运行时库
GCC 生成的可执行文件在运行时依赖若干核心库。
21.1 libgcc
GCC 最底层的运行时库,提供目标 CPU 指令集未直接实现的功能:
- 64 位整型在 32 位机器上的除法与取模
- 浮点模拟(无硬件浮点单元时)
- 异常处理与栈展开基础支持
- 静态库版本
libgcc.a和共享库版本libgcc_s.so
21.2 libstdc++
C++ 标准库实现,g++ 自动链接。提供 STL 容器、算法、IO 流、字符串、线程支持等全部 C++ 标准设施。
21.3 libc
标准 C 库。Linux 上通常使用 GNU C Library(glibc),另有 musl、uClibc 等轻量级实现用于嵌入式场景。
第二十二章 高级特性
22.1 链接时优化(LTO)
传统编译以源文件为单位独立进行,跨文件的函数无法内联。LTO 解决了这一问题。
启用 LTO 时(-flto),编译器将 GIMPLE 中间表示与汇编代码一并存入 .o 文件。在最终链接阶段,链接器读取所有目标文件中的 GIMPLE,整合为一个完整单元,在全局范围内重新运行优化 Pass。
优点:跨文件内联、全局死代码消除、全程序常量传播等激进优化成为可能
缺点:显著增加链接时间和内存消耗
22.2 GCC 插件
GCC 提供插件接口,允许开发者在不修改 GCC 源码的情况下,向编译流水线中注入自定义逻辑。插件可以挂载到 GIMPLE Pass 或 RTL Pass 的各个节点。
典型应用:
- 自定义静态分析规则
- 代码安全检查
- 自动化插桩
- 性能 Profiling 工具开发
22.3 运行时检查(Sanitizers)
通过编译时插入检测代码,在运行时捕获各类错误:
| 选项 | 检测对象 |
|---|---|
-fsanitize=address | 内存错误(缓冲区溢出、use-after-free、内存泄漏) |
-fsanitize=thread | 数据竞争(多线程环境中的竞态条件) |
-fsanitize=undefined | 未定义行为(整数溢出、空指针解引用、非法类型转换等) |
-fsanitize=leak | 内存泄漏检测 |
-fsanitize=memory | 未初始化内存读取 |
22.4 交叉编译
GCC 从设计之初即支持交叉编译,能在一种平台上编译生成在另一种平台上运行的程序。
GCC 区分三个平台概念:
- 构建平台(build):运行编译器的机器
- 主机平台(host):编译器自身运行的平台
- 目标平台(target):生成的可执行文件运行的平台
在交叉编译场景中(build = host ≠ target),只需配置正确的目标后端即可。GCC 通过机器描述文件实现后端的快速移植,这使其成为嵌入式开发领域的工业标准。
第二十三章 配套工具链
23.1 编译过程调用的工具
| 工具 | 作用 |
|---|---|
cpp | C 预处理器 |
cc1 / cc1plus | C/C++ 语言编译器 |
as | GNU 汇编器 |
collect2 | 链接包装程序,调用 ld |
ld | GNU 链接器 |
23.2 二进制工具集(Binutils)
| 工具 | 功能 |
|---|---|
ar | 创建静态库(.a 文件) |
ranlib | 为静态库生成索引 |
nm | 列出目标文件中的符号 |
objdump | 反汇编目标文件或可执行文件 |
objcopy | 复制和转换目标文件格式 |
strip | 移除符号表和调试信息,减小体积 |
readelf | 解析并显示 ELF 文件详细信息 |
size | 显示 ELF 文件各段的大小 |
strings | 从二进制文件中提取可打印字符串 |
第二十四章 常用编译选项速查
24.1 优化级别
| 选项 | 说明 |
|---|---|
-O0 | 不优化(默认),编译速度最快 |
-O1 | 基本优化,平衡编译时间与效果 |
-O2 | 标准优化级别,包含绝大多数不增加体积的优化 |
-O3 | 激进优化,包含循环展开、函数内联等 |
-Os | 优化代码体积 |
-Ofast | 极致优化,忽略严格标准合规(可能改变浮点语义) |
24.2 调试与诊断
| 选项 | 说明 |
|---|---|
-g | 生成调试信息 |
-ggdb | 生成 GDB 专用调试信息 |
-Wall | 启用大多数常见警告 |
-Wextra | 启用额外警告 |
-Werror | 将所有警告视为错误 |
-Wpedantic | 严格遵循语言标准,发出标准合规性警告 |
-fdiagnostics-color=always | 彩色输出诊断信息 |
-fno-omit-frame-pointer | 保留帧指针 |
24.3 语言标准
| 选项 | 说明 |
|---|---|
-std=c11 | 使用 C11 标准 |
-std=c++17 | 使用 C++17 标准 |
-std=c++20 | 使用 C++20 标准 |
24.4 预处理
| 选项 | 说明 |
|---|---|
-I<dir> | 添加头文件搜索路径 |
-D<macro> | 定义宏 |
-D<macro>=<value> | 定义带值的宏 |
-U<macro> | 取消宏定义 |
24.5 链接
| 选项 | 说明 |
|---|---|
-L<dir> | 添加库文件搜索路径 |
-l<lib> | 链接指定库(如 -lm 链接数学库) |
-static | 静态链接 |
-shared | 生成动态库 |
-fPIC | 生成位置无关代码 |
-Wl,<option> | 向链接器传递选项 |
-Wl,-rpath=<dir> | 将运行时库搜索路径嵌入可执行文件 |
24.6 输出控制
| 选项 | 说明 |
|---|---|
-o <file> | 指定输出文件名 |
-c | 只编译和汇编,不链接 |
-S | 只编译,生成汇编代码 |
-E | 只运行预处理器 |
第二十五章 常见编译错误速览
| 错误类型 | 典型信息 | 常见原因 |
|---|---|---|
| 预处理错误 | fatal error: xxx.h: No such file or directory | 找不到头文件,使用 -I 指定路径 |
| 编译错误 | expected ';' before ... | 语法错误,检查上一行代码 |
| 编译警告 | implicit declaration of function ... | 忘记包含相应头文件 |
| 链接错误 | undefined reference to 'func' | 函数有声明无定义,或缺少库/目标文件 |
| 链接错误 | multiple definition of ... | 同一符号被重复定义 |
第二十六章 与其他编译器的关系
26.1 Clang/LLVM
Clang 是基于 LLVM 后端的 C/C++/Objective-C 编译器。与 GCC 相比,Clang 编译速度通常更快,错误信息更友好,对 IDE 和静态分析工具的支持更好。GCC 的优势在于更成熟的优化能力、更广泛的架构支持以及在 Linux 内核编译等场景的不可替代性。
26.2 MSVC(Microsoft Visual C++)
微软的 C/C++ 编译器,是 Windows 生态的主要编译器。GCC 通过 MinGW 提供了 Windows 平台的替代选择。两者在语法扩展、ABI、标准库实现上存在差异。
第二十七章 与构建系统的配合
GCC 通常不是孤立使用的,它嵌入在更大的开发生态中:
- make:经典的构建工具,通过 Makefile 组织编译规则
- CMake:跨平台的元构建系统,可生成 Makefile、Ninja 等
- Meson:现代、快速的构建系统
- Autotools:GNU 项目的传统构建系统
- GDB:GNU 调试器,消费 GCC 生成的 DWARF 调试信息
- Valgrind:运行时内存分析工具,与 GCC 编译产物配合使用
- perf / gprof:性能分析工具,利用 GCC 的
-pg或-fprofile-arcs选项生成数据
第二十八章 安装
28.1 通过包管理器安装(以 Ubuntu 为例)
sudo apt update
sudo apt install build-essential gdb binutils
build-essential:包含gcc、g++、make、libc-dev等基础开发工具gdb:GNU 调试器binutils:包含ld、as、objdump等二进制工具
验证安装:
gcc --version
g++ --version
gdb --version
ld --version
28.2 安装特定版本
sudo apt install gcc-15 g++-15
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 100
sudo update-alternatives --config gcc # 切换默认版本
28.3 从源码编译安装
适用于需要最新版本或自定义编译配置的场景:
# 下载源码
wget https://ftp.gnu.org/gnu/gcc/gcc-15.1.0/gcc-15.1.0.tar.gz
tar -xzf gcc-15.1.0.tar.gz
cd gcc-15.1.0
# 安装构建依赖
./contrib/download_prerequisites
# 配置与编译
mkdir build && cd build
../configure --prefix=/usr/local/gcc-15.1.0 \
--enable-languages=c,c++ \
--disable-multilib
make -j$(nproc) # 并行编译,充分利用多核
sudo make install
# 添加到 PATH
echo 'export PATH=/usr/local/gcc-15.1.0/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
第二十九章 文档与帮助
29.1 内置帮助
gcc --help # 查看所有选项概览
gcc --help=optimizers # 优化选项
gcc --help=warnings # 警告选项
gcc --help=target # 目标平台相关选项
gcc --help=common # 通用选项
29.2 查看当前启用的选项
gcc -Q -O2 --help=optimizers # 查看 O2 级别启用的优化
gcc -Q -O3 --help=optimizers # 与 O3 对比
gcc -Q --help=warnings | grep enabled # 查看默认启用的警告
29.3 手册页与在线文档
man gcc # 详尽的手册页
info gcc # GNU Info 文档
在线文档:https://gcc.gnu.org/onlinedocs
第三十章 许可证与社区
30.1 许可证
GCC 本身使用 GNU 通用公共许可证第三版(GPLv3+)。同时,GCC 的运行时库(libgcc、libstdc++ 等)附带运行时库例外条款(GCC Runtime Library Exception),允许编译生成的非 GPL 程序链接这些库而不被 GPL 传染。
30.2 参与贡献
GCC 的开发依托于开放的社区:
- 邮件列表:gcc@gcc.gnu.org 为主要讨论渠道
- Bug 跟踪:通过 GCC Bugzilla 报告和跟踪问题
- 代码仓库:Git 仓库托管于 git://gcc.gnu.org/git/gcc.git
- 编码规范:遵循 GCC Coding Conventions(主要是 C 语言的 GNU 风格)
- 版权转让:贡献代码通常需要签署 FSF 版权转让文件
附录:参考资源
- GCC 官方网站:https://gcc.gnu.org
- 在线文档:https://gcc.gnu.org/onlinedocs
- Wiki:https://gcc.gnu.org/wiki
- 邮件列表归档:https://gcc.gnu.org/ml/gcc/
- Bugzilla:https://gcc.gnu.org/bugzilla
GCC Technical Overview
Preface
GCC (GNU Compiler Collection) is one of the most prestigious projects under the Free Software Foundation. Since its inception in 1987, it has grown from a single compiler supporting only the C language into a compiler infrastructure supporting dozens of languages and platforms. As the core of the GNU toolchain and the cornerstone of the Linux ecosystem, virtually all system software and user programs on Linux distributions are compiled by GCC.
Chapter 1: History and Current Status
1.1 Origins and Early Development
In 1987, Richard Stallman launched the GNU C Compiler project to provide a core compilation tool for building the GNU operating system. Initially supporting only the C language, the compiler quickly became one of the most important compilers in the Unix world due to its excellent performance and open, free nature.
1.2 The EGCS Fork and Transformation
In 1997, due to the overly closed official GCC development process and slow code merging, a group of developers initiated the EGCS (Experimental/Enhanced GNU Compiler System) fork. EGCS adopted a more open development model, actively accepting various language front ends and optimization improvements. This fork profoundly changed GCC’s destiny—in 1999, EGCS was officially adopted as the official version, and the project was simultaneously renamed the GNU Compiler Collection, marking its transformation from a single-language compiler to a multi-language compiler collection. Since then, GCC’s development model has remained open to this day, which has directly shaped its modular and extensible architectural design.
1.3 Key Version Milestones
| Version | Year | Milestone |
|---|---|---|
| GCC 1.0 | 1987 | First public release |
| GCC 2.0 | 1992 | Added C++ support |
| GCC 3.0 | 2001 | Unified architecture, integrating all language front ends |
| GCC 4.0 | 2005 | Introduced SSA (Static Single Assignment) optimization framework |
| GCC 5.0 | 2015 | Default C++ ABI switched to C++11 standard |
| GCC 10.0 | 2020 | Full C++20 support |
| GCC 14.0 | 2024 | Continued evolution |
1.4 Version Release Strategy
GCC adopts an annual major version release strategy, typically releasing a new major version number (e.g., GCC 13, GCC 14) in the first half of each year. Each major version enters a maintenance period after release, continuously receiving bug fixes and backports. The versioning scheme is major.minor.patch, where a major version change signifies the addition of new language standard support, significant optimization improvements, or architecture adaptations. Developers can use gcc -v to view the currently used GCC version and its compilation configuration details.
Chapter 2: The Compiler Family
2.1 Language Front Ends at a Glance
GCC is a compiler collection that includes multiple language front-end commands:
| Command | Language | Description |
|---|---|---|
gcc | C | C language compiler |
g++ | C++ | C++ language compiler (automatically links libstdc++) |
gfortran | Fortran | Replaces the old g77 |
gnat | Ada | Ada language compiler |
gccgo | Go | Go language compiler |
gdc | D | D language compiler |
cc1obj | Objective-C | Objective-C compiler |
cc1objplus | Objective-C++ | Objective-C++ compiler |
2.2 Supported Language Standards
Each language front end supports multiple versions of language standards, specified through the -std= option:
| Language | Supported Standards |
|---|---|
| C | C89, C99, C11, C17, C23 |
| C++ | C++98, C++11, C++14, C++17, C++20, C++23 |
| Fortran | F77, F90, F95, F2003, F2008, F2018 |
| Ada | Ada 83, Ada 95, Ada 2005, Ada 2012 |
| Go | Go 1 |
| D | D 2.0 |
Chapter 3: Supported Platforms
GCC is renowned for its extensive cross-platform support. Through its front-end/back-end separation design, adapting to a new architecture only requires replacing the back end.
3.1 Processor Architectures
- x86 / x86_64: Intel and AMD mainstream desktop/server processors
- ARM (32/64-bit): Dominant architecture in mobile devices and embedded fields
- AArch64: ARM 64-bit architecture
- RISC-V: Open-source instruction set architecture, emerging ecosystem
- MIPS: Historically significant, widely used in embedded and networking equipment
- PowerPC: IBM architecture, used in servers and embedded fields
- SPARC: Sun/Oracle server architecture
- s390x: IBM mainframe architecture
- LoongArch: Domestic independent instruction set architecture
3.2 Operating Systems
- GNU/Linux: Primary target platform, supports all major distributions
- Windows: Runs via MinGW or Cygwin environments
- macOS: Native compilation on Apple platforms
- FreeBSD / OpenBSD / NetBSD: BSD family systems
- Solaris: Oracle Solaris system
3.3 Embedded and Bare-Metal Development
GCC can be used for bare-metal embedded development, generating firmware code that does not depend on an operating system. By configuring an appropriate --target and linker script, developers can compile programs for OS-less environments such as microcontrollers.
Chapter 4: Core Architecture
GCC adopts the classic front-end–middle-end–back-end three-stage design. This modular separation enables it to support multiple programming languages while generating machine code for multiple target platforms.
4.1 Overall Flow
Source Code
↓
Front End: Lexical Analysis → Syntax Analysis → Semantic Analysis → Generate Abstract Syntax Tree (AST)
↓
GENERIC: Language-independent AST representation
↓
Middle-End Optimization Phase 1
↓
GIMPLE: Three-address code, simplified control flow
↓
GIMPLE SSA: Static Single Assignment form, facilitating data flow analysis
↓
Middle-End Optimization Phase 2 (The vast majority of optimizations are completed here)
↓
RTL (Register Transfer Language): Low-level intermediate representation
↓
Back End: Instruction Selection → Instruction Scheduling → Register Allocation → Assembly Generation
↓
Assembly Code
4.2 Front End
The front end is responsible for processing source code of a specific language, performing lexical analysis, syntax analysis, and semantic analysis. Different languages correspond to different front-end programs:
| Front-End Program | Language |
|---|---|
cc1 | C |
cc1plus | C++ |
f951 | Fortran |
go1 | Go |
The front end parses source code into an AST, then gradually lowers it to GENERIC, and finally outputs GIMPLE for processing by the middle end.
4.3 Middle End
The middle end is the core of GCC’s optimization capabilities. It runs over 200 independent optimization passes on the GIMPLE SSA form, performing language- and architecture-independent transformations, including:
- Constant propagation and folding
- Dead code and unreachable code elimination
- Loop optimizations (unrolling, vectorization, fusion, interchange)
- Function inlining
- Common subexpression elimination
- Tail call optimization
- Value range propagation and branch prediction
4.4 Back End
The back end maps the optimized RTL to instructions for the target machine. This stage handles architecture-dependent tasks:
- Instruction selection: Selecting the optimal instruction sequence from the target instruction set
- Instruction scheduling: Reordering instructions to fully utilize the CPU pipeline
- Register allocation: Mapping virtual registers to physical registers
- Assembly code output
Chapter 5: Fundamental Compilation Concepts
Understanding GCC’s operation requires some basic knowledge of compiler theory. The following concepts will frequently appear in subsequent chapters and are briefly defined here.
5.1 Lexical Analysis
Decomposes the source code character stream into a meaningful sequence of tokens, such as keywords (int, return), identifiers (variable names, function names), literals (42, "hello"), and operators (+, =).
5.2 Syntax Analysis
Organizes the token stream into a tree-like structure—the Abstract Syntax Tree (AST)—according to language grammar rules. The AST preserves the hierarchical structure of the source code.
5.3 Semantic Analysis
Checks the semantic correctness of the program: whether types match, whether variables are declared, whether function call arguments are legal, etc., and annotates the AST as necessary.
5.4 Intermediate Code Generation
Converts the AST into the compiler’s internal Intermediate Representation (IR), gradually lowering it to a form closer to machine instructions.
5.5 Code Optimization
Performs equivalent transformations on the intermediate representation to improve execution efficiency or reduce code size without changing program semantics.
5.6 Target Code Generation
Converts the optimized intermediate representation into assembly language for the target machine or directly generates machine code.
Chapter 6: Intermediate Representation System
GCC internally uses a three-tier progressive Intermediate Representation, which is a hallmark feature of its architecture.
6.1 GENERIC
GENERIC is a language-independent AST representation. Each language front end uniformly converts its language-specific AST into GENERIC, masking language differences. It retains the high-level structure of the source code and serves as a transitional representation.
6.2 GIMPLE
GIMPLE is GCC’s core IR, where the vast majority of middle-end optimizations run.
Three-Address Code: Each GIMPLE statement contains at most three operands, in the form t1 = a + b. Complex expressions are broken down into a series of simple three-address operations.
Control Flow Simplification: High-level control structures like if, while, and for are decomposed into goto jumps and basic blocks.
Viewing GIMPLE: gcc -fdump-tree-gimple main.c outputs GIMPLE in text form, facilitating debugging and analysis of the optimization process.
6.3 GIMPLE SSA (Static Single Assignment Form)
SSA (Static Single Assignment) is the cornerstone of modern compiler optimization. Its core rule is: each variable in the program is assigned exactly once statically in the text. When control flow merges, values from different paths are combined through φ (phi) functions.
Advantages of SSA:
- Def-use chains become direct and explicit
- Greatly simplifies the implementation of optimization algorithms such as dead code elimination, constant propagation, and value range analysis
- Saves memory and computational overhead
GCC first converts GIMPLE into SSA form to run extensive optimizations, then exits SSA form after optimization is complete to enter the RTL phase.
6.4 RTL (Register Transfer Language)
RTL is GCC’s lowest-level intermediate representation, close to machine instructions. It uses S-expressions (Lisp-like style) to describe data transfers and operations from register to register.
The RTL phase is responsible for:
- Instruction selection (mapping operations to target machine instructions)
- Instruction combination (merging adjacent instructions)
- Register allocation
- Instruction scheduling
Viewing RTL: gcc -fdump-rtl-all main.c outputs the results of each RTL pass.
Chapter 7: Basic Blocks and Control Flow Graph
7.1 Basic Block
A basic block is a sequence of instructions executed sequentially, with only one entry (the first instruction) and one exit (the last instruction), with no jump instructions entering or leaving in the middle.
7.2 Control Flow Graph (CFG)
The Control Flow Graph uses basic blocks as nodes and jump relationships as directed edges. The vast majority of GCC’s middle-end optimizations are built upon the CFG, relying on it for loop detection, dominance relationship analysis, and data flow analysis.
Chapter 8: The Pass System
GCC’s optimizations are not performed by a single large function but by a pipeline of over 200 independent Passes run on the IR in a predetermined order.
8.1 GIMPLE Passes
Optimizations running on GIMPLE SSA, performing language- and architecture-independent transformations:
- Function inlining, Inter-Procedural Analysis (IPA)
- Loop unrolling, vectorization, fusion
- Common subexpression elimination
- Dead code elimination
- Alias analysis
- Tail call optimization
8.2 RTL Passes
Optimizations and preparations running on RTL:
- Instruction combination (merging adjacent instructions to exploit complex instructions)
- Register allocation (graph coloring)
- Instruction scheduling (pipeline optimization)
- Peephole optimization (local instruction pattern replacement)
- Delay slot filling (on some architectures)
8.3 Viewing and Debugging Passes
- View pass execution order:
gcc -O2 -fdump-passes main.c - Disable a specific pass individually:
-fdisable-tree-vrp1,-fdisable-rtl-combine - View IR changes before and after a pass:
-fdump-tree-all,-fdump-rtl-all
Chapter 9: Register Allocation
Register allocation is the process of mapping the infinite virtual registers in the IR to the limited physical registers of the target machine. It is one of the most critical and complex steps in the compiler back end.
9.1 Graph Coloring Method
Models the register allocation problem as a graph coloring problem: build an interference graph, where two variables have an edge if they are simultaneously live. Color the graph using no more colors than the number of physical registers; if coloring is impossible, spilling is required.
9.2 Spilling
When physical registers are insufficient, the values of certain variables are temporarily stored in stack memory and retrieved when needed. The choice of spill points directly impacts code performance.
9.3 GCC’s Implementation
GCC employs improved register allocators:
- IRA (Integrated Register Allocator): Uses priority-based graph coloring allocation
- LRA (Local Register Allocator): More robust in handling complex constraints and spilling
Chapter 10: Instruction Selection and Scheduling
10.1 Instruction Selection
Matches RTL operation patterns to specific machine instructions for the target. GCC uses Machine Description files (.md) to declaratively define instruction templates, generating pattern matching code through automated tools.
10.2 Instruction Scheduling
Reorders instruction execution sequences to fully utilize the CPU’s instruction pipeline, reducing stalls caused by data hazards and structural hazards.
10.3 Peephole Optimization
Slides a small observation window over the generated instruction sequence, identifying inefficient patterns and replacing them with superior equivalent sequences.
Chapter 11: Machine Description Files
The GCC back end is not hardcoded in C; instead, the target architecture is declaratively described through Machine Description files (.md files).
Machine Description Files Contain:
- Instruction Templates: RTL patterns corresponding to each machine instruction
- Expansion Patterns: Optimization rules for merging multiple instructions into one
- Attribute Definitions: Information such as instruction latency, functional unit occupation, and instruction length, used by the scheduler and optimizer
During the GCC build process, these .md files are automatically processed to generate the vast majority of the back-end code. This design significantly reduces the effort required to port GCC to a new architecture.
Chapter 12: Calling Conventions and ABI
Code generated by GCC must adhere to the target platform’s ABI (Application Binary Interface) and calling conventions to ensure correct interoperability between code compiled from different compilation units and different languages.
12.1 Register Usage Conventions
- Parameter passing registers: Specifies which registers are used to pass function arguments
- Caller-saved registers: May be modified across calls; the caller must save them if needed
- Callee-saved registers: The callee must save the original values before use and restore them before returning
12.2 Stack Frame Layout
- Parameter passing order (typically pushed right-to-left)
- Location of the return value (register or stack)
- Stack alignment requirements (e.g., x86_64 requires 16-byte alignment)
12.3 Name Mangling
C++ features such as function overloading, templates, and namespaces require encoding function names into unique symbol names. GCC follows the Itanium C++ ABI specification for name mangling.
12.4 Major Supported ABIs
- System V AMD64 ABI (x86_64 Linux)
- ARM EABI / AArch64 ABI
- Various embedded ABI variants for different architectures
Chapter 13: Debug Information
GCC can embed source-level debugging information into object files for use by debuggers like GDB.
13.1 Supported Standards
- DWARF (Mainstream standard, versions 2/3/4/5): Feature-rich, supports complex types, macro information, etc.
- STABS (Older format, now less commonly used)
- CTF / BTF: Compact debugging formats used in scenarios like BPF
13.2 Information Contained in DWARF
- Mapping of source code line numbers to instruction addresses
- Variable type information and scope
- Stack frame descriptions (for stack unwinding and backtracing)
- Calling convention information
13.3 Usage Options
-g: Generate default format debug information (usually DWARF)-ggdb: Generate debug information including GDB extensions-gdwarf-4: Specify DWARF version-fno-omit-frame-pointer: Preserve the frame pointer, facilitating debugging and profiling
Chapter 14: Exception Handling Mechanisms
GCC supports two exception handling models, used for C++ try/catch and similar mechanisms.
14.1 DWARF Zero-Cost Exception Handling
The normal execution path has absolutely no additional overhead (“zero cost”). When an exception is thrown, the call stack is unwound frame by frame using DWARF Call Frame Information tables, calling destructors and searching for a matching exception handler. This is the default model on modern Linux platforms.
14.2 SjLj (setjmp/longjmp) Exceptions
Implemented based on the C standard library’s setjmp/longjmp. Simple to implement, but incurs bookkeeping overhead even on the normal execution path. Mainly used on some embedded platforms that do not support DWARF unwinding.
14.3 Stack Unwinding
When an exception is thrown, the runtime system traces back up the call stack, executing destructors in each stack frame in turn, until the exception is caught by a catch block or the program terminates.
Chapter 15: Compilation Process
The gcc command is essentially a Compiler Driver that sequentially invokes independent toolchain components to complete four stages:
15.1 Preprocessing
Invokes cpp, processing preprocessing directives like #include, #define, #ifdef, expanding macros and header files.
gcc -E main.c -o main.i
Output: main.i (preprocessed source file)
15.2 Compilation
Invokes the language front end (cc1, etc.) to compile the preprocessed code into assembly.
gcc -S main.c -o main.s
Output: main.s (assembly code)
15.3 Assembly
Invokes as to convert assembly code into an object file.
gcc -c main.c -o main.o
Output: main.o (object file)
15.4 Linking
Invokes collect2 (which wraps the system linker ld) to link multiple object files and libraries into an executable.
gcc main.o -o app
Output: app (executable file)
Chapter 16: Output Formats
GCC can output intermediate products in different formats during various compilation stages:
| Option | Output File | Content |
|---|---|---|
-E | .i / .ii | Preprocessed source code (all macros and headers expanded) |
-S | .s | Assembly code (text format) |
-c | .o | Object file (binary) |
-fdump-tree-gimple | .gimple | GIMPLE intermediate representation |
-fdump-rtl-all | .rtl.* | Various stages of RTL intermediate representation |
Furthermore, the default executable format output by GCC is ELF (Linux), but it can also be configured for PE (Windows), Mach-O (macOS), and other formats.
Chapter 17: Object File Structure
Object files (.o) follow the ELF (Executable and Linkable Format) format (on Linux platforms), internally containing:
| Section | Purpose |
|---|---|
.text | Compiled machine instruction code |
.data | Initialized global and static variables |
.bss | Uninitialized global variables (only records size in the file header, occupies no disk space) |
.rodata | Read-only data (string constants, const global variables) |
| Symbol Table | Records function names, variable names, and their corresponding addresses or offsets |
| Relocation Table | Records address placeholders that need the linker to fix during final linking |
Chapter 18: Static and Dynamic Libraries
GCC supports generating and using code in two shared forms.
18.1 Static Libraries (.a)
Packages multiple object files into a single archive file; required code is copied into the executable during linking.
Creating a static library:
gcc -c math.c -o math.o
ar rcs libmath.a math.o
Using a static library:
gcc main.c -L. -lmath -o app
18.2 Dynamic Libraries (.so)
Code is loaded at runtime; multiple programs can share the same library file, saving memory and disk space.
Creating a dynamic library:
gcc -fPIC -c math.c -o math.o
gcc -shared math.o -o libmath.so
Using a dynamic library:
gcc main.c -L. -lmath -o app
LD_LIBRARY_PATH=. ./app
18.3 Position-Independent Code (PIC) and PLT/GOT
Dynamic libraries must be loaded into different address spaces of different processes, and thus must be compiled as Position-Independent Code, not relying on absolute addresses. GCC generates PIC code via the -fPIC option. During dynamic linking, the PLT (Procedure Linkage Table) and GOT (Global Offset Table) cooperate to implement lazy symbol binding.
Chapter 19: The Linking Process
19.1 Symbol Resolution
The linker matches each symbol reference (function call, global variable access) with its unique definition. Unmatched references result in an undefined reference error.
19.2 Relocation
Address placeholders in object files are replaced with final runtime absolute or relative addresses during linking.
19.3 Dynamic Linking
When using dynamic libraries, the linker does not copy library code into the executable but records the names of the dependent shared libraries. When the program starts, the dynamic linker (ld.so) is responsible for loading the required shared libraries and performing the final symbol binding.
Chapter 20: GCC’s Bootstrap Build
GCC uses a special 3-stage bootstrap method to compile itself:
- Stage 1: Compile GCC source code using the system’s existing compiler to produce an initial GCC
- Stage 2: Recompile its own source code using the GCC compiled in Stage 1 to produce the Stage 2 GCC
- Stage 3: Compile itself again using the Stage 2 GCC and compare the output binary with the Stage 2 output
The 3-stage build ensures the compiler itself is correctly compiled and can also expose potential compiler bugs. The consistency of the two binary comparisons is also a sign of a successful build.
Chapter 21: Runtime Libraries
Executables generated by GCC depend on several core libraries at runtime.
21.1 libgcc
GCC’s lowest-level runtime library, providing functionality not directly implemented by the target CPU instruction set:
- 64-bit integer division and modulo on 32-bit machines
- Floating-point emulation (when no hardware floating-point unit is present)
- Basic support for exception handling and stack unwinding
- Available in a static library version
libgcc.aand shared library versionlibgcc_s.so
21.2 libstdc++
The C++ standard library implementation, automatically linked by g++. Provides all C++ standard facilities including STL containers, algorithms, IO streams, strings, and thread support.
21.3 libc
The standard C library. On Linux, GNU C Library (glibc) is commonly used, with lightweight implementations like musl and uClibc available for embedded scenarios.
Chapter 22: Advanced Features
22.1 Link-Time Optimization (LTO)
Traditional compilation is performed independently per source file, preventing cross-file function inlining. LTO solves this problem.
When LTO is enabled (-flto), the compiler stores the GIMPLE intermediate representation alongside the assembly code in the .o file. During the final linking phase, the linker reads the GIMPLE from all object files, integrates them into a single whole unit, and re-runs optimization passes on a global scale.
Advantages: Enables aggressive optimizations such as cross-file inlining, global dead code elimination, and whole-program constant propagation
Disadvantages: Significantly increases linking time and memory consumption
22.2 GCC Plugins
GCC provides a plugin interface, allowing developers to inject custom logic into the compilation pipeline without modifying the GCC source code. Plugins can hook into various points of the GIMPLE passes or RTL passes.
Typical Applications:
- Custom static analysis rules
- Code security checks
- Automated instrumentation
- Performance profiling tool development
22.3 Runtime Checking (Sanitizers)
Catches various errors at runtime by inserting detection code during compilation:
| Option | Detection Target |
|---|---|
-fsanitize=address | Memory errors (buffer overflow, use-after-free, memory leaks) |
-fsanitize=thread | Data races (race conditions in multi-threaded environments) |
-fsanitize=undefined | Undefined behavior (integer overflow, null pointer dereference, illegal type casts, etc.) |
-fsanitize=leak | Memory leak detection |
-fsanitize=memory | Uninitialized memory reads |
22.4 Cross-Compilation
GCC was designed from the start to support cross-compilation, capable of compiling programs on one platform to run on another.
GCC distinguishes three platform concepts:
- Build platform: The machine running the compiler
- Host platform: The platform the compiler itself runs on
- Target platform: The platform the generated executable runs on
In cross-compilation scenarios (build = host ≠ target), only the correct target back end needs to be configured. GCC enables rapid back-end porting through Machine Description files, making it the industry standard in the embedded development field.
Chapter 23: Supporting Toolchain
23.1 Tools Invoked During Compilation
| Tool | Function |
|---|---|
cpp | C Preprocessor |
cc1 / cc1plus | C/C++ Language Compiler |
as | GNU Assembler |
collect2 | Linker wrapper program, invokes ld |
ld | GNU Linker |
23.2 Binary Utilities (Binutils)
| Tool | Function |
|---|---|
ar | Create static libraries (.a files) |
ranlib | Generate index for static libraries |
nm | List symbols in object files |
objdump | Disassemble object files or executables |
objcopy | Copy and convert object file formats |
strip | Remove symbol table and debug information, reduce size |
readelf | Parse and display detailed ELF file information |
size | Display the sizes of sections in an ELF file |
strings | Extract printable strings from binary files |
Chapter 24: Quick Reference for Common Compilation Options
24.1 Optimization Levels
| Option | Description |
|---|---|
-O0 | No optimization (default), fastest compilation speed |
-O1 | Basic optimization, balances compilation time and effect |
-O2 | Standard optimization level, includes most optimizations that do not increase size |
-O3 | Aggressive optimization, includes loop unrolling, function inlining, etc. |
-Os | Optimize for code size |
-Ofast | Extreme optimization, disregards strict standard compliance (may alter floating-point semantics) |
24.2 Debugging and Diagnostics
| Option | Description |
|---|---|
-g | Generate debug information |
-ggdb | Generate GDB-specific debug information |
-Wall | Enable most common warnings |
-Wextra | Enable extra warnings |
-Werror | Treat all warnings as errors |
-Wpedantic | Strictly follow the language standard, issue standard compliance warnings |
-fdiagnostics-color=always | Colored diagnostic output |
-fno-omit-frame-pointer | Preserve frame pointer |
24.3 Language Standards
| Option | Description |
|---|---|
-std=c11 | Use the C11 standard |
-std=c++17 | Use the C++17 standard |
-std=c++20 | Use the C++20 standard |
24.4 Preprocessing
| Option | Description |
|---|---|
-I<dir> | Add header file search path |
-D<macro> | Define a macro |
-D<macro>=<value> | Define a macro with a value |
-U<macro> | Undefine a macro |
24.5 Linking
| Option | Description |
|---|---|
-L<dir> | Add library search path |
-l<lib> | Link specified library (e.g., -lm links the math library) |
-static | Static linking |
-shared | Generate a dynamic library |
-fPIC | Generate Position-Independent Code |
-Wl,<option> | Pass an option to the linker |
-Wl,-rpath=<dir> | Embed runtime library search path into the executable |
24.6 Output Control
| Option | Description |
|---|---|
-o <file> | Specify output file name |
-c | Compile and assemble only, do not link |
-S | Compile only, generate assembly code |
-E | Run only the preprocessor |
Chapter 25: Common Compilation Errors at a Glance
| Error Type | Typical Message | Common Cause |
|---|---|---|
| Preprocessing Error | fatal error: xxx.h: No such file or directory | Header file not found, use -I to specify the path |
| Compilation Error | expected ';' before ... | Syntax error, check the preceding line of code |
| Compilation Warning | implicit declaration of function ... | Forgot to include the corresponding header file |
| Linker Error | undefined reference to 'func' | Function declared but not defined, or missing library/object file |
| Linker Error | multiple definition of ... | The same symbol was defined repeatedly |
Chapter 26: Relationship with Other Compilers
26.1 Clang/LLVM
Clang is a C/C++/Objective-C compiler based on the LLVM back end. Compared to GCC, Clang typically compiles faster, provides friendlier error messages, and offers better support for IDEs and static analysis tools. GCC’s advantages lie in its more mature optimization capabilities, broader architecture support, and irreplaceability in scenarios like Linux kernel compilation.
26.2 MSVC (Microsoft Visual C++)
Microsoft’s C/C++ compiler, the primary compiler for the Windows ecosystem. GCC provides an alternative for the Windows platform through MinGW. The two differ in syntax extensions, ABI, and standard library implementations.
Chapter 27: Integration with Build Systems
GCC is typically not used in isolation; it is embedded within a larger development ecosystem:
- make: The classic build tool, organizing compilation rules via Makefiles
- CMake: A cross-platform meta-build system that can generate Makefiles, Ninja, etc.
- Meson: A modern, fast build system
- Autotools: The traditional build system for GNU projects
- GDB: The GNU Debugger, consumes DWARF debug information generated by GCC
- Valgrind: A runtime memory analysis tool, works with binaries compiled by GCC
- perf / gprof: Performance analysis tools, utilize GCC’s
-pgor-fprofile-arcsoptions to generate data
Chapter 28: Installation
28.1 Installation via Package Manager (using Ubuntu as an example)
sudo apt update
sudo apt install build-essential gdb binutils
build-essential: Includes basic development tools such asgcc,g++,make,libc-devgdb: GNU Debuggerbinutils: Includes binary tools likeld,as,objdump
Verify installation:
gcc --version
g++ --version
gdb --version
ld --version
28.2 Installing a Specific Version
sudo apt install gcc-15 g++-15
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 100
sudo update-alternatives --config gcc # Switch default version
28.3 Compiling and Installing from Source
Suitable for scenarios requiring the latest version or custom build configurations:
# Download source
wget https://ftp.gnu.org/gnu/gcc/gcc-15.1.0/gcc-15.1.0.tar.gz
tar -xzf gcc-15.1.0.tar.gz
cd gcc-15.1.0
# Install build dependencies
./contrib/download_prerequisites
# Configure and build
mkdir build && cd build
../configure --prefix=/usr/local/gcc-15.1.0 \
--enable-languages=c,c++ \
--disable-multilib
make -j$(nproc) # Parallel build, fully utilizes multiple cores
sudo make install
# Add to PATH
echo 'export PATH=/usr/local/gcc-15.1.0/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
Chapter 29: Documentation and Help
29.1 Built-in Help
gcc --help # View overview of all options
gcc --help=optimizers # Optimization options
gcc --help=warnings # Warning options
gcc --help=target # Target platform-related options
gcc --help=common # Common options
29.2 Checking Currently Enabled Options
gcc -Q -O2 --help=optimizers # View optimizations enabled at O2 level
gcc -Q -O3 --help=optimizers # Compare with O3
gcc -Q --help=warnings | grep enabled # View default enabled warnings
29.3 Man Pages and Online Documentation
man gcc # Detailed man page
info gcc # GNU Info documentation
Online documentation: https://gcc.gnu.org/onlinedocs
Chapter 30: License and Community
30.1 License
GCC itself uses the GNU General Public License version 3 (GPLv3+). Additionally, GCC’s runtime libraries (libgcc, libstdc++, etc.) come with the GCC Runtime Library Exception, allowing compiled non-GPL programs to link against these libraries without the GPL propagating.
30.2 Contributing
GCC’s development relies on an open community:
- Mailing List: gcc@gcc.gnu.org is the primary discussion channel
- Bug Tracking: Report and track issues via GCC Bugzilla
- Code Repository: Git repository hosted at git://gcc.gnu.org/git/gcc.git
- Coding Standards: Follow the GCC Coding Conventions (primarily GNU style for C)
- Copyright Assignment: Submitting code usually requires signing an FSF copyright assignment
Appendix: Reference Resources
- GCC Official Website: https://gcc.gnu.org
- Online Documentation: https://gcc.gnu.org/onlinedocs
- Wiki: https://gcc.gnu.org/wiki
- Mailing List Archive: https://gcc.gnu.org/ml/gcc/
- Bugzilla: https://gcc.gnu.org/bugzilla