轉載:https://blog.csdn.net/pengfei240/article/details/55228228
背景
有時我們的程序會定義一些暫時使用不上的功能和函數(shù)稠通,雖然我們不使用這些功能和函數(shù)捆憎,但它們往往會浪費我們的ROM和RAM的空間秒啦。這在使用靜態(tài)庫時觉既,體現(xiàn)的更為嚴重。有時阳仔,我們只使用了靜態(tài)庫僅有的幾個功能掸冤,但是系統(tǒng)默認會自動把整個靜態(tài)庫全部鏈接到可執(zhí)行程序中,造成可執(zhí)行程序的大小大大增加食呻。
參數(shù)詳解
為了解決前面分析的問題流炕,我們引入了標題中的幾個參數(shù)澎现。GCC鏈接操作是以section作為最小的處理單元,只要一個section中的某個符號被引用每辟,該section就會被加入到可執(zhí)行程序中去剑辫。因此,GCC在編譯時可以使用 -ffunction-sections 和 -fdata-sections 將每個函數(shù)或符號創(chuàng)建為一個sections渠欺,其中每個sections名與function或data名保持一致妹蔽。而在鏈接階段, -Wl,–gc-sections 指示鏈接器去掉不用的section(其中-wl, 表示后面的參數(shù) -gc-sections 傳遞給鏈接器)峻堰,這樣就能減少最終的可執(zhí)行程序的大小了讹开。
我們常常使用下面的配置啟用這個功能:
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文件中可以發(fā)現(xiàn),fun_0 ~ fun_3 每個函數(shù)都是一個獨立的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]:
可以看見,在最終的目標文件中镶蹋,未使用的函數(shù)并未被鏈接進最終的目標文件成艘。