转载:https://blog.csdn.net/pengfei240/article/details/55228228
背景
有时我们的程序会定义一些暂时使用不上的功能和函数,虽然我们不使用这些功能和函数,但它们往往会浪费我们的ROM和RAM的空间。这在使用静态库时,体现的更为严重。有时,我们只使用了静态库仅有的几个功能,但是系统默认会自动把整个静态库全部链接到可执行程序中,造成可执行程序的大小大大增加。
参数详解
为了解决前面分析的问题,我们引入了标题中的几个参数。GCC链接操作是以section作为最小的处理单元,只要一个section中的某个符号被引用,该section就会被加入到可执行程序中去。因此,GCC在编译时可以使用 -ffunction-sections 和 -fdata-sections 将每个函数或符号创建为一个sections,其中每个sections名与function或data名保持一致。而在链接阶段, -Wl,–gc-sections 指示链接器去掉不用的section(其中-wl, 表示后面的参数 -gc-sections 传递给链接器),这样就能减少最终的可执行程序的大小了。
我们常常使用下面的配置启用这个功能:
CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,--gc-sections
例子
main.c 文件如下:
#include <stdio.h>
int fun0(void)
{
printf("%s:%d\n",__FUNCTION__,__LINE__);
return 0;
}
int fun1(void)
{
printf("%s:%d\n",__FUNCTION__,__LINE__);
return 0;
}
int fun2(void)
{
printf("%s:%d\n",__FUNCTION__,__LINE__);
return 0;
}
int fun3(void)
{
printf("%s:%d\n",__FUNCTION__,__LINE__);
return 0;
}
void main(void)
{
fun0();
fun3();
}
Makefile如下:
main_sections:
gcc -ffunction-sections -fdata-sections -c main.c
gcc -Wl,--gc-sections -o $@ main.o
main_normal:
gcc -c main.c
gcc -o $@ main.o
clean:
rm -rf *.o main_sections main_normal
验证
运行
$ make main_sections
gcc -ffunction-sections -fdata-sections -c main.c
gcc -Wl,--gc-sections -o main_sections main.o
$ make main_normal
gcc -c main.c
gcc -o main_normal main.o
比较大小
$ ls -l main_*
-rwxrwxr-x 1 8896 2月 16 00:42 main_normal*
-rwxrwxr-x 1 8504 2月 16 00:42 main_sections*
可以看见使用该功能的二进制文件要小于不使用该功能的二进制文件
分析sections
$readelf -t main.o
Section Headers:
[Nr] Name
Type Addr Off Size ES Lk Inf Al
Flags
[ 0]
NULL 00000000 000000 000000 00 0 0 0
[00000000]:
[ 1] .text
PROGBITS 00000000 000034 000000 00 0 0 1
[00000006]: ALLOC, EXEC
[ 2] .data
PROGBITS 00000000 000034 000000 00 0 0 1
[00000003]: WRITE, ALLOC
[ 3] .bss
NOBITS 00000000 000034 000000 00 0 0 1
[00000003]: WRITE, ALLOC
[ 4] .rodata
PROGBITS 00000000 000034 000007 00 0 0 1
[00000002]: ALLOC
[ 5] .text.fun0
PROGBITS 00000000 00003b 000029 00 0 0 1
[00000006]: ALLOC, EXEC
[ 6] .rel.text.fun0
REL 00000000 00093c 000018 08 24 5 4
[00000000]:
[ 7] .text.fun1
PROGBITS 00000000 000064 000029 00 0 0 1
[00000006]: ALLOC, EXEC
[ 8] .rel.text.fun1
REL 00000000 000954 000018 08 24 7 4
[00000000]:
[ 9] .text.fun2
PROGBITS 00000000 00008d 000029 00 0 0 1
[00000006]: ALLOC, EXEC
[10] .rel.text.fun2
REL 00000000 00096c 000018 08 24 9 4
[00000000]:
[11] .text.fun3
PROGBITS 00000000 0000b6 000029 00 0 0 1
[00000006]: ALLOC, EXEC
[12] .rel.text.fun3
REL 00000000 000984 000018 08 24 11 4
[00000000]:
[13] .text.main
PROGBITS 00000000 0000df 000012 00 0 0 1
[00000006]: ALLOC, EXEC
[14] .rel.text.main
REL 00000000 00099c 000010 08 24 13 4
[00000000]:
[15] .rodata.__FUNCTION__.1826
PROGBITS 00000000 0000f1 000005 00 0 0 1
[00000002]: ALLOC
[16] .rodata.__FUNCTION__.1830
PROGBITS 00000000 0000f6 000005 00 0 0 1
[00000002]: ALLOC
[17] .rodata.__FUNCTION__.1834
PROGBITS 00000000 0000fb 000005 00 0 0 1
[00000002]: ALLOC
[18] .rodata.__FUNCTION__.1838
PROGBITS 00000000 000100 000005 00 0 0 1
[00000002]: ALLOC
[19] .comment
PROGBITS 00000000 000105 00002c 01 0 0 1
[00000030]: MERGE, STRINGS
[20] .note.GNU-stack
PROGBITS 00000000 000131 000000 00 0 0 1
[00000000]:
[21] .eh_frame
PROGBITS 00000000 000134 0000b8 00 0 0 4
[00000002]: ALLOC
[22] .rel.eh_frame
REL 00000000 0009ac 000028 08 24 21 4
[00000000]:
[23] .shstrtab
STRTAB 00000000 0001ec 00010e 00 0 0 1
[00000000]:
[24] .symtab
SYMTAB 00000000 00070c 0001c0 10 25 22 4
[00000000]:
[25] .strtab
STRTAB 00000000 0008cc 000070 00 0 0 1
[00000000]:
从object文件中可以发现,fun_0 ~ fun_3 每个函数都是一个独立的section.
而如果使用 make main_normal 生成的object文件,则共享一个默认的sections(.text)。
分析elf文件:
helongbao@lubaoquan-HP-280-Pro-G1-MT:~/leixiaoye$ readelf -t main.o
There are 13 section headers, starting at offset 0x24c:
Section Headers:
[Nr] Name
Type Addr Off Size ES Lk Inf Al
Flags
[ 0]
NULL 00000000 000000 000000 00 0 0 0
[00000000]:
[ 1] .text
PROGBITS 00000000 000034 0000b6 00 0 0 1
[00000006]: ALLOC, EXEC
[ 2] .rel.text
REL 00000000 0005f4 000070 08 11 1 4
[00000000]:
[ 3] .data
PROGBITS 00000000 0000ea 000000 00 0 0 1
[00000003]: WRITE, ALLOC
[ 4] .bss
NOBITS 00000000 0000ea 000000 00 0 0 1
[00000003]: WRITE, ALLOC
[ 5] .rodata
PROGBITS 00000000 0000ea 00001b 00 0 0 1
[00000002]: ALLOC
[ 6] .comment
PROGBITS 00000000 000105 00002c 01 0 0 1
[00000030]: MERGE, STRINGS
[ 7] .note.GNU-stack
PROGBITS 00000000 000131 000000 00 0 0 1
[00000000]:
[ 8] .eh_frame
PROGBITS 00000000 000134 0000b8 00 0 0 4
[00000002]: ALLOC
[ 9] .rel.eh_frame
REL 00000000 000664 000028 08 11 8 4
[00000000]:
[10] .shstrtab
STRTAB 00000000 0001ec 00005f 00 0 0 1
[00000000]:
[11] .symtab
SYMTAB 00000000 000454 000130 10 12 13 4
[00000000]:
[12] .strtab
STRTAB 00000000 000584 000070 00 0 0 1
[00000000]:
可以看见,在最终的目标文件中,未使用的函数并未被链接进最终的目标文件。