gcc -ffunction-sections -fdata-sections -Wl,–gc-sections 參數詳解

原文鏈接:https://blog.csdn.net/pengfei240/article/details/55228228

版權聲明:本文爲博主原創文章,遵循 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
可以看見,在最終的目標文件中,未使用的函數並未被鏈接進最終的目標文件。
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章