第2章 GCC编译选项详解
完整 485 页的 《编译与调试完全指南》见: https://github.com/lengjingzju/notes/releases/tag/v0.0.2
GCC(GNU Compiler Collection)作为 Linux 生态的核心编译器,提供了极为丰富的编译链接选项。合理配置这些选项,是保障代码质量、安全性和性能的关键工程手段。本章按照构建流程的逻辑,从基础选项到高级特性,系统性地介绍 GCC 的各类编译选项,涵盖基础编译控制、文件查找、依赖生成、链接、警告、优化、架构、安全增强、动态检测、静态分析和性能分析等方面。
2.1 基础编译选项
基础编译选项是日常使用 GCC 时最频繁接触的部分,包括宏定义、输出控制和编译阶段控制三大类。
2.1.1 宏定义选项
-D 选项用于在编译时定义预处理宏,等价于在源代码中编写 #define 指令。
| 选项 | 说明 | 等价代码 |
|---|---|---|
-Dname | 定义宏 name,默认值为 1 | #define name 1 |
-Dname=value | 定义宏 name,值为 value | #define name value |
-Dname=\"value\" | 定义宏 name,值为字符串 value | #define name "value" |
实践示例:
# 定义数值宏,等价于 #define MAX_NUM 1024
gcc -DMAX_NUM=1024 test.c -o test
# 定义字符串宏,等价于 #define AUTHOR "LengJing"
gcc -DAUTHOR=\"LengJing\" test.c -o test
# 组合使用多个宏定义
gcc -DMAX_NUM=1024 -DAUTHOR=\"LengJing\" -DDEBUG test.c -o test
易错点:
- 字符串宏的引号需要转义:
-DAUTHOR=\"LengJing\"而非-DAUTHOR="LengJing"(后者在 shell 中引号会被吃掉) - 在 Makefile 中使用时,通常写成
CFLAGS += -DAUTHOR='"LengJing"'
2.1.2 输出控制选项
| 选项 | 说明 | 典型用途 |
|---|---|---|
-o output | 指定编译生成的文件名为 output | 所有编译场景 |
-v | 显示详细编译过程(包括调用的子命令、搜索路径等) | 排查编译问题、了解工具链内部行为 |
实践示例:
# 指定输出文件名
gcc test.c -o myprogram
# 查看详细编译过程(可以看到实际调用了哪些工具、搜索了哪些路径)
gcc -v test.c -o test
-v 选项的输出包含以下关键信息:
- 编译器搜索头文件的路径顺序
- 链接器搜索库文件的路径顺序
- 实际调用的
cc1、as、ld等子命令及其参数 - 链接的启动文件(crt1.o、crti.o、crtn.o)
2.1.3 编译阶段控制
| 选项 | 说明 | 输出 |
|---|---|---|
-E | 仅执行预处理,不生成文件,输出到终端 | 预处理后的 C 代码(.i) |
-S | 编译到汇编语言,不进行汇编和链接 | 汇编代码(.s) |
-c | 仅编译源文件,生成目标文件,不链接 | 目标文件(.o) |
实践示例:
# 仅预处理(查看宏展开、头文件包含结果)
gcc -E test.c -o test.i
# 仅编译到汇编(查看生成的汇编代码)
gcc -S test.c -o test.s
# 仅编译到目标文件(不链接)
gcc -c test.c -o test.o
关于编译四个阶段的详细说明,参见第 1 章 1.2 节。
2.2 文件查找选项
文件查找选项控制 GCC 在编译和链接过程中搜索头文件和库文件的路径。正确配置这些选项是解决"找不到头文件"和"找不到库"问题的关键。
2.2.1 头文件查找
| 选项 | 说明 | 示例 |
|---|---|---|
-I<directory> | 添加头文件搜索路径(最常用) | -I./include -I/usr/local/include |
-isystem <directory> | 添加系统头文件目录,抑制该目录中头文件的警告 | -isystem /usr/include |
-idirafter <directory> | 在所有 -I 目录之后搜索(不常用) | -idirafter /opt/local/include |
-nostdinc | 不搜索标准系统头文件目录(交叉编译、自定义 C 库) | 需配合完整的头文件路径 |
搜索顺序:
-I 与 -isystem 的区别:
| 对比项 | -I | -isystem |
|---|---|---|
| 警告处理 | 该目录中头文件产生的警告会被报告 | 该目录中头文件产生的警告会被抑制 |
| 典型用途 | 项目自身的头文件 | 第三方库头文件、系统头文件 |
| 搜索优先级 | 高于系统目录 | 低于 -I,高于 -idirafter |
实践示例:
# 基本用法
gcc -I./include -I/usr/local/include -c main.c -o main.o
# 第三方库头文件使用 -isystem 抑制警告
gcc -isystem /opt/thirdparty/include -c main.c -o main.o
2.2.2 库文件查找
| 选项 | 说明 | 示例 |
|---|---|---|
-L<directory> | 添加库文件搜索路径 | -L./lib -L/usr/local/lib |
-Wl,-rpath-link=<directory> | 链接时指定运行时库搜索路径(不写入二进制) | -Wl,-rpath-link=./lib |
-Wl,-rpath=<directory> | 将目录嵌入可执行文件作为运行时搜索路径 | -Wl,-rpath,'$$ORIGIN/lib' |
三者区别对比表:
| 特性 | -L | -Wl,-rpath-link | -Wl,-rpath |
|---|---|---|---|
| 作用阶段 | 链接时 | 链接时 | 运行时 |
| 目的 | 找到要链接的库 | 找到依赖库以解析符号 | 告诉动态链接器去哪找库 |
| 是否写入二进制文件 | 否 | 否 | 是(嵌入到 ELF 中) |
| 影响对象 | 直接链接的库(-l 指定) | 间接依赖的库 | 所有运行时需要的库 |
| 搜索顺序 | 在标准库目录之前 | 用于依赖解析 | 在 LD_LIBRARY_PATH 之后,系统目录之前 |
| 可传递性 | 仅影响当前链接 | 仅影响当前链接 | 影响程序和所有使用者 |
| 常用场景 | 链接非标准位置的库 | 链接复杂依赖的库 | 发布自包含的程序 |
特殊变量 $$ORIGIN:
$$ORIGIN 表示可执行文件所在目录,用于构建相对路径的运行时库搜索:
# 将库放在可执行文件同目录的 lib/ 子目录下
gcc -o program main.o -Wl,-rpath,'$$ORIGIN/lib' -L./lib -lfoo
2.2.3 交叉编译路径
| 选项 | 说明 | 示例 |
|---|---|---|
-B <prefix> | 指定编译器工具路径前缀 | -B /usr/local/bin/ |
--sysroot=<directory> | 指定系统根目录(交叉编译时使用) | --sysroot=/mnt/arm-sysroot |
--sysroot 影响范围:
- 头文件搜索:在该目录下搜索
/usr/include等 - 库文件搜索:在该目录下搜索
/usr/lib等 - 动态链接器:在该目录下搜索
/lib/ld-linux.so
实践示例:
# 交叉编译时指定目标系统根目录
aarch64-linux-gnu-gcc --sysroot=/mnt/arm-sysroot -c main.c -o main.o
# -B 影响编译器前端和后端的工具查找
gcc -B /opt/toolchain/bin/ -c main.c -o main.o
2.2.4 灵活的查找配置
在构建系统中,通常通过 Makefile 变量动态构建搜索路径:
# 基础模式
CPFLAGS += $(addprefix -I,$(addprefix $(DEP_PREFIX),/include /usr/include))
# 条件搜索模式
ifdef SEARCH_HDRS
CPFLAGS += $(addprefix -I,$(wildcard \
$(addprefix $(DEP_PREFIX)/include/,$(SEARCH_HDRS)) \
$(addprefix $(DEP_PREFIX)/usr/include/,$(SEARCH_HDRS)) \
))
endif
# 库路径配置
LDFLAGS += $(addprefix -L,$(addprefix $(DEP_PREFIX),/lib /usr/lib))
LDFLAGS += $(addprefix -Wl$(comma)-rpath-link=,$(addprefix $(DEP_PREFIX),/lib /usr/lib))
其中 $(comma) 在 Makefile 中表示逗号,因为 Makefile 中逗号有特殊含义。
2.3 自动依赖生成
自动依赖生成是构建系统的核心功能之一,它分析 #include 指令,生成目标文件与头文件的依赖关系,确保头文件修改后能自动触发重新编译。
2.3.1 依赖生成选项
| 选项 | 说明 | 是否同时编译 |
|---|---|---|
-M | 输出所有依赖关系(包括系统头文件) | 否,仅输出依赖 |
-MM | 输出依赖关系(排除系统头文件) | 否,仅输出依赖 |
-MD | 编译时同时生成依赖关系文件(包括系统头文件) | 是 |
-MMD | 编译时同时生成依赖关系文件(排除系统头文件) | 是 |
-M 与 -MM 的区别:
# -M 输出包含系统头文件(如 /usr/include/stdio.h)
$ gcc -M test.c
test.o: test.c /usr/include/stdio.h /usr/include/features.h ...
# -MM 排除系统头文件(通常这是期望的行为)
$ gcc -MM test.c
test.o: test.c myheader.h
-MD 与 -MMD 的区别:
-MD/-MMD会同时进行编译和依赖生成,效率更高- 默认生成
.d文件(与.o文件同名) - 实际工程中推荐使用
-MMD(排除系统头文件,避免系统头文件变化触发不必要的重编译)
2.3.2 依赖输出控制
| 选项 | 说明 | 典型用途 |
|---|---|---|
-MT <target> | 指定依赖规则中的目标名称 | 确保目标名称与实际的 .o 文件路径匹配 |
-MQ <target> | 类似 -MT,但对特殊字符进行引用转义 | 目标名称包含 $、空格等特殊字符时 |
-MF <file> | 将依赖关系输出到指定文件 | 指定 .d 文件的输出路径 |
-MP | 为每个依赖的头文件生成伪目标规则 | 防止头文件删除后 make 报错 |
-MT 的作用详解:
默认情况下,-M 或 -MM 生成的目标是源文件对应的 .o 文件名(如 source.o: ...)。使用 -MT 可以指定不同的目标名称,在复杂的构建系统中特别有用:
# 默认行为:目标名为 source.o
$ gcc -MM source.c
source.o: source.c header.h
# 使用 -MT 指定不同的目标名
$ gcc -MM -MT build/output/source.o source.c
build/output/source.o: source.c header.h
-MP 的作用详解:
为每个头文件生成 <header>: 的空规则。当头文件被删除时,避免 make 报错"没有规则创建目标":
# 没有 -MP 时,如果 header.h 被删除,make 会报错:
# "No rule to make target 'header.h'"
# 有 -MP 时,生成的 .d 文件包含:
# header.h:
# 即使 header.h 不存在,make 也不会报错
-MQ 与 -MT 的区别:
# -MT 不做转义
gcc -MM -MT '$@' test.c
# 输出:$@: test.c
# -MQ 对特殊字符进行转义
gcc -MM -MQ '$@' test.c
# 输出:$$@: test.c
2.3.3 依赖文件工作原理与 Makefile 集成
完整的依赖生成规则:
# 自动生成依赖文件
$(OBJ_PREFIX)%.o: %.c
$(CC) $(CFLAGS) $(CPFLAGS) -MMD -MP -MT $$@ -MF $$(patsubst %.o,%.d,$$@) -c $$< -o $$@
# 包含依赖文件(- 前缀表示文件不存在时不报错)
-include $(DEPS)
生成的 .d 文件示例:
# build/main.d 文件内容
build/main.o: main.c myop.h /usr/include/stdio.h
myop.h:
/usr/include/stdio.h:
2.4 链接选项
链接选项控制 GCC 在链接阶段的行为,包括库的链接方式、搜索顺序、符号解析策略等。
2.4.1 基础库链接
| 选项 | 说明 | 示例 |
|---|---|---|
-l<library> | 链接名为 lib<library>.so 或 lib<library>.a 的库 | -lpthread -lm -ldl |
-static | 完全静态链接,生成无运行时依赖的可执行文件 | 增大文件体积,但部署简单 |
-shared | 创建共享库(动态库) | -shared -Wl,-soname,libfoo.so.1 |
-fPIC | 生成位置无关代码(用于共享库) | 必须用于共享库中的所有目标文件 |
链接搜索顺序:
-L指定的目录(按命令行顺序)- 环境变量
LIBRARY_PATH - 默认系统库目录(
/lib、/usr/lib等)
实践示例:
# 链接数学库和线程库
gcc -o program main.o -lm -lpthread
# 完全静态链接
gcc -static -o program main.o -lm
# 创建共享库
gcc -shared -fPIC -o libfoo.so foo.o -Wl,-soname,libfoo.so.1
2.4.2 混合静态/动态链接
| 选项 | 说明 |
|---|---|
-Wl,-Bstatic | 后续的 -l 库使用静态链接 |
-Wl,-Bdynamic | 后续的 -l 库使用动态链接(默认) |
实践示例:
# libfoo.a 和 libbar.a 以静态方式链接,libbaz.so 以动态方式链接
gcc -o program main.o -Wl,-Bstatic -lfoo -lbar -Wl,-Bdynamic -lbaz
# Makefile 中的组合模式
LDFLAGS += -Wl,-Bstatic $(addprefix -l,$(STATIC_LIBS)) -Wl,-Bdynamic $(addprefix -l,$(DYNAMIC_LIBS))
2.4.3 循环依赖处理
| 选项 | 说明 |
|---|---|
-Wl,--start-group | 开始一个库组 |
-Wl,--end-group | 结束一个库组 |
实践示例:
# 处理 liba、libb、libc 之间的循环依赖
gcc -o program main.o -Wl,--start-group -la -lb -lc -Wl,--end-group
工作原理: 链接器会反复搜索组内的库,直到解析所有符号。代价是链接时间增加,因此仅在确实存在循环依赖时使用。
2.4.4 按需链接与符号解析
| 选项 | 说明 | 推荐 |
|---|---|---|
-Wl,--as-needed | 只链接实际使用的库 | ✓ 推荐 |
-Wl,--no-as-needed | 链接所有指定的库(默认行为) | |
-Wl,--no-undefined | 将所有未解析的符号视为链接错误 | 确保共享库完整性 |
-Wl,-z,defs | 要求所有符号有定义(类似 --no-undefined) |
实践示例:
# 推荐:只链接实际使用的库,减少不必要的依赖
gcc -o program main.o -Wl,--as-needed -lfoo -lbar -lbaz
# 确保共享库没有未定义符号
gcc -shared -o libfoo.so foo.o -Wl,--no-undefined
2.4.5 全量链接
| 选项 | 说明 |
|---|---|
-Wl,--whole-archive | 强制包含静态库中的所有目标文件 |
-Wl,--no-whole-archive | 恢复正常链接行为 |
实践示例:
# 强制链接 libfoo.a 中的所有 .o 文件(即使某些符号未被引用)
gcc -o program main.o -Wl,--whole-archive -lfoo -Wl,--no-whole-archive
典型应用场景:
- 注册表模式: 库中通过构造函数(
__attribute__((constructor)))注册回调 - 插件系统: 需要加载库中所有模块
- 单元测试: 需要链接所有测试用例
2.4.6 特定库链接
| 选项 | 说明 |
|---|---|
-static-libgcc | 静态链接 GCC 运行时库 |
-static-libstdc++ | 静态链接 C++ 标准库 |
-l:filename | 直接指定库文件名(不添加 lib 前缀和后缀) |
-fuse-ld=bfd | 使用 bfd 链接器 |
-fuse-ld=gold | 使用 gold 链接器 |
-fuse-ld=lld | 使用 LLVM lld 链接器 |
-fuse-ld=mold | 使用 mold 链接器 |
实践示例:
# 静态链接 C++ 标准库(避免目标机器缺少 libstdc++)
g++ -o program main.o -static-libstdc++
# 直接指定库文件名
gcc -o program main.o -l:libfoo.so.1.2.3
# 使用更快的链接器
gcc -o program main.o -fuse-ld=mold
2.5 警告选项
警告选项是代码质量的前置保障。合理配置警告选项,可以在编码阶段提前发现潜在问题。本节从基础警告到具体分类警告,再到警告的精细控制,系统性地介绍 GCC 的警告体系。
2.5.1 基础警告
| 选项 | 说明 | 建议 |
|---|---|---|
-Wall | 启用一组最常用、最有用的警告 | 始终启用 |
-Wextra | 启用 -Wall 未包含的额外警告(以前称为 -W) | 推荐启用 |
-Werror | 将所有警告升级为编译错误 | CI/CD 中推荐 |
-w | 关闭所有警告 | 不推荐,风险大 |
-Wfatal-errors | 遇到第一个错误时中止编译 | 加速编译失败反馈 |
-fmax-errors=n | 限制最大错误数量为 n | 避免错误信息过多 |
-Werror 的精细控制:
# 将特定警告视为错误
-Werror=return-type
-Werror=implicit-function-declaration
# 将特定警告不作为错误(即使有 -Werror)
-Wno-error=unused-variable
# 组合使用:所有警告视为错误,但未使用变量除外
-Werror -Wno-error=unused-variable
2.5.2 常用具体警告
| 选项 | 说明 | 检测的问题 |
|---|---|---|
-Wshadow | 局部变量遮盖外层作用域的同名变量时警告 | 可能导致逻辑错误 |
-Wunused | 检测未使用的变量、函数、标签等 | 代码冗余 |
-Wunused-variable | 未使用的变量 | 代码冗余 |
-Wunused-function | 未使用的函数 | 代码冗余 |
-Wunused-parameter | 未使用的函数参数 | 接口设计问题 |
-Wmissing-braces | 初始化数组或结构体时缺失大括号警告 | 初始化错误 |
-Wreturn-type | 函数缺少返回类型或默认返回 int 警告 | 类型安全 |
-Wpointer-arith | 对函数指针或 void 指针进行算术运算时警告 | 未定义行为 |
-Waddress | 对总是真或假的地址表达式警告 | 逻辑错误 |
2.5.3 严格标准符合性
| 选项 | 说明 |
|---|---|
-Wpedantic | 严格遵循 ISO C/C++ 标准,对 GNU 扩展发出警告 |
-pedantic-errors | 将 pedantic 警告视为错误 |
-Wconversion | 可能改变值的隐式类型转换警告 |
-Wsign-conversion | 有符号和无符号整数之间转换警告 |
-Wfloat-conversion | 浮点类型之间的隐式转换警告 |
2.5.4 严格内存与边界检查
| 选项 | 说明 | 单位 |
|---|---|---|
-Wlarger-than=<size> | 定义超过指定大小的对象时警告 | 字节 |
-Wframe-larger-than=<size> | 函数栈帧超过指定大小时警告 | 字节 |
-Warray-bounds | 检测编译时可确定的数组越界访问(默认级别 1) | - |
-Warray-bounds=2 | 更严格的数组边界检查 | - |
-Wstringop-overflow | 检测字符串操作可能导致的缓冲区溢出(默认级别 2) | - |
-Wstringop-overflow=4 | 最严格的字符串操作检查(可能误报) | - |
-Wstrict-aliasing | 检测违反严格别名规则的内存访问 | - |
2.5.5 严格函数声明检查
| 选项 | 说明 | 适用语言 |
|---|---|---|
-Wstrict-prototypes | 要求函数声明有完整的原型 | C |
-Wmissing-prototypes | 函数定义前没有原型声明时警告 | C |
-Wold-style-definition | 使用旧式 K&R 函数定义时警告 | C |
示例:
// -Wstrict-prototypes 会警告以下写法
int func(); // 警告:应写为 int func(void);
// -Wmissing-prototypes 会警告没有提前声明的函数定义
int helper(int x) { return x * 2; } // 警告:缺少原型声明
2.5.6 堆栈溢出检查选项
堆栈溢出是嵌入式和高可靠性程序中的常见问题。GCC 提供了一组编译时警告选项,用于检测可能导致栈溢出的代码模式。这些选项在嵌入式开发、内核开发和实时系统中尤为重要。
| 选项 | 说明 | 默认值 |
|---|---|---|
-Wstack-usage=<byte-size> | 函数栈使用量可能超过指定值时警告 | PTRDIFF_MAX(默认不触发) |
-Walloc-size-larger-than=<byte-size> | 分配函数请求的内存超过指定值时警告 | PTRDIFF_MAX |
-Walloca-larger-than=<byte-size> | alloca 调用参数超过指定值或无上界时警告 | PTRDIFF_MAX |
-Wvla-larger-than=<byte-size> | 变长数组(VLA)大小超过指定值时警告 | PTRDIFF_MAX |
-Wformat-overflow=<level> | 检测 sprintf 等函数可能的缓冲区溢出 | 级别 1(由 -Wformat 启用) |
-Wstringop-overflow=<level> | 检测字符串/内存操作函数的缓冲区溢出 | 级别 2(默认启用) |
-Wstack-usage 详解:
-Wstack-usage 检查函数的栈使用量(包括 alloca、VLA 等动态分配),是保守估计。警告信息格式根据栈使用量的确定性分为三种:
| 栈使用量类型 | 警告信息格式 | 说明 |
|---|---|---|
| 完全静态且超标 | warning: stack usage is 1120 bytes | 栈使用量确定 |
| 部分动态但有上界 | warning: stack usage might be 1648 bytes | 有界但不确定 |
| 部分动态且无上界 | warning: stack usage might be unbounded | 最危险,无法确定 |
实践示例:
# 限制每个函数栈使用不超过 1024 字节(嵌入式常用)
gcc -Wstack-usage=1024 -c main.c -o main.o
# 限制 alloca 调用不超过 500 字节
gcc -Walloca-larger-than=500 -c main.c -o main.o
# 限制 VLA 不超过 4096 字节
gcc -Wvla-larger-than=4096 -c main.c -o main.o
# 限制分配函数请求不超过 1MB
gcc -Walloc-size-larger-than=1048576 -c main.c -o main.o
-Walloca-larger-than 示例:
void func(size_t n) {
void *p;
if (n <= 1000)
p = alloca(n); // -Walloca-larger-than=1000 不警告(有界)
else
p = malloc(n);
// ...
}
void func_unbounded(size_t n) {
void *p = alloca(n); // -Walloca-larger-than=500 警告(无上界)
// ...
}
-Wformat-overflow 等级详解:
| 等级 | 说明 | 启发式策略 |
|---|---|---|
| 1 | 保守模式,仅警告最可能溢出的调用 | 未知数值参数假设为 1,未知字符串假设为空 |
| 2 | 激进模式,警告可能溢出的调用 | 未知有符号数值假设为 INT_MIN,未知字符串假设为 1 字符 |
void f(int a, int b) {
char buf[13];
sprintf(buf, "a = %i, b = %i\n", a, b);
// 级别 1:即使 a=b=0,NUL 终止符也会越界 → 警告
// 级别 2:若 a=INT_MIN,输出更长 → 警告(需至少 34 字节)
}
-Wstringop-overflow 等级详解:
| 等级 | Object Size 检查类型 | 说明 |
|---|---|---|
| 1 | type-0 | 仅检查最大外层对象边界,等价于 _FORTIFY_SOURCE 的编译时行为 |
| 2 | type-1 | 检查已知大小的最大完整对象(默认) |
| 3 | type-2 | 检查最小对象或数据成员(最严格,可能误报) |
| 4 | type-3 | 检查任何数据成员,多对象时取最大者(可能误报) |
2.5.7 格式化字符串检查
| 选项 | 说明 |
|---|---|
-Wformat | 检查 printf/scanf 等函数的格式字符串与参数类型是否匹配 |
-Wformat=2 | 启用 -Wformat 加上 -Wformat-nonliteral、-Wformat-security、-Wformat-y2k |
-Wformat-nonliteral | 格式字符串不是字面量时警告(无法编译时检查) |
-Wformat-security | 检测可能的安全问题的格式化字符串(如 printf(user_input)) |
-Wformat-y2k | 检测可能产生 2000 年问题的 strftime 格式 |
-Wformat-truncation | 检测 snprintf 等函数输出可能被截断 |
-Wformat-overflow | 检测 sprintf 等函数可能的缓冲区溢出 |
-Wno-format-extra-args | 忽略格式字符串参数过多的警告 |
示例:
// -Wformat 会警告以下写法
int x = 42;
printf("%s\n", x); // 警告:格式 %s 需要 char*,但传入了 int
// -Wformat-security 会警告以下写法
char *user_input = get_input();
printf(user_input); // 警告:格式字符串不是字面量(安全风险)
// -Wformat-truncation 会警告以下写法
char buf[10];
snprintf(buf, sizeof(buf), "hello %s", long_string); // 警告:输出可能被截断
2.5.8 取消与分级控制警告
禁用特定警告:
-Wno-unused-variable
-Wno-deprecated-declarations
-Wno-format-truncation
-Wno-stringop-truncation
警告优先级规则:
- 更具体的选项优先于更一般的选项(如
-Wno-unused-variable优先于-Wunused) - 相同优先级时,最后一个选项生效
- 通过 pragma 启用的选项相当于出现在命令行末尾
2.5.9 代码内禁用警告
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// 被忽略警告的代码
deprecated_function();
#pragma GCC diagnostic pop
使用要点:
push和pop必须配对使用- 作用范围仅限于
push和pop之间的代码 - 适用于无法修改的第三方代码产生的警告
2.6 优化等级选项
优化选项控制编译器对生成代码的优化程度。选择合适的优化级别需要在性能、代码大小、编译时间和可调试性之间取得平衡。
2.6.1 标准优化级别
| 级别 | 说明 | 适用场景 |
|---|---|---|
-O0 | 无优化(默认) | 调试阶段,保证完全可调试性 |
-O1 / -O | 基本优化 | 平衡编译时间和代码质量 |
-O2 | 高级优化(推荐) | 生产环境 |
-O3 | 最大优化 | 计算密集型应用 |
-Os | 优化代码大小 | 嵌入式系统 |
-Og | 调试友好优化 | 调试时仍需要一定性能 |
-Ofast | 快速激进优化 | 忽视严格标准符合性的场景 |
各级别详细说明:
-O0:禁用所有优化,最快编译速度,保留所有调试信息,不重新排列代码-O1:进行基本的、安全的优化(删除未使用代码、简化表达式等),不影响调试-O2:进行更多优化(指令调度、循环优化、函数内联等),平衡性能与代码大小-O3:激进优化(自动向量化、更积极的内联等),可能增加代码大小和编译时间-Os:在-O2基础上禁用可能增加代码大小的优化,最小化二进制大小-Og:在保持可调试性的前提下进行优化,不影响调试体验-Ofast:-O3加上不严格遵守标准的优化(包含-ffast-math),可能违反 IEEE 或 ISO 标准
2.6.2 优化级别对比表
| 级别 | 编译速度 | 代码大小 | 调试友好度 | 性能 | 适用场景 |
|---|---|---|---|---|---|
-O0 | 最快 | 最大 | 最好 | 最差 | 开发调试 |
-O1 | 快 | 较大 | 好 | 较好 | 快速原型 |
-O2 | 中等 | 中等 | 一般 | 好 | 生产环境 |
-O3 | 慢 | 较大 | 差 | 可能最好 | 计算密集 |
-Os | 中等 | 最小 | 一般 | 中等 | 嵌入式 |
-Og | 中等 | 中等 | 好 | 中等 | 调试+优化 |
-Ofast | 慢 | 较大 | 差 | 激进 | 特殊场景 |
2.6.3 调试信息控制
| 选项 | 说明 |
|---|---|
-g | 生成调试信息(操作系统原生格式),等价于 -g2 |
-g0 | 不生成调试信息 |
-g1 | 最小调试信息,仅回溯栈 |
-g2 / -g | 默认级别,包含符号表、行号 |
-g3 | 包含宏定义等额外信息 |
-ggdb | 生成 GDB 专用格式的调试信息 |
-ggdb0 ~ -ggdb3 | GDB 调试信息级别 |
-gdwarf | 生成 DWARF 格式调试信息 |
-gdwarf-version | 指定 DWARF 版本(2/3/4/5) |
调试信息与优化的兼容性:
- GCC 允许
-g与-O一起使用 - 优化后的代码可能使调试行为出人意料(变量被优化掉、代码重排等)
- 考虑使用
-Og配合-g获得更好的调试体验 - 通常调试时使用
-O0 -g -ggdb
2.6.4 节区优化
| 选项 | 说明 |
|---|---|
-ffunction-sections | 将每个函数放在独立的节区 |
-fdata-sections | 将每个数据对象放在独立的节区 |
-Wl,--gc-sections | 删除未使用的节区,减小二进制大小 |
使用示例:
gcc -ffunction-sections -fdata-sections -Wl,--gc-sections -o program program.c
工作原理: 配合使用时,链接器可以识别并删除未被引用的函数和数据,显著减小嵌入式程序的二进制大小。
注意: 调试版本通常不启用节区优化,因为这会影响调试体验:
ifneq ($(ENV_BUILD_TYPE),debug)
CPFLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,--gc-sections
else
LDFLAGS += -Wl,-O1
endif
2.6.5 链接器优化
| 选项 | 说明 |
|---|---|
-Wl,-O<level> | 控制链接器的优化级别(0/1/2) |
级别说明:-O0(无优化)、-O1(基本优化)、-O2(默认)。
2.7 架构与指令集优化
架构与指令集选项控制 GCC 生成代码的目标平台和可利用的指令集。在交叉编译和性能优化场景中尤为重要。
2.7.1 目标架构指定
| 选项 | 说明 | 示例 |
|---|---|---|
-march=<arch> | 指定目标 CPU 架构(决定可以使用哪些指令集) | -march=x86-64-v3、-march=native |
-mtune=<cpu> | 针对特定 CPU 进行性能调优(决定如何调度和优化代码) | -mtune=generic |
-mcpu=<cpu> | 指定 CPU 型号(ARM 架构,同时设置架构和调优) | -mcpu=cortex-a72 |
-march 与 -mtune 的区别:
| 对比项 | -march | -mtune |
|---|---|---|
| 作用 | 决定可以使用哪些指令集 | 决定如何调度和优化代码 |
| 影响 | 生成的代码可能无法在旧 CPU 上运行 | 不影响指令集选择 |
| 典型值 | x86-64、armv8-a、native | generic、znver3、cortex-a72 |
2.7.2 字长与 ABI
| 选项 | 说明 | 适用架构 |
|---|---|---|
-m32 | 生成 32 位代码 | x86-64 |
-m64 | 生成 64 位代码 | x86-64 |
-mabi=<abi> | 指定应用程序二进制接口 | 多种架构 |
-mfloat-abi=<abi> | 指定 ARM 浮点 ABI | ARM |
-mlong-double-128 | 使用 128 位表示 long double | x86-64 |
ARM 浮点 ABI 选项对比:
| 选项 | 说明 | 性能 | 兼容性 |
|---|---|---|---|
soft | 软件模拟浮点运算 | 最慢 | 最好(无需 FPU) |
softfp | 硬件浮点指令,使用软浮点 ABI | 中等 | 与 soft 兼容 |
hard | 硬件浮点指令,使用硬浮点 ABI | 最快 | 需要 FPU |
2.7.3 x86 SIMD 指令集
| 选项 | 说明 | 向量宽度 |
|---|---|---|
-msse、-msse2、-msse3 | 启用 SSE/SSE2/SSE3 指令集 | 128 位 |
-mssse3、-msse4、-msse4.1、-msse4.2 | 启用 SSSE3/SSE4 指令集 | 128 位 |
-mavx、-mavx2 | 启用 AVX/AVX2 指令集 | 256 位 |
-mavx512f、-mavx512bw | 启用 AVX-512 指令集 | 512 位 |
配套宏定义:
-DUSING_SSE128 // 使用 SSE 128 位向量
-DUSING_AVX256 // 使用 AVX 256 位向量
-DUSING_AVX512 // 使用 AVX-512
2.7.4 ARM 浮点与 NEON
| 选项 | 说明 |
|---|---|
-mfpu=<fpu> | 指定 ARM 浮点单元 |
-mfpu=neon | 启用 NEON SIMD 指令集 |
-DUSING_NEON | 配套宏定义 |
2.8 激进优化选项
激进优化选项提供超出标准优化级别的性能提升手段,但可能带来兼容性或正确性风险,需谨慎使用。
2.8.1 浮点数学优化
| 选项 | 说明 | 风险 |
|---|---|---|
-ffast-math | 启用一系列激进的浮点优化 | 可能违反 IEEE 标准 |
-funsafe-math-optimizations | 允许违反严格标准的数学优化 | 数值精度可能降低 |
-fassociative-math | 允许浮点加法和乘法重新关联 | 可能改变结果 |
-ffinite-math-only | 假设没有 NaN 或无穷大 | 不处理特殊值 |
注意: -ffast-math 包含 -fno-math-errno、-fassociative-math 等多个子选项。仅在确认代码不依赖 IEEE 浮点严格语义时使用。
2.8.2 循环优化
| 选项 | 说明 |
|---|---|
-funroll-loops | 展开循环,减少分支开销 |
-funroll-all-loops | 展开所有循环 |
-ftree-vectorize | 启用自动向量化 |
-floop-nest-optimize | 优化嵌套循环 |
-ftree-loop-distribute-patterns | 将循环转换为库函数调用(如 memcpy) |
2.8.3 链接时优化(LTO)
| 选项 | 说明 |
|---|---|
-flto | 启用链接时优化,在链接阶段进行跨模块优化 |
-flto=<n> | 指定 LTO 并行线程数 |
-fuse-linker-plugin | 使用链接器插件改善 LTO |
LTO 工作流程:
实践示例:
# 编译和链接都需要加 -flto
gcc -flto -O2 -c file1.c -o file1.o
gcc -flto -O2 -c file2.c -o file2.o
gcc -flto -O2 -o program file1.o file2.o
# 并行 LTO(加速链接)
gcc -flto=4 -O2 -o program file1.o file2.o
2.9 安全增强选项
安全增强选项为生成的二进制文件提供多层次的安全防护,包括栈溢出保护、格式化字符串安全、地址空间随机化、控制流完整性等。在生产环境中合理组合这些选项,是防御常见攻击手段的基本措施。
2.9.1 栈保护(Stack Protector)
栈保护原理(Canary 机制):
Stack-protector 的基本思想是在函数返回地址被压入栈后,立即将一个 canary 值(随机选择的整数)压入栈中。在函数返回之前,程序会检查 canary 值是否发生变化。如果 canary 被修改(说明发生了栈溢出),程序将调用 __stack_chk_fail 函数中止执行,而不是继续执行可能被攻击者篡改的返回地址。
性能影响:
- 使用 stack-protector 选项编译后,会占用更大的栈空间(每个受保护函数多一个 canary 值的空间)
- 运行速度有所降低(函数入口需要获取 canary 值,出口需要校验)
- 栈溢出是在运行时检测的,不是编译时
栈保护选项:
| 选项 | 说明 | 保护范围 |
|---|---|---|
-fstack-protector | 对包含 ≥8 字节字符数组的函数插入保护 | 较小 |
-fstack-protector-strong | 对包含数组、局部变量地址引用的函数插入保护 | 中等(推荐) |
-fstack-protector-all | 对所有函数插入保护 | 最大 |
-fstack-clash-protection | 防止栈冲突攻击(Stack Clash) | 补充保护 |
-fstack-check | 在栈增长时插入探测点,防止跳过栈保护页 | 运行时检查 |
-fno-stack-protector | 禁用栈保护 | - |
-fstack-protector-strong 的保护条件(GCC ≥ 4.9 或 Linux ≥ 3.14):
- 函数包含任何大小的任意类型数组(即使数组定义在 struct 或 union 中)
- 函数包含局部变量地址赋给变量值
-fstack-protector 的阈值调整:
可通过 --param=ssp-buffer-size=N 改变默认的 8 字节阈值。例如:
# 将保护阈值改为 4 字节(更激进的保护)
gcc -fstack-protector --param=ssp-buffer-size=4 -o program program.c
-fstack-clash-protection 详解:
Stack Clash 是一种攻击技术,攻击者通过大量分配栈空间使栈增长"跳过"保护页(guard page),直接侵入其他内存区域(如另一个线程的栈或堆)。-fstack-clash-protection 通过以下机制防御:
- 编译器在每次大幅栈空间分配时,生成逐页探测代码(probe),确保每次栈增长都触及保护页
- 防止攻击者一次性跳过整个 guard page
# 启用栈冲突保护
gcc -fstack-clash-protection -o program program.c
-fstack-check 详解:
-fstack-check 在栈增长过程中插入周期性探测点,确保操作系统能够正确检测栈溢出并发送 SIGSEGV 信号。这对于多线程程序尤为重要,因为线程栈通常有固定大小限制:
- 没有
-fstack-check时,线程栈溢出可能直接跳过 guard page 而不触发信号 - 有
-fstack-check时,每次栈增长超过一定阈值都会探测,确保 guard page 被正确触发
# 在线程栈上启用保护探测
gcc -fstack-check -o program program.c -lpthread
内核编译时开启栈保护:
编译内核时通过 menuconfig(搜索 STACKPROTECTOR)开启栈保护:
Kernel hacking → Compiler options → Stack Protector
├── None
├── Basic stack-protector buffer overflow detection
└── Strong stack-protector buffer overflow detection ← 推荐
实战示例——验证栈保护是否生效:
// stack_test.c
#include <stdio.h>
#include <string.h>
void vulnerable_func(char *input) {
char buf[64];
strcpy(buf, input); // 危险的字符串复制
printf("%s\n", buf);
}
int main(void) {
char attack[128];
memset(attack, 'A', sizeof(attack));
attack[127] = '\0';
vulnerable_func(attack); // 触发栈溢出
return 0;
}
# 启用栈保护编译
$ gcc -fstack-protector-strong -g -o stack_test stack_test.c
# 运行(触发栈溢出时 canary 被覆盖,程序中止)
$ ./stack_test
*** stack smashing detected ***: terminated
Aborted (core dumped)
# 禁用栈保护编译(危险!仅用于对比演示)
$ gcc -fno-stack-protector -g -o stack_test_noprotect stack_test.c
$ ./stack_test_noprotect
Segmentation fault (core dumped)
2.9.2 格式化字符串安全
| 选项 | 说明 |
|---|---|
-D_FORTIFY_SOURCE=1 | 启用基本运行时缓冲区溢出检查 |
-D_FORTIFY_SOURCE=2 | 启用更强的运行时检查(需要 -O2 或更高) |
-Wformat | 检查 printf/scanf 等格式化字符串 |
-Wformat-security | 检测可能的安全问题的格式化字符串 |
-Werror=format-security | 将格式化安全问题视为错误 |
_FORTIFY_SOURCE 工作原理:
- 替换某些函数调用为更安全的版本(如
memcpy→__memcpy_chk) - 在运行时检查缓冲区边界
- 级别 2 比级别 1 更严格,但需要
-O2或更高优化级别
2.9.3 位置无关代码
| 选项 | 说明 | 使用场景 |
|---|---|---|
-fPIC | 生成位置无关代码 | 共享库(必须) |
-fPIE | 生成位置无关可执行文件 | 可执行文件(编译时) |
-pie | 链接位置无关可执行文件 | 可执行文件(链接时) |
使用场景:
- 共享库: 必须使用
-fPIC - 可执行文件: 使用
-fPIE -pie以支持 ASLR(地址空间布局随机化)
2.9.4 链接器安全
| 选项 | 说明 |
|---|---|
-Wl,-z,relro | 设置重定位表只读(部分 RELRO) |
-Wl,-z,now | 在程序启动时解析所有符号(立即绑定) |
-Wl,-z,relro -Wl,-z,now | 完全 RELRO |
-Wl,-z,noexecstack | 标记栈不可执行 |
-Wl,-z,separate-code | 强制分离代码段与数据段 |
-Wl,--as-needed | 只链接实际使用的库(减少攻击面) |
RELRO 保护原理:
- 防止 GOT(全局偏移表)覆盖攻击
- 部分 RELRO:将 GOT 中已解析的条目设置为只读
- 完全 RELRO:在程序启动时解析所有符号并设置为只读
2.9.5 控制流完整性
| 选项 | 说明 |
|---|---|
-fcf-protection=none | 禁用控制流保护 |
-fcf-protection=return | 仅保护返回指令 |
-fcf-protection=branch | 仅保护分支指令 |
-fcf-protection=full | 完全保护(需要 CPU 支持 CET) |
2.9.6 安全选项组合推荐表
| 场景 | 推荐选项 |
|---|---|
| 生产服务器 | -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fPIE -pie -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack |
| 嵌入式系统 | -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,noexecstack |
| 桌面应用 | -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fPIE -pie -Wl,-z,relro |
| 调试开发 | 安全选项可适当放宽,使用 -fno-stack-protector |
2.10 动态检测选项(Sanitizer)
Sanitizer 是编译器提供的运行时检测工具,能在程序运行时自动检测各类错误。相比 Valgrind 等外部工具,Sanitizer 性能开销更小,但需要重新编译。
2.10.1 AddressSanitizer(ASan)
| 选项 | 说明 |
|---|---|
-fsanitize=address | 启用地址消毒器,检测内存错误 |
-fsanitize-address-use-after-scope | 检测作用域后使用 |
检测能力:
- 堆/栈/全局缓冲区的越界访问
- use-after-free(使用已释放的内存)
- double-free(双重释放)
- 内存泄漏(与 LSan 配合)
使用示例:
# 编译(编译和链接都需要加)
gcc -fsanitize=address -g -o program program.c
# 运行(检测到错误时自动输出详细报告)
./program
2.10.2 ThreadSanitizer(TSan)
| 选项 | 说明 |
|---|---|
-fsanitize=thread | 启用线程消毒器,检测数据竞争 |
检测能力:多线程数据竞争、不正确的锁使用。
注意: 不能与 -fsanitize=address 同时使用。
2.10.3 UndefinedBehaviorSanitizer(UBSan)
| 选项 | 说明 |
|---|---|
-fsanitize=undefined | 启用未定义行为消毒器 |
-fsanitize=signed-integer-overflow | 检测有符号整数溢出 |
-fsanitize=float-divide-by-zero | 检测浮点除零 |
-fsanitize=null | 检测空指针解引用 |
-fsanitize=bounds | 检测数组越界 |
-fsanitize=alignment | 检测未对齐的内存访问 |
2.10.4 MemorySanitizer(MSan)
| 选项 | 说明 |
|---|---|
-fsanitize=memory | 启用内存消毒器,检测未初始化内存读取 |
注意: 目前主要由 Clang 支持,GCC 支持有限。
2.10.5 LeakSanitizer(LSan)
| 选项 | 说明 |
|---|---|
-fsanitize=leak | 启用内存泄漏检测器 |
使用示例:
gcc -fsanitize=leak -g -o program program.c
./program
# 程序退出时报告内存泄漏
2.10.6 检测通用选项
| 选项 | 说明 |
|---|---|
-fsanitize-recover=<check> | 允许从某些检查中恢复继续运行 |
-fsanitize-trap=<check> | 对某些检查使用陷阱而非报告 |
-fno-sanitize=<check> | 禁用特定的消毒器检查 |
2.10.7 Sanitizer 对比表
| Sanitizer | 检测范围 | 性能开销 | 互斥关系 | 适用场景 |
|---|---|---|---|---|
| ASan | 内存越界、use-after-free、double-free | ~2x | 与 TSan/MSan 互斥 | 内存错误调试 |
| TSan | 数据竞争 | ~5-10x | 与 ASan/MSan 互斥 | 多线程调试 |
| UBSan | 未定义行为 | ~1.2x | 无 | 代码健壮性检查 |
| MSan | 未初始化内存读取 | ~3x | 与 ASan/TSan 互斥 | 初始化检查 |
| LSan | 内存泄漏 | 小 | 可与 ASan 共存 | 泄漏检测 |
2.10.8 Sanitizer 运行时环境变量
Sanitizer 的行为可以通过环境变量在运行时进行精细控制:
| 环境变量 | 对应工具 | 示例 |
|---|---|---|
ASAN_OPTIONS | AddressSanitizer | ASAN_OPTIONS=detect_leaks=1:halt_on_error=0 |
TSAN_OPTIONS | ThreadSanitizer | TSAN_OPTIONS=halt_on_error=0:history_size=7 |
UBSAN_OPTIONS | UndefinedBehaviorSanitizer | UBSAN_OPTIONS=halt_on_error=0:print_stacktrace=1 |
LSAN_OPTIONS | LeakSanitizer | LSAN_OPTIONS=verbosity=1:log_threads=1 |
MSAN_OPTIONS | MemorySanitizer | MSAN_OPTIONS=halt_on_error=0 |
常用 ASAN_OPTIONS 配置:
# 启用泄漏检测(默认开启)
export ASAN_OPTIONS=detect_leaks=1
# 检测到错误后继续运行(收集更多错误)
export ASAN_OPTIONS=halt_on_error=0
# 输出到文件
export ASAN_OPTIONS=log_path=asan.log
# 检测栈返回后使用
export ASAN_OPTIONS=detect_stack_use_after_return=1
# 组合配置
export ASAN_OPTIONS=detect_leaks=1:halt_on_error=0:log_path=asan.log:verbosity=1
查看可用选项:
# 设置 help=1 可在程序启动时显示所有可用选项
ASAN_OPTIONS=help=1 ./program
常用 TSAN_OPTIONS 配置:
# 检测到竞争后继续运行
export TSAN_OPTIONS=halt_on_error=0
# 增大历史缓冲区(提高检测精度,增加内存使用)
export TSAN_OPTIONS=history_size=7
# 输出到文件
export TSAN_OPTIONS=log_path=tsan.log
2.11 静态分析选项
静态分析在不运行程序的情况下检查代码中的潜在问题,是 CI/CD 流水线中的重要环节。
2.11.1 GCC 静态分析器(GCC 10.0+)
| 选项 | 说明 |
|---|---|
-fanalyzer | 启用 GCC 内置的静态分析器 |
-Wanalyzer-use-after-free | 检测使用已释放的内存 |
-Wanalyzer-malloc-leak | 检测 malloc 分配的内存泄漏 |
-Wanalyzer-double-free | 检测双重释放内存 |
-Wanalyzer-null-dereference | 检测可能的空指针解引用 |
检测能力:内存泄漏、use-after-free、双重释放、空指针解引用、未初始化值。
要求:GCC 10.0 或更高版本。
2.11.2 Clang 静态分析器
| 选项 | 说明 |
|---|---|
--analyze | 启用 Clang 静态分析器 |
-Xanalyzer <arg> | 传递参数给 Clang 静态分析器 |
2.11.3 静态分析报告控制
| 选项 | 说明 |
|---|---|
-fanalyzer-verbose | 生成详细的静态分析报告 |
-fanalyzer-fine-grained | 进行更细粒度的分析 |
2.11.4 静态检查补充
| 选项 | 说明 |
|---|---|
-Warray-bounds=2 | 更严格的数组边界静态检查 |
-Wstringop-overflow=4 | 最严格的字符串操作静态检查 |
-Wnull-dereference | 检测编译时可确定的空指针解引用 |
2.12 性能分析编译选项
性能分析编译选项为性能分析工具(如 gprof、perf)提供必要的插桩和调试信息支持。正确配置这些选项是获得准确性能分析结果的前提。
2.12.1 Gprof 支持
| 选项 | 说明 |
|---|---|
-pg | 插入性能分析代码,生成 gmon.out |
-fno-omit-frame-pointer | 保留帧指针寄存器,配合 -pg 效果更好 |
使用流程:
# 编译
gcc -pg -g -o program program.c
# 运行(生成 gmon.out)
./program
# 分析
gprof program gmon.out > analysis.txt
2.12.2 Perf 支持
| 选项 | 说明 |
|---|---|
-fno-omit-frame-pointer | 保留帧指针,perf 需要用于调用栈回溯 |
-g | 生成调试信息,perf report 需要 |
为什么 -fno-omit-frame-pointer 对 perf 至关重要:
默认情况下,-O2 及以上优化级别会省略帧指针(将 RBP 寄存器释放为通用寄存器),这会导致:
perf record -g无法正确回溯调用栈(基于帧指针的栈回溯失效)- 火焰图显示不完整的调用链
- 函数调用关系丢失
解决方案对比:
| 方案 | 编译选项 | 优点 | 缺点 |
|---|---|---|---|
| 保留帧指针 | -fno-omit-frame-pointer | 栈回溯准确、开销小 | 少一个通用寄存器,可能影响性能(~1-3%) |
| DWARF 回溯 | -g(不需要帧指针) | 不牺牲寄存器 | 需要 -g 编译,perf record --call-graph dwarf 开销较大 |
# 推荐:性能分析时的编译选项
gcc -O2 -g -fno-omit-frame-pointer -o program program.c
# 如果无法重编译,使用 DWARF 模式
perf record --call-graph dwarf ./program
2.12.3 函数仪器化
| 选项 | 说明 |
|---|---|
-finstrument-functions | 在函数入口和出口插入调用 |
-finstrument-functions-exclude-file-list | 排除特定文件的仪器化 |
-finstrument-functions-exclude-function-list | 排除特定函数的仪器化 |
需要实现的钩子函数:
void __cyg_profile_func_enter(void *func, void *caller);
void __cyg_profile_func_exit(void *func, void *caller);
2.12.4 PGO 反馈优化
| 选项 | 说明 |
|---|---|
-fprofile-generate | 插入代码收集性能数据 |
-fprofile-use | 使用收集的性能数据进行优化 |
-fprofile-correction | 数据校正 |
-fauto-profile | 使用外部性能分析数据(.afdo 文件) |
PGO 工作流程:
2.12.5 调试辅助
| 选项 | 说明 |
|---|---|
-fdebug-prefix-map=<old>=<new> | 标准化调试信息中的路径,使构建可重现 |
-gsplit-dwarf | 将调试信息分离到单独文件(.dwo),减小二进制大小 |
2.13 本章选项速查总表
本章系统介绍了 GCC 从基础编译到高级优化的全部选项类别。以下按类别汇总核心选项,并给出典型构建场景的推荐组合。
| 类别 | 选项 | 说明 |
|---|---|---|
| 基础 | -E | 仅预处理 |
-S | 到汇编 | |
-c | 到目标文件 | |
-o <file> | 指定输出 | |
-D<name> | 定义宏 | |
-v | 显示详细过程 | |
| 文件查找 | -I<dir> | 头文件搜索路径 |
-isystem <dir> | 系统头文件路径 | |
-L<dir> | 库搜索路径 | |
-l<lib> | 链接库 | |
| 警告 | -Wall | 主要警告 |
-Wextra | 额外警告 | |
-Werror | 警告视为错误 | |
-w | 关闭警告 | |
-Wstack-usage=<N> | 栈使用量检查 | |
-Wformat-overflow=<N> | 格式化溢出检查 | |
| 优化 | -O0 ~ -O3 | 优化级别 |
-Os | 优化大小 | |
-Og | 调试友好优化 | |
-Ofast | 激进优化 | |
| 调试 | -g | 调试信息 |
-ggdb | GDB 格式 | |
-gdwarf | DWARF 格式 | |
| 安全 | -fstack-protector-strong | 栈保护 |
-fstack-clash-protection | 栈冲突保护 | |
-fstack-check | 栈增长探测 | |
-D_FORTIFY_SOURCE=2 | 强化检查 | |
-fPIE -pie | 位置无关可执行 | |
-Wl,-z,relro,-z,now | 完全 RELRO | |
| 检测 | -fsanitize=address | ASan |
-fsanitize=thread | TSan | |
-fsanitize=undefined | UBSan | |
-fsanitize=leak | LSan | |
| 静态分析 | -fanalyzer | GCC 静态分析器 |
| 性能 | -pg | gprof 支持 |
-fno-omit-frame-pointer | 保留帧指针(perf 需要) | |
-fprofile-generate | PGO 生成 | |
-fprofile-use | PGO 使用 |
典型构建场景选项组合速查:
| 场景 | 推荐选项组合 |
|---|---|
| 开发调试 | -O0 -g -ggdb -Wall -Wextra |
| 生产发布 | -O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fPIE -pie -Wl,-z,relro -Wl,-z,now |
| 嵌入式发布 | -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -fstack-protector -Wstack-usage=1024 |
| 内存调试 | -O1 -g -fsanitize=address -fno-omit-frame-pointer |
| 多线程调试 | -O1 -g -fsanitize=thread -fPIC |
| 性能分析 | -O2 -g -pg -fno-omit-frame-pointer |
| PGO 优化 | 第一阶段 -O2 -fprofile-generate,第二阶段 -O2 -fprofile-use |

9864

被折叠的 条评论
为什么被折叠?



