版權聲明:本文爲博主原創文章,遵循 CC 4.0 by-sa 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接: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 傳遞給鏈接器),這樣就能減少最終的可執行程序的大小了。
我們常常使用下面的配置啓用這個功能:
---------------------
版權聲明:本文爲CSDN博主「p_fly」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/pengfei240/article/details/55228228
CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,--gc-sections
例子
main.c 文件如下:
#include <stdio.h>
int fun_0(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_1(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_2(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
int fun_3(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}
void main(void)
{
fun_0();
fun_3();
}
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
比較大小
$ ll main_*
-rwxrwxr-x 1 8896 2月 16 00:42 main_normal*
-rwxrwxr-x 1 8504 2月 16 00:42 main_sections*
可以看見使用該功能的二進制文件要小於不使用該功能的二進制文件
分析sections
$ make 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
$ readelf -t main.o
...
[ 5] .text.fun_0
PROGBITS PROGBITS 0000000000000000 0000000000000048 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[ 6] .rela.text.fun_0
RELA RELA 0000000000000000 0000000000000508 24
0000000000000048 0000000000000018 5 8
[0000000000000040]: INFO LINK
[ 7] .text.fun_1
PROGBITS PROGBITS 0000000000000000 000000000000006c 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[ 8] .rela.text.fun_1
RELA RELA 0000000000000000 0000000000000550 24
0000000000000048 0000000000000018 7 8
[0000000000000040]: INFO LINK
[ 9] .text.fun_2
PROGBITS PROGBITS 0000000000000000 0000000000000090 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[10] .rela.text.fun_2
RELA RELA 0000000000000000 0000000000000598 24
0000000000000048 0000000000000018 9 8
[0000000000000040]: INFO LINK
[11] .text.fun_3
PROGBITS PROGBITS 0000000000000000 00000000000000b4 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[12] .rela.text.fun_3
RELA RELA 0000000000000000 00000000000005e0 24
0000000000000048 0000000000000018 11 8
[0000000000000040]: INFO LINK
從object文件中可以發現,fun_0 ~ fun_3 每個函數都是一個獨立的section.
而如果使用 make main_normal 生成的object文件,則共享一個默認的sections(.text)。
分析elf文件
$ readelf -a main_normal | grep fun_
52: 0000000000400526 36 FUNC GLOBAL DEFAULT 14 fun_0
55: 000000000040056e 36 FUNC GLOBAL DEFAULT 14 fun_2
65: 0000000000400592 36 FUNC GLOBAL DEFAULT 14 fun_3
66: 000000000040054a 36 FUNC GLOBAL DEFAULT 14 fun_1
$ readelf -a main_sections | grep fun_
49: 0000000000400526 36 FUNC GLOBAL DEFAULT 14 fun_0
57: 000000000040054a 36 FUNC GLOBAL DEFAULT 14 fun_3
可以看見,在最終的目標文件中,未使用的函數並未被鏈接進最終的目標文件。