減少可執行程序的大小

一、背景
可執行程序需要鏈接一些靜態庫,但是靜態庫中的函數並沒有全部使用,只用了其中的幾個,但是系統默認會自動把整個靜態庫全部鏈接到可執行程序中,造成可執行程序的大小大大增加,浪費了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)但是函數體不能重複定義

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