写代码的过程难免会出现存在未用代码的情况 (一般是未用到的函数)
尤其在基础组件的编码过程中(有些函数不会被调用)
如何让生成的二进制可执行文件不包含冗余信息呢?
情况是这样的,有一个基础组件,有六组函数,文件就一个 xxx.h 和 xxx.c
然后编译成 xxx.o (gcc -c xxx.c)
(37K 的 xxx.o)
然后主程序用到其中的一组函数,于是需要include xxx.h
并且链接时需要链接到xxx.o (gcc -o main main.c xxx.o 编译链接一起了)
(44K 的 main,实际只用了组件的很小一部分)
后来发现main程序(二进制可执行文件)特别大,因为这个程序包含了全部六组的函数的代码
可以使用nm命令来查看 可以清楚的看到全部函数都被链接进去了
(nm用来列出目标文件的符号清单)
(箭头指向的才是使了的函数,这个图有误,但无关痛痒)
原来gcc会直接链接整个部分而不管你使用与否
使用 readelf查看生成的 xxx.o文件里面的 section(段)信息
全部函数和数据之类都是放到一个段(section)的
而链接操作以section作为最小的处理单元,只要一个section中有某个符号被引用,该section就会被放入output中。于是全部代码都被编译到了main中。
怎样解决这个问题呢?查阅了gcc的文档后
发现有两个参数可以使用
一个是 -ffunction-sections (为每个function函数分配独立的section)
另一个是 -fdata-sections (为每个data item数据项分配独立的section)
现在使用 gcc -c -ffunction-sections -fdata-sections xxx.c 编译
(xxx.o的文件尺寸明显变大了,因为段多了)
此时再查看 section信息
段已经多得显示不全了(除了接口函数外,还有一系列内部函数)
然后链接的时候使用 -Wl,--gc-sections
-Wl,的意思是将后面的内容传递给链接器
--gc-sections是链接器参数,不链接未使用的section
现在使用 gcc -Wl,--gc-sections -o main main.c xxx.o 来编译&链接
现在main的大小已经明显变小了(44K -> 18K)
再用 nm命令查看
原来的符号表已经少了很多了(清除了无用的)
既然可以这样,为啥默认不采用这种方式呢?
有人说每个函数分个段,程序执行的时候段间跳转,多慢啊
应该不会的,可以使用readelf读取main的信息
可以清楚的看到并没有很多的(冗余)段,代码主体还是在一起的
至于这个参数, gcc的 官方文档如下
大概意思就是:在目标文件中每个函数、数据项使用独立的段,段名由函数、数据项名决定。使用这个选项可以提高指令空间的利用率。大多数使用ELF目标文件格式和SPARC处理器运行Solaris2有链接器支持它。AIX将来可能支持。当你使用这个时,汇编器和链接器会产生更大的目标文件,并且处理得更慢。你也不能使用gprof(一个性能分析工具)在你用了这个选项后,而且如果同时用了-g选项,调试的时候可能会产生问题。
官方文档就只说明了这些,至于有没有其他的潜在问题,还得看gcc实现了吧。
在 Release版本中,使用这个还是有明显的好处的。