You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GCC中-fdata-sections与-ffunction-sections选项的作用及应用场景

关于-ffunction-sections和-fdata-sections的实际应用场景

背景说明

根据手册描述:

-ffunction-sections
-fdata-sections
如果目标平台支持自定义段,将每个函数或数据项放置到输出文件中独立的段内。函数或数据项的名称决定了输出文件中段的名称。

我用这两个选项编译了以下C代码:

...

int bss_var_1 = 0;
int bss_var_2;
int bss_var_3;

int data_var_1 = 90;
int data_var_2 = 47;
int data_var_3[128] = {212};

int foo() {
    printf("hello, foo()\n");
}

int func() {
    printf("hello, func()\n");
}

int main(void) {
    ...
}

生成main.o后,执行readelf build/main.o -S查看段信息,确认每个函数和数据都被放到了独立的段中:

$ readelf build/main.o -S
There are 34 section headers, starting at offset 0xeb0:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000000 00  AX  0   0  2
  [ 2] .data             PROGBITS        00000000 000034 000000 00  WA  0   0  1
  [ 3] .bss              NOBITS          00000000 000034 000000 00  WA  0   0  1
  [ 4] .bss.bss_var_1    NOBITS          00000000 000034 000004 00  WA  0   0  4
  [ 5] .bss.bss_var_2    NOBITS          00000000 000034 000004 00  WA  0   0  4
  [ 6] .bss.bss_var_3    NOBITS          00000000 000034 000004 00  WA  0   0  4
  [ 7] .data.data_var_1  PROGBITS        00000000 000034 000004 00  WA  0   0  4
  [ 8] .data.data_var_2  PROGBITS        00000000 000038 000004 00  WA  0   0  4
  [ 9] .data.data_var_3  PROGBITS        00000000 00003c 000200 00  WA  0   0  4
  [10] .rodata           PROGBITS        00000000 00023c 000047 00   A  0   0  4
  [11] .text.foo         PROGBITS        00000000 000284 000014 00  AX  0   0  4
  [12] .rel.text.foo     REL             00000000 000b78 000010 08   I 31  11  4
  [13] .text.func        PROGBITS        00000000 000298 000014 00  AX  0   0  4
  [14] .rel.text.func    REL             00000000 000b88 000010 08   I 31  13  4
  [15] .text.main        PROGBITS        00000000 0002ac 000028 00  AX  0   0  4
  [16] .rel.text.main    REL             00000000 000b98 000020 08   I 31  15  4

  ...

现在想知道:开发者为什么需要这两个编译选项?具体有哪些实际应用场景?


实际应用场景

1. 缩减可执行文件体积(死代码/数据消除)

默认编译时,所有函数会被打包到.text段,已初始化数据进.data段,未初始化数据进.bss段。链接器只能整段保留或丢弃这些内容。但启用这两个选项后,每个函数和数据都是独立段,配合链接器的--gc-sections参数,可以直接丢弃未被引用的函数/数据段。

比如你引入了一个包含大量工具函数的库,但实际只用到其中几个,这个组合能彻底删掉没用的部分,大幅减小二进制体积——这在嵌入式开发里尤为关键,毕竟这类设备的Flash和RAM空间通常很紧张。

2. 精准控制内存布局

在嵌入式或实时系统开发中,经常需要把特定函数/数据放到指定内存区域:比如把中断处理函数放到速度更快的SRAM,或者把加密密钥放到带硬件保护的内存块。

有了独立段后,你可以在链接脚本里直接通过段名(比如.text.foo、.data.data_var_1)指定它们的加载地址,不用把整个.text或.data段都挪过去,灵活性大幅提升。

3. 优化缓存性能,减少冲突

CPU缓存按块(Cache Line)加载内容,如果多个高频访问的函数/数据挤在同一个段,可能会被加载到同一个缓存块,导致互相覆盖(缓存颠簸)。

将每个函数/数据放到独立段后,链接器可以调整段的排列顺序,把高频访问的内容分散到不同缓存块,减少冲突,提升程序运行效率。这种优化在高性能计算或实时系统中很实用。

4. 支持代码热更新/动态补丁

如果需要实现不重启程序就更新部分代码的功能,独立段的结构会非常方便。你可以定位到某个函数对应的段(比如.text.foo),直接替换这段内存的内容,不会影响其他函数。

这种场景常见于服务器程序的热修复,或者需要动态升级功能的嵌入式设备。

5. 调试与性能分析更精准

使用性能分析工具(如perf)时,独立段能让你精确统计每个函数的执行时间、缓存命中情况,因为每个函数的边界清晰,工具能直接定位到具体函数的段信息。调试时也能更清晰地查看每个数据的内存分布,排查问题更高效。


内容的提问来源于stack exchange,提问作者laplacedoge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 04:15:38