一、背景
可执行程序需要链接一些静态库,但是静态库中的函数并没有全部使用,只用了其中的几个,但是系统默认会自动把整个静态库全部链接到可执行程序中,造成可执行程序的大小大大增加,浪费了flash空间和内存空间。
二、方法
因为GCC链接操作以section作为最小的处理单元,只要一个section中有某个符号被引用,该section就会被加入。
如果我们的某个.c程序中所有function都加入同一个section.则如果用到这个.c生成的.o的其中任何一个function.则必须将所有function(符号)加入其中。如此,则使用-ffunction-sections 和 -fdata-sections将每个符号创建为一个sections. sections名与function,data名保持一致。
则在link阶段,-Wl,–gc-sections 申明去掉不用的section。就可以去掉没用的function(符号)了。
1、编译时候加上 -ffunction-sections
2、链接时候加上 -Wl,–gc-sections
三、示例
func.h
int fun_0();
int fun_1();
int fun_2();
int fun_3();
int fun_4();
int fun_5();
int fun_6();
func.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "func.h"
int fun_0()
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_1()
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_2()
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_3()
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_4()
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_5()
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_6()
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "func.h"
int main()
{
fun_0();
return 0;
}
makefile
drop:
gcc -c -ffunction-sections func.c
gcc -c -ffunction-sections main.c
gcc -Wl,--gc-sections -o test_size func.o main.o
normal:
gcc -c func.c
gcc -c main.c
gcc -o test_size func.o main.o
clean:
rm *.o test_size
运行结果对比
root@ubuntu:/home/work/test/func# make normal
gcc -c func.c
gcc -c main.c
gcc -o test_size func.o main.o
root@ubuntu:/home/work/test/func# ls -l
total 32
-rwxr--r-- 1 nobody nogroup 622 Jul 1 16:24 func.c
-rwxr--r-- 1 nobody nogroup 102 Jul 1 15:53 func.h
-rw-r--r-- 1 root root 2048 Jul 1 16:25 func.o
-rwxr--r-- 1 nobody nogroup 125 Jul 1 16:24 main.c
-rw-r--r-- 1 root root 960 Jul 1 16:25 main.o
-rwxr--r-- 1 nobody nogroup 239 Jul 1 16:01 Makefile
-rwxr-xr-x 1 root root 7744 Jul 1 16:25 test_size
root@ubuntu:/home/work/test/func# make drop
gcc -c -ffunction-sections func.c
gcc -c -ffunction-sections main.c
gcc -Wl,--gc-sections -o test_size func.o main.o
root@ubuntu:/home/work/test/func# ls -l
total 32
-rwxr--r-- 1 nobody nogroup 622 Jul 1 16:24 func.c
-rwxr--r-- 1 nobody nogroup 102 Jul 1 15:53 func.h
-rw-r--r-- 1 root root 2788 Jul 1 16:25 func.o
-rwxr--r-- 1 nobody nogroup 125 Jul 1 16:24 main.c
-rw-r--r-- 1 root root 1028 Jul 1 16:25 main.o
-rwxr--r-- 1 nobody nogroup 239 Jul 1 16:01 Makefile
-rwxr-xr-x 1 root root 7432 Jul 1 16:25 test_size
减少了 7744-7432=312 bytes
查看func.o关于section情况对比,看rel.text的内容,如果每个函数都有,那么就证明每个函数都是独立的section了,同理可查看静态库.a文件,readelf -t func.a
root@ubuntu:/home/work/test/func# readelf -t func.o
There are 26 section headers, starting at offset 0x6d4:
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 000032 00 0 0 1
[00000002]: ALLOC
[ 5] .text.fun_0
PROGBITS 00000000 000066 000024 00 0 0 1
[00000006]: ALLOC, EXEC
[ 6] .rel.text.fun_0
REL 00000000 0005f4 000018 08 24 5 4
[00000040]: INFO LINK
[ 7] .text.fun_1
PROGBITS 00000000 00008a 000024 00 0 0 1
[00000006]: ALLOC, EXEC
[ 8] .rel.text.fun_1
REL 00000000 00060c 000018 08 24 7 4
[00000040]: INFO LINK
[ 9] .text.fun_2
PROGBITS 00000000 0000ae 000024 00 0 0 1
[00000006]: ALLOC, EXEC
[10] .rel.text.fun_2
REL 00000000 000624 000018 08 24 9 4
[00000040]: INFO LINK
[11] .text.fun_3
PROGBITS 00000000 0000d2 000024 00 0 0 1
[00000006]: ALLOC, EXEC
[12] .rel.text.fun_3
REL 00000000 00063c 000018 08 24 11 4
[00000040]: INFO LINK
[13] .text.fun_4
PROGBITS 00000000 0000f6 000024 00 0 0 1
[00000006]: ALLOC, EXEC
[14] .rel.text.fun_4
REL 00000000 000654 000018 08 24 13 4
[00000040]: INFO LINK
[15] .text.fun_5
PROGBITS 00000000 00011a 000024 00 0 0 1
[00000006]: ALLOC, EXEC
[16] .rel.text.fun_5
REL 00000000 00066c 000018 08 24 15 4
[00000040]: INFO LINK
[17] .text.fun_6
PROGBITS 00000000 00013e 000024 00 0 0 1
[00000006]: ALLOC, EXEC
[18] .rel.text.fun_6
REL 00000000 000684 000018 08 24 17 4
[00000040]: INFO LINK
[19] .comment
PROGBITS 00000000 000162 000025 01 0 0 1
[00000030]: MERGE, STRINGS
[20] .note.GNU-stack
PROGBITS 00000000 000187 000000 00 0 0 1
[00000000]:
[21] .eh_frame
PROGBITS 00000000 000188 0000f8 00 0 0 4
[00000002]: ALLOC
[22] .rel.eh_frame
REL 00000000 00069c 000038 08 24 21 4
[00000040]: INFO LINK
[23] .shstrtab
STRTAB 00000000 000280 0000cb 00 0 0 1
[00000000]:
[24] .symtab
SYMTAB 00000000 00034c 0001f0 10 25 23 4
[00000000]:
[25] .strtab
STRTAB 00000000 00053c 0000b7 00 0 0 1
[00000000]:
root@ubuntu:/home/work/test/func#
root@ubuntu:/home/work/test/func# make clean
rm *.o test_size
root@ubuntu:/home/work/test/func#
root@ubuntu:/home/work/test/func#
root@ubuntu:/home/work/test/func# make normal
gcc -c func.c
gcc -c main.c
gcc -o test_size func.o main.o
root@ubuntu:/home/work/test/func#
root@ubuntu:/home/work/test/func#
root@ubuntu:/home/work/test/func# readelf -t func.o
There are 13 section headers, starting at offset 0x5f8:
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 0000fc 00 0 0 1
[00000006]: ALLOC, EXEC
[ 2] .rel.text
REL 00000000 000518 0000a8 08 11 1 4
[00000040]: INFO LINK
[ 3] .data
PROGBITS 00000000 000130 000000 00 0 0 1
[00000003]: WRITE, ALLOC
[ 4] .bss
NOBITS 00000000 000130 000000 00 0 0 1
[00000003]: WRITE, ALLOC
[ 5] .rodata
PROGBITS 00000000 000130 000032 00 0 0 1
[00000002]: ALLOC
[ 6] .comment
PROGBITS 00000000 000162 000025 01 0 0 1
[00000030]: MERGE, STRINGS
[ 7] .note.GNU-stack
PROGBITS 00000000 000187 000000 00 0 0 1
[00000000]:
[ 8] .eh_frame
PROGBITS 00000000 000188 0000f8 00 0 0 4
[00000002]: ALLOC
[ 9] .rel.eh_frame
REL 00000000 0005c0 000038 08 11 8 4
[00000040]: INFO LINK
[10] .shstrtab
STRTAB 00000000 000280 00005f 00 0 0 1
[00000000]:
[11] .symtab
SYMTAB 00000000 0002e0 000180 10 12 16 4
[00000000]:
[12] .strtab
STRTAB 00000000 000460 0000b7 00 0 0 1
[00000000]:
注:
1)使用了section选项,当函数被声明了,但是函数没有被调用,函数体不实现也编译ok
2)但是函数体不能重复定义