- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
GCC 页面中针对功能部分和数据部分选项的以下内容:
-ffunction-sections
-fdata-sectionsPlace each function or data item into its own section in the output file if the target supports arbitrary sections. The name of the function or the name of the data item determines the section's name in the output file. Use these options on systems where the linker can perform optimizations to improve locality of reference in the instruction space. Most systems using the ELF object format and SPARC processors running Solaris 2 have linkers with such optimizations. AIX may have these optimizations in the future.
Only use these options when there are significant benefits from doing so. When you specify these options, the assembler and linker will create larger object and executable files and will also be slower. You will not be able to use gprof on all systems if you specify this option and you may have problems with debugging if you specify both this option and -g.
我的印象是这些选项将有助于减小可执行文件的大小。为什么这个页面说它会创建更大的可执行文件?我错过了什么吗?
最佳答案
有趣的是,使用 -fdata-sections
可以使您的函数的文字池,从而使您的函数本身更大。我特别在 ARM 上注意到了这一点,但在其他地方也可能如此。我测试的二进制文件只增长了四分之一,但它确实增长了。查看已更改函数的反汇编,原因就很清楚了。
如果目标文件中的所有 BSS(或 DATA)条目都分配给一个单独的部分,那么编译器可以将该部分的地址存储在函数文字池中,并生成与函数中该地址的已知偏移量的加载访问您的数据。但是,如果您启用 -fdata-sections
,它会将每段 BSS(或 DATA)数据放入其自己的部分,并且因为它不知道这些部分中的哪些可能稍后被垃圾收集,或者是什么为了链接器将所有这些部分放入最终的可执行镜像,它不能再使用单个地址的偏移量加载数据。因此,它必须为每个使用的数据在文字池中分配一个条目,一旦链接器弄清楚最终图像中的内容和位置,它就可以使用实际地址修复这些文字池条目数据。
所以是的,即使使用 -Wl,--gc-sections
结果图像也可能更大,因为实际的函数文本更大。
下面我添加了一个最小的例子
下面的代码足以看出我正在谈论的行为。请不要被 volatile 声明和全局变量的使用所迷惑,这两者在实际代码中都是有问题的。这里他们确保在使用 -fdata-sections 时创建两个数据部分。
static volatile int head;
static volatile int tail;
int queue_empty(void)
{
return head == tail;
}
本次测试使用的 GCC 版本为:
gcc version 6.1.1 20160526 (Arch Repository)
首先,在没有 -fdata-sections 的情况下,我们得到以下结果。
> arm-none-eabi-gcc -march=armv6-m \
-mcpu=cortex-m0 \
-mthumb \
-Os \
-c \
-o test.o \
test.c
> arm-none-eabi-objdump -dr test.o
00000000 <queue_empty>:
0: 4b03 ldr r3, [pc, #12] ; (10 <queue_empty+0x10>)
2: 6818 ldr r0, [r3, #0]
4: 685b ldr r3, [r3, #4]
6: 1ac0 subs r0, r0, r3
8: 4243 negs r3, r0
a: 4158 adcs r0, r3
c: 4770 bx lr
e: 46c0 nop ; (mov r8, r8)
10: 00000000 .word 0x00000000
10: R_ARM_ABS32 .bss
> arm-none-eabi-nm -S test.o
00000000 00000004 b head
00000000 00000014 T queue_empty
00000004 00000004 b tail
从 arm-none-eabi-nm
我们看到 queue_empty 是 20 字节长(14 十六进制),arm-none-eabi-objdump
输出显示函数末尾有一个重定位字,它是 BSS 部分(未初始化数据的部分)的地址。函数中的第一条指令将该值(BSS 的地址)加载到 r3 中。接下来的两条指令相对于 r3 加载,分别偏移 0 和 4 字节。这两个负载是head和tail的值的负载。我们可以在 arm-none-eabi-nm
输出的第一列中看到这些偏移量。函数末尾的nop
是对文字池地址进行字对齐。
接下来我们将看看添加 -fdata-sections 时会发生什么。
arm-none-eabi-gcc -march=armv6-m \
-mcpu=cortex-m0 \
-mthumb \
-Os \
-fdata-sections \
-c \
-o test.o \
test.c
arm-none-eabi-objdump -dr test.o
00000000 <queue_empty>:
0: 4b03 ldr r3, [pc, #12] ; (10 <queue_empty+0x10>)
2: 6818 ldr r0, [r3, #0]
4: 4b03 ldr r3, [pc, #12] ; (14 <queue_empty+0x14>)
6: 681b ldr r3, [r3, #0]
8: 1ac0 subs r0, r0, r3
a: 4243 negs r3, r0
c: 4158 adcs r0, r3
e: 4770 bx lr
...
10: R_ARM_ABS32 .bss.head
14: R_ARM_ABS32 .bss.tail
arm-none-eabi-nm -S test.o
00000000 00000004 b head
00000000 00000018 T queue_empty
00000000 00000004 b tail
我们立即看到 queue_empty 的长度增加了 4 个字节,达到 24 个字节(18 十六进制),现在 queue_empty 的文字池中有两个重定位要完成。这些重定位对应于创建的两个 BSS 部分的地址,每个部分对应一个全局变量。这里需要有两个地址,因为编译器无法知道链接器最终会将这两个段放在哪个相对位置。查看queue_empty开头的指令,我们看到有一个额外的负载,编译器必须生成单独的加载对以获取该部分的地址,然后是该部分中变量的值。这个版本的 queue_empty 中的额外指令不会使函数体变长,它只是占据了以前 nop 的位置,但一般情况下不会出现这种情况。
关于c - 查询 gcc 的 -ffunction-section & -fdata-sections 选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4274804/
我现在有一个第三方提供的c静态库(用arm-gcc编译的)。我不可能(让第三方)重新编译库。 在调查库内容时,我发现 gcc 选项 -ffunction-sections 和 -fdata-secti
我想在编译时从代码中删除未使用的函数。然后我写一些代码(main.c): #include const char *get1(); int main() { puts( get1() );
GCC 页面中针对功能部分和数据部分选项的以下内容: -ffunction-sections -fdata-sections Place each function or data item into
我是一名优秀的程序员,十分优秀!