-ffunction-sections 单独使用无效,因其仅将函数分段,裁剪需配合链接器选项 -wl,--gc-sections 才能生效;二者必须共用,缺一则体积无法缩减。

为什么 -ffunction-sections 单独用完全没效果
很多人加了 -ffunction-sections 就以为函数能被自动裁掉,结果 Flash 体积纹丝不动。这是因为该选项只让编译器把每个函数放进独立的 .text.xxx 段,但链接器默认仍会把所有输入段无条件合并进输出文件——它根本不会主动丢东西。
真正触发裁剪的是链接器的垃圾收集(garbage collection),必须显式启用:-Wl,--gc-sections。而且它只对「按段划分」的输入才起作用,所以二者是绑定关系:没有 -ffunction-sections,--gc-sections 就没得收;有了 -ffunction-sections 却不配 --gc-sections,就只是白占编译器资源。
-
-ffunction-sections是编译阶段开关,影响.o文件内部结构 -
-Wl,--gc-sections是链接阶段开关,由ld执行实际裁剪 - 二者必须同时出现,顺序无关,但缺一不可
类成员函数是否会被 --gc-sections 正确识别和裁剪
可以,但有前提:不能有隐式引用。C++ 类的普通成员函数、static 成员函数、内联函数(若未强制展开)都会生成独立符号并落入各自函数段,--gc-sections 能正常处理。
真正危险的是以下几类「看不见的引用」:
- 虚函数表(vtable)中的条目:哪怕你没调用某个虚函数,只要类类型被实例化或其指针/引用被传递,整个 vtable 就可能被拉进来,连带其中所有虚函数
- 模板实例化:一个模板函数被某处实例化一次,对应代码就保留;但若头文件中定义了未使用的特化版本,且未被 ODR-used,编译器可能不生成,也可能生成后因无引用被裁——行为依赖于具体实现和优化级别
- 静态数据成员初始化函数(如
__cxx_global_var_init):这类由编译器生成的辅助函数容易被忽略,但若其关联的静态变量未被引用,对应 init 函数也应被裁,前提是编译器生成时也用了-fdata-sections
建议搭配 -fdata-sections -Wl,--gc-sections 一起用,否则全局对象、字符串字面量等静态数据仍会常驻 Flash。
常见导致裁剪失效的隐藏引用点
即使参数全对,Flash 体积也没变小,大概率是某些符号被意外保留。最常踩的坑包括:
- 使用了
__attribute__((used))或__attribute__((section("...")))的函数/变量:链接器认为它们「被标记为需保留」,直接跳过 GC - 中断向量表中显式列出的函数名:比如
void TIM2_IRQHandler(void)被写在启动文件或向量表里,即使源码中没调用,也会被强引用 - 通过函数指针间接调用但未被 LTO 识别的场景:例如将函数地址存入结构体数组、回调表,而该结构体本身被全局变量持有,就会形成隐式根引用
- 未开启
-O2或更高优化级:GCC 在-O0下可能不内联 trivial 函数,也不消除明显死代码,导致本可省略的调用链残留,阻碍 GC 判定
验证是否真被裁掉,可用 arm-none-eabi-objdump -t your.elf | grep 'your_func_name' 查符号是否存在;或用 arm-none-eabi-size -A your.elf 对比各段大小变化。
对裸机固件特别关键的补充操作
嵌入式环境下,标准 C++ 运行时(如 libstdc++)和启动代码(crt0.o)自带大量未使用的辅助函数(如 __aeabi_*、__libc_init_array),它们往往不在你的源码控制范围内,却吃掉数 KB Flash。
- 务必使用
-nostdlib -nodefaultlibs彻底剥离标准库,自己提供最小_start和必要的__libc_init_array(如果用了全局构造) - 确认链接脚本中未保留整个
.text.*段:有些旧版链接脚本用*(.text)收集所有文本段,但漏掉了*(.text.*),导致-ffunction-sections生成的细粒度段被忽略 - 若用 CMake,确保
target_link_options和target_compile_options都正确传递,且add_executable(... EXCLUDE_FROM_ALL)不干扰链接流程
最棘手的情况是:你确认所有函数都未被调用,但链接器仍保留它们——这时候大概率是 C++ 异常、RTTI 或运行时类型信息在背后偷偷建表。关闭它们:-fno-exceptions -fno-rtti,否则 vtable、typeinfo、catch 块相关函数永远无法被 GC。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











