背景

在开发一个项目时,使用了非常多的第三方.a静态库文件,导致编译出的可执行文件非常大。这样一是占用ROM空间,二是会导致程序启动加载速度变慢(项目对启动时间非常敏感)。其实,这些静态库中的函数,并非所有都有调用,项目只使用了其中小部分。这种情况下,gcc的“-Wl,–gc-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是否带来区别:

$ 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. jvm 参数 -verbose:gc 和 -XX:+PrintGC 的区别?

    文章目录 一.日志内容 1.1. `-verbose:gc` 参数: 1.2. `-XX:+PrintGC` 参数: 二.官方说明 2.1.`-verbose:gc` 是 稳定版本 2.2.`-XX: ...

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

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

最新文章

  1. webpack配置alias别名设置
  2. linux 硬链接 软链接 简介
  3. 庆祝Dojo中文博客成为CSDN博客专家!
  4. 「Apollo」class DescriptorBase(metaclass=DescriptorMetaclass)
  5. 微软为何痛失移动操作系统?
  6. PyTorch | torch.randperm()使用方法
  7. Spring+Quartz实现定时任务的配置步骤
  8. 计算机上缺少vsix安装程序,使用MSI安装程序部署VSIX
  9. html透明背景字体不透明,css3背景透明,文字不透明
  10. css实现方框内打勾
  11. 由WMT机器翻译双向夺冠看搜狗语音交互布局
  12. 从拼多多解析社交电商
  13. python代码画猪头_如何用python画猪头
  14. ASO 相似测试
  15. 【数字图像处理】秒懂傅里叶变换,仅需此文
  16. 4章 RxJava基本响应类型
  17. 计算机控制篮球,【精品课程设计】计算机控制技术弱电课程之篮球比赛计时计分器doc.doc...
  18. WPBC乳腺癌数据集建模(Ⅱ)—预后诊断模型建立
  19. module ‘dlib‘ has no attribute ‘get_face_chips‘
  20. Java异常处理的简单总结+文件操作

热门文章

  1. js-09二级联动购物车案例
  2. Java实现将文件(图片)上传到七牛云对象存储,并实现下载和删除功能
  3. 5个适合新手练习的Python刷题网站
  4. 编程之美-中国象棋将帅问题
  5. VUE + Element-ui 按住Shift实现多选
  6. java mp3 头信息_java读取mp3文件头信息
  7. 浏览器支持的视频和音频格式
  8. Erlang并发编程(五) --- bigwig
  9. 天玑9000和高通骁龙870哪个好
  10. 基于订单号可重入的交易系统接口设计探讨