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


背景

有時我們的程序會定義一些暫時使用不上的功能和函數,雖然我們不使用這些功能和函數,但它們往往會浪費我們的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 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

  可以看見,在最終的目標文件中,未使用的函數並未被鏈接進最終的目標文件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM