背景

在开发一个项目时,使用了非常多的第三方.a静态库文件,导致编译出的可执行文件非常大。这样一是占用ROM空间,二是会导致程序启动加载速度变慢(项目对启动时间非常敏感)。其实,这些静态库中的函数,并非所有都有调用,项目只使用了其中小部分。这种情况下,gcc的“-Wl,–gc-sections”参数,就非常有用。
补充说明:想要达到生成最终可执行文件,只链接.a库中用到的函数,需要在编译生成.a库时,就带有-ffunction-sections参数。

参数说明

英文原版的-Wl,–gc-sections解释说明如下:

6.3.3.2 Compilation options
The operation of eliminating the unused code and data from the final executable is directly performed by the linker.

In order to do this, it has to work with objects compiled with the following options: -ffunction-sections -fdata-sections.

These options are usable with C and Ada files. They will place respectively each function or data in a separate section in the resulting object file.

Once the objects and static libraries are created with these options, the linker can perform the dead code elimination. You can do this by setting the -Wl,–gc-sections option to gcc command or in the -largs section of gnatmake. This will perform a garbage collection of code and data never referenced.

If the linker performs a partial link (-r linker option), then you will need to provide the entry point using the -e / --entry linker option.

Note that objects compiled without the -ffunction-sections and -fdata-sections options can still be linked with the executable. However, no dead code elimination will be performed on those objects (they will be linked as is).

The GNAT static library is now compiled with -ffunction-sections and -fdata-sections on some platforms. This allows you to eliminate the unused code and data of the GNAT library from your executable.

大致说明如下:

  1. 在编译C、Ada源文件(C++也可以),在gcc/g++编译选项中增加-ffunction-sections-fdata-sections,在编译生成的.o目标文件中,会将每个函数或数据段,放在各种单独独立的section中;
  2. 在链接生成最终可执行文件时,如果带有-Wl,--gc-sections参数,并且之前编译目标文件时带有-ffunction-sections-fdata-sections参数,则链接器ld不会链接未使用的函数,从而减小可执行文件大小;
  3. 如果使用了-r的链接参数,来产生重定位的输出,需要显示的调用-e参数来指定程序入口。否则-Wl,--gc-sections不会生效。

关于GNU ld更多参数的说明,可以参考官网。

使用示例

测试程序section_test.c如下:

#include <stdio.h>void func_0(void) {printf("%s\n\r", __func__);
}void func_1(void) {printf("%s\n\r", __func__);
}void func_2(void) {printf("%s\n\r", __func__);
}void func_3(void) {printf("%s\n\r", __func__);
}void func_4(void) {printf("%s\n\r", __func__);
}void func_5(void) {printf("%s\n\r", __func__);
}void func_6(void) {printf("%s\n\r", __func__);
}void func_7(void) {printf("%s\n\r", __func__);
}int main(void) {func_0();return 0;
}

Makefile如下:

.PHONY: test_sections test_normalall: test_sections test_normaltest_sections:gcc -ffunction-sections -fdata-sections -c section_test.cgcc -Wl,--gc-sections -o $@ section_test.omv section_test.o with_section.otest_normal:gcc -c section_test.cgcc -o $@ section_test.omv section_test.o no_section.oclean:rm -rf *.o main_sections main_normal

执行make后,编译生成2个.o文件和2个可执行程序:no_section.owith_section.otest_normaltest_sections

首先,通过ls -l查看可执行文件大小。可以看到,没有链接未使用函数的test_sections稍微小一点。如果工程中使用的静态库.a多,且未使用的函数也多的话,效果会更显著:

-rwxr-xr-x 1 user user 8864 9月  10 14:40 test_normal
-rwxr-xr-x 1 user user 8272 9月  10 14:40 test_sections

然后,查看.o目标文件,在gcc编译选项-ffunction-sections -fdata-sections是否带来区别:

  • 没有-ffunction-sections -fdata-sections选项的目标文件,只有1个textrodata
  • 带有-ffunction-sections -fdata-sections选项的目标文件,分别有text.func_0text.func_7,以及8个rodata.__func__。说明gcc在编译生成目标文件时,将每个函数或数据段,放在各种单独独立的section
$ readelf -t no_section.o
There are 13 section headers, starting at offset 0xa08:Section Headers:[Nr] NameType              Address          Offset            LinkSize              EntSize          Info              AlignFlags[ 0] NULL                   NULL             0000000000000000  0000000000000000  0[ 1] .textPROGBITS               PROGBITS         0000000000000000  0000000000000040  0[ 2] .rela.textRELA                   RELA             0000000000000000  0000000000000670  10[ 3] .dataPROGBITS               PROGBITS         0000000000000000  0000000000000148  0[ 4] .bssNOBITS                 NOBITS           0000000000000000  0000000000000148  0[ 5] .rodataPROGBITS               PROGBITS         0000000000000000  0000000000000148  0
$ readelf -t with_section.o
There are 38 section headers, starting at offset 0xce8:Section Headers:[Nr] NameType              Address          Offset            LinkSize              EntSize          Info              AlignFlags[ 0] NULL                   NULL             0000000000000000  0000000000000000  0[ 1] .textPROGBITS               PROGBITS         0000000000000000  0000000000000040  0[ 2] .dataPROGBITS               PROGBITS         0000000000000000  0000000000000040  0[ 3] .bssNOBITS                 NOBITS           0000000000000000  0000000000000040  0[ 4] .rodataPROGBITS               PROGBITS         0000000000000000  0000000000000040  0[ 5] .text.func_0PROGBITS               PROGBITS         0000000000000000  0000000000000045  0[ 6] .rela.text.func_0RELA                   RELA             0000000000000000  0000000000000808  35[ 7] .text.func_1PROGBITS               PROGBITS         0000000000000000  0000000000000064  0[ 8] .rela.text.func_1RELA                   RELA             0000000000000000  0000000000000850  35[ 9] .text.func_2PROGBITS               PROGBITS         0000000000000000  0000000000000083  0[10] .rela.text.func_2RELA                   RELA             0000000000000000  0000000000000898  35[11] .text.func_3PROGBITS               PROGBITS         0000000000000000  00000000000000a2  0[12] .rela.text.func_3RELA                   RELA             0000000000000000  00000000000008e0  35[13] .text.func_4PROGBITS               PROGBITS         0000000000000000  00000000000000c1  0[14] .rela.text.func_4RELA                   RELA             0000000000000000  0000000000000928  35[15] .text.func_5PROGBITS               PROGBITS         0000000000000000  00000000000000e0  0[16] .rela.text.func_5RELA                   RELA             0000000000000000  0000000000000970  35[17] .text.func_6PROGBITS               PROGBITS         0000000000000000  00000000000000ff  0[18] .rela.text.func_6RELA                   RELA             0000000000000000  00000000000009b8  35[19] .text.func_7PROGBITS               PROGBITS         0000000000000000  000000000000011e  0[20] .rela.text.func_7RELA                   RELA             0000000000000000  0000000000000a00  35[21] .text.mainPROGBITS               PROGBITS         0000000000000000  000000000000013d  0[22] .rela.text.mainRELA                   RELA             0000000000000000  0000000000000a48  35[23] .rodata.__func__.2250PROGBITS               PROGBITS         0000000000000000  000000000000014d  0[24] .rodata.__func__.2254PROGBITS               PROGBITS         0000000000000000  0000000000000154  0[25] .rodata.__func__.2258PROGBITS               PROGBITS         0000000000000000  000000000000015b  0[26] .rodata.__func__.2262PROGBITS               PROGBITS         0000000000000000  0000000000000162  0[27] .rodata.__func__.2266PROGBITS               PROGBITS         0000000000000000  0000000000000169  0[28] .rodata.__func__.2270PROGBITS               PROGBITS         0000000000000000  0000000000000170  0[29] .rodata.__func__.2274PROGBITS               PROGBITS         0000000000000000  0000000000000177  0[30] .rodata.__func__.2278PROGBITS               PROGBITS         0000000000000000  000000000000017e  0

最后,再看一下,不带-Wl,–gc-sections参数生成的可执行文件,与带-Wl,–gc-sections参数生成的可执行文件的差别。很明显,在链接过程中,带-Wl,–gc-sections参数后,可执行文件仅联编了调用的函数。

$ readelf -a test_normal | grep func_55: 00000000000006c6    31 FUNC    GLOBAL DEFAULT   14 func_457: 000000000000064a    31 FUNC    GLOBAL DEFAULT   14 func_058: 0000000000000688    31 FUNC    GLOBAL DEFAULT   14 func_260: 0000000000000704    31 FUNC    GLOBAL DEFAULT   14 func_671: 00000000000006a7    31 FUNC    GLOBAL DEFAULT   14 func_372: 0000000000000669    31 FUNC    GLOBAL DEFAULT   14 func_173: 0000000000000723    31 FUNC    GLOBAL DEFAULT   14 func_774: 00000000000006e5    31 FUNC    GLOBAL DEFAULT   14 func_5
readelf -a test_sections | grep func_48: 000000000000064a    31 FUNC    GLOBAL DEFAULT   14 func_0

使用gcc参数-Wl,–gc-sections,不链接未用函数,减小可执行文件大小相关推荐

  1. gcc参数-Wl,–gc-sections,不链接未用函数,减小可执行文件大小

    背景 在开发一个项目时,使用了非常多的第三方.a静态库文件,导致编译出的可执行文件非常大.这样一是占用ROM空间,二是会导致程序启动加载速度变慢(项目对启动时间非常敏感).其实,这些静态库中的函数,并 ...

  2. 复杂命令行参数gcc的-Wl的含义,注意是字母l不是数字1

    复杂命令行参数:gcc的-Wl, -Wl,表示后面的参数将传给link程序ld(因为gcc可能会自动调用ld),如果后面的ld参数有空格,怎么传呢?比如想加一个-rpath /path? 下面几种方法 ...

  3. Linux平台Makefile文件的编写基础篇和GCC参数详解

    问:gcc中的-I.是什么意思....看到了有的是gcc -I. -I/usr/xxxxx..那个-I.是什么意思呢 最佳答案 答:-Ixxx 的意思是除了默认的头文件搜索路径(比如/usr/incl ...

  4. JVM基础系列第14讲:JVM参数之GC日志配置

    说到 Java 虚拟机,不得不提的就是 Java 虚拟机的 GC(Garbage Collection)日志.而对于 GC 日志,我们不仅要学会看懂,而且要学会如何设置对应的 GC 日志参数.今天就让 ...

  5. gcc参数 -i, -L, -l, -include

    gcc参数 -i, -L, -l, -include -i,-L,-l,-include -l和-L -l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢? ...

  6. GCC详解-gcc之-Wl选项

    1.介绍 -Wl后面的东西是作为参数传递给链接器ld的.比如: gcc -Wl,aaa,bbb,ccc 最后会被解释为: ld aaa bbb ccc 2.-Wl,-Map=xxx.txt 生成map ...

  7. 02_可执行文件生成过程和gcc参数介绍

    文章结构 可执行文件生成过程 1. 预处理器 2. 编译器 3. 汇编器 4. 链接器 gcc参数用法 语法 参数 可执行文件生成过程 对于一个源文件test.c生成可执行文件test.out的过程如 ...

  8. java verbose gc_jvm参数-verbose:gc和-XX:+PrintGC有区别?

    jvm调优,参数-verbose:gc和-XX:+PrintGC有什么具体的区别?还是说效果一样的,打印下了没发现什么差别. 参数1: -XX:+PrintGC -XX:+PrintGCDetails ...

  9. 【Java 虚拟机原理】垃圾回收算法 ( 设置 JVM 命令参数输出 GC 日志 | GC 日志输出示例 | GC 日志分析 )

    文章目录 一.设置 JVM 命令参数输出 GC 日志 二.GC 日志示例 三.GC 日志分析 一.设置 JVM 命令参数输出 GC 日志 在 IntelliJ IDEA 的启动参数中设置 -XX:+P ...

最新文章

  1. Spring 命名空间
  2. Unity3D架构之PureMVC
  3. python使用del保留字定义一个函数-Python使用什么保留字定义一个函数。
  4. 博弈入门(思想)HDkiki‘s game;
  5. JSP的9个内置对象-application
  6. rt-n18u usb3.0 linux ex,2.4G王者 华硕 AUSU RT-N18U 评测
  7. 转帖:由C++转向C#的几种对策
  8. 易筋SpringBoot 2.1 | 第廿一篇:SpringBoot的Mybatis生成工具Generator
  9. 2020牛客多校训练1 H Minimum-cost Flow(最小费用流)
  10. 高等代数 北大版 Page 17 推论 证明
  11. c++ 建立MFC应用程序
  12. LaTex缺少宏包问题
  13. 分数混合运算简便方法_分数混合运算和简便算法
  14. 如何用python爬取股票数据选股_用python爬取股票数据
  15. C - Reverse Polish Calculator
  16. 通过SqlDbx导出*.sql
  17. 一统大数据江湖,趣话图说“存算分离”武学心法
  18. p5405 [CTS2019]氪金手游
  19. 配置OSPF实现pc机互通小实验
  20. MySQL中的子查询(第八章)

热门文章

  1. 在电脑上写好的移动端页面,如何在手机上快速查看
  2. [HNOI2007]最小矩形覆盖(旋转卡壳)
  3. excel替换指定列的文本
  4. 实现全国行政区域结构化管理
  5. Fuchsia Friday:一切皆实体
  6. 项目mysql数据导入数据的Java程序
  7. 多模块项目-项目复制出现Module xx must not contain source root xx The root already belongs to module xx
  8. 乐视X520(乐2 全网通)一键刷机教程
  9. java 注解传值_java注解 - Mr.yang.localhost - 博客园
  10. 摆渡人 --如果命运是一条孤独的河流,谁会是你的灵魂摆渡人