GCC 优化过于复杂,这里探索 GCC 对未使用的代码和变量以及文件的优化策略。

样例

如下两个文件。

$ tree
├── main.c
└── unused.c

main.c 文件内容如下。

#include <stdio.h>
// __attribute__((used, section(".linker_entry_list")))
int main_extern_unused_var = 0;
// __attribute__((used, section(".linker_entry_list")))
static int main_static_unused_var = 0;
// __attribute__((used))
static int main_static_unused_func(void) {
    printf("static unused function.\n");
    return 0;
}
// __attribute__((used))
int main_extern_unused_func(void) {
    printf("extern unused function.\n");
    return 0;
}
int main(int argc, char* argv[]) {
    printf("hello world!\n");
    return 0;
}

unused.c

#include <stdio.h>
// __attribute__((used, section(".linker_entry_list")))
int other_extern_unused_var = 0;
// __attribute__((used, section(".linker_entry_list")))
static int other_static_unused_var = 0;
// __attribute__((used))
static int other_static_unused_func(void) {
    printf("static unused function.\n");
    return 0;
}
// __attribute__((used))
int other_extern_unused_func(void) {
    printf("extern unused function.\n");
    return 0;
}

探索

  • 默认编译选项,通过可执行文件确定所有变量和函数都未优化。

    $ gcc -o main.app main.c unused.c 
     readelf -a main.app |grep unused
        37: 0000000000004018     4 OBJECT  LOCAL  DEFAULT   26 main_static_unused_var
        38: 0000000000001149    27 FUNC    LOCAL  DEFAULT   16 main_static_unused_func
        40: 0000000000004020     4 OBJECT  LOCAL  DEFAULT   26 other_static_unused_var
        41: 00000000000011a5    27 FUNC    LOCAL  DEFAULT   16 other_static_unused_func
        57: 0000000000001164    27 FUNC    GLOBAL DEFAULT   16 main_extern_unused_func
        68: 00000000000011c0    27 FUNC    GLOBAL DEFAULT   16 other_extern_unused_func
        69: 000000000000401c     4 OBJECT  GLOBAL DEFAULT   26 other_extern_unused_var
        70: 0000000000004014     4 OBJECT  GLOBAL DEFAULT   26 main_extern_unused_var
  • -O1 -O2 -Os 选项,所有static 链接属性的变量和函数被用优化掉。

    $ gcc -Os -o main.app main.c unused.c 
    $ readelf -a main.app |grep unused
        53: 0000000000001169    21 FUNC    GLOBAL DEFAULT   16 main_extern_unused_func
        64: 000000000000117e    21 FUNC    GLOBAL DEFAULT   16 other_extern_unused_func
        65: 0000000000004018     4 OBJECT  GLOBAL DEFAULT   26 other_extern_unused_var
        66: 0000000000004014     4 OBJECT  GLOBAL DEFAULT   26 main_extern_unused_var
  • static 链接属性的变量增加 __attribute__((used)) 属性。优化被抑制。

    __attribute__((used)) static 
    int other_static_unused_var=0;
    __attribute__((used)) static 
    int other_static_unused_func(void) {
        printf("static unused function.\n");
        return 0;
    }
    $ gcc -Os -o main.app main.c unused.c 
    $ readelf -a main.app |grep unused
        29: 0000000000001169    21 FUNC    LOCAL  DEFAULT   16 main_static_unused_func
        30: 0000000000004018     4 OBJECT  LOCAL  DEFAULT   26 main_static_unused_var
        40: 0000000000001193    21 FUNC    LOCAL  DEFAULT   16 other_static_unused_func
        41: 0000000000004020     4 OBJECT  LOCAL  DEFAULT   26 other_static_unused_var
        57: 000000000000117e    21 FUNC    GLOBAL DEFAULT   16 main_extern_unused_func
        68: 00000000000011a8    21 FUNC    GLOBAL DEFAULT   16 other_extern_unused_func
        69: 000000000000401c     4 OBJECT  GLOBAL DEFAULT   26 other_extern_unused_var
        70: 0000000000004014     4 OBJECT  GLOBAL DEFAULT   26 main_extern_unused_var
  • 增加 -Wl,--gc-sections 选项,所有的 unused.c 变量和函数,以及 main.c 中的变量全部被优化。

    $gcc -Wl,--gc-sections -o main.app main.c unused.c 
    $readelf -a main.app |grep unused
        37: 0000000000001149    27 FUNC    LOCAL  DEFAULT   16 main_static_unused_func
        53: 0000000000001164    27 FUNC    GLOBAL DEFAULT   16 main_extern_unused_func
  • 同时使用 -Wl,--gc-sections-O1。进一步优化掉 main.c 中的 static 函数,只剩下最后一个未使用的 extern 函数。

    $ gcc -Wl,--gc-sections -O1 -o main.app main.c unused.c 
    $ readelf -a main.app |grep unused
        52: 0000000000001149    30 FUNC    GLOBAL DEFAULT   16 main_extern_unused_func
  • 同时使用 -Wl,--gc-sections-O2 或者 -Os 。所有未使用的函数、变量全部被优化。

    $ gcc -Wl,--gc-sections -O2 -o main.app main.c unused.c 
  • 或者额外指定 -ffunction-sections ,进一步把未使用的函数优化掉。

    $gcc -Wl,--gc-sections -ffunction-sections -fdata-sections -o main.app main.c unused.c
  • 对于--gc-sections ,通过设置 KEEP 链接抑制变量的优化。注意,并不能对函数使用属性。

    __attribute__((used, section(".linker_entry_list"))) int main_extern_unused_var = 0;
    __attribute__((used, section(".linker_entry_list"))) static int main_static_unused_var = 0;
    .linker_entry_list : {
    KEEP(*(SORT(.linker_entry_list*)));
    }
    $gcc -Wl,--gc-sections -ffunction-sections -fdata-sections  -o main.app main.c unused.c -T x86_64.lds
    $ readelf -a main.app |grep unused
        25: 3ffbdb64     4 OBJECT  LOCAL  DEFAULT   13 main_static_unused_var
        27: 3ffbdb6c     4 OBJECT  LOCAL  DEFAULT   13 other_static_unused_var
        88: 3ffbdb68     4 OBJECT  GLOBAL DEFAULT   13 other_extern_unused_var
        98: 3ffbdb60     4 OBJECT  GLOBAL DEFAULT   13 main_extern_unused_var

结论

选项结果
默认选项/-O0不优化
-O1/2/3/s按链接属性优化,优化未使用的 static 链接属性的变量和函数。可以通过 __attribute__((used)) 抑制该优化。
-Wl,--gc-sections按未使用的变量、函数、文件进行优化,默认优化未使用变量(-fdata-sections)和未使用文件,通过指定 (-ffunction-sections)选择未优化函数。对于 Data 可以使用 KEEP 抑制该优化。

本文由 Jay 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处,点赞4

还不快抢沙发

添加新评论