Sam在程序开发中,经常要用到多层库嵌套。
Sam常在想,一些底层库的符号(symbols),在上层库乃至应用程序中并非全部要使用到。如果这些符号(symbols)实现一层层报喊道上一层库中,并最终添加到应用程序中,这不是既增大了应用程序大小,也造成运行速度的降低?
记得GCC编译选项中有解决这个问题的方法。于是查找之。
0. 总体思想:
网络上有人建议如下设置:
1. 编译阶段使用:
-ffunction-sections
-fdata-sections
2. 链接阶段使用:
-Wl,--gc-sections
在GCC,LD官方文档中如下讲解:
-ffunction-sections
-fdata-sections
Place each function or data item
into its own section in the output file if the target supports
arbitrary sections. The name of the function or the name of the
data item determines the section's name in the output file.
Use these options on systems where the linker can perform
optimizations to improve locality of reference in the instruction
space. Most systems using the ELF object format and SPARC
processors running Solaris 2 have linkers with such optimizations.
AIX may have these optimizations in the future.
Only use these options when there are significant benefits from
doing so. When you specify these options, the assembler and linker
will create larger object and executable files and will also be
slower. You will not be able to use gprof on all
systems if you specify this option and you may have problems with
debugging if you specify both this option and -g.
--gc-sections
--no-gc-sections
Enable garbage collection of unused input sections. It is
ignored on targets that do not support this option. The default
behaviour (of not performing this garbage collection) can be
restored by specifying `--no-gc-sections' on the command
line.
`--gc-sections' decides which
input sections are used by examining symbols and relocations. The
section containing the entry symbol and all sections containing
symbols undefined on the command-line will be kept, as will
sections containing symbols referenced by dynamic objects. Note
that when building shared libraries, the linker must assume that
any visible symbol is referenced. Once this initial set of sections
has been determined, the linker recursively marks as used any
section referenced by their relocations. See `--entry' and
`--undefined'.
This option can be set when doing a partial link (enabled with
option `-r'). In this case the root of
symbols kept must be explicitly specified either by an
`--entry' or `--undefined'
option or by a ENTRY command in the linker script.
因为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(符号)了。
总体思想就是这样,Sam决定以验证之。
1. 验证过程:
Sam决定以Hi3716C平台中,Graphics_Base,libSTBUtils,3D_Example_HIGO此层次结构为例,研究之。
Graphics_Base是个底层库,架设在HiAPI之上,Sam当时选择将其编译为静态库。
libSTBUtils则是中间层,它架设在Graphics_Base层之上,作为3D应用程序的支持库,Sam选择其为动态库。
3D_Example_HIGO是个3D应用程序。依赖于libSTBUtils.so.
首先于Graphics_Base开始。它由多个.c文件组成。最终编译为静态库---libGraphics_Base.a
1.1 : 静态库中的分别:
即:产生静态库时,是否使用-ffunction-sections,
-fdata-sections产生的不同后果。
1.1.1: 不使用-ffunction-sections,
-fdata-sections
#arm-hisiv200-linux-gcc -c -O2 ...
#arm-hisiv200-linux-ar rv libGraphics_Base.a $?
此时,我们会希望看到以下2中信息:
1. .a中包含的符号。
2. .a中包含的sections.
首先看符号:
#arm-hisiv200-linux-readelf -s
libGraphics_Base.a
其次看section:
#arm-hisiv200-linux-readelf -t
libGraphics_Base.a
结果如下:
#arm-hisiv200-linux-readelf -s
libGraphics_Base.a
以下是节选:
140:
00000000 160
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_SetVdecAttr
141:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_GetAttr
142:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_SetAttr
143:
00000000 0 NOTYPE GLOBAL DEFAULT UND
puts
144:
00000000 0 NOTYPE GLOBAL DEFAULT UND
printf
145:
00000000 0 NOTYPE GLOBAL DEFAULT UND
_GLOBAL_OFFSET_TABLE_
146:
000000a0 168
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_SetVdecAdvAt
147:
00000148 1432
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_SetAdecAttr
148:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HIADP_HDMI_SetAdecAttr
149:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_SND_SetSampleRate
150:
00000004 1024 OBJECT GLOBAL
DEFAULT COM u8DecOpenBuf
151:
00000000 0 NOTYPE GLOBAL DEFAULT UND
__aeabi_uidivmod
152:
000006e0 264
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_PlayAud
153:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_Stop
154:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_Start
155:
000007e8 524
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_PlayProg
156:
000009f4 504
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_Create
157:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_GetDefaultC
158:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_Create
159:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_ChnOpen
160:
00000bec 220
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_RegADecLib
161:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_RegisterAco
162:
00000cc8 24
FUNC GLOBAL
DEFAULT 1
HIADP_AVPlay_Init
163:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_UNF_AVPLAY_Init
164:
00000ce0 56
FUNC GLOBAL
DEFAULT 1
HIADP_SLIC_Close
165:
00000000 0 NOTYPE GLOBAL DEFAULT UND
close
166:
00000d18 92
FUNC GLOBAL
DEFAULT 1
HIADP_AIAO_DeInit
167:
00000000 0 NOTYPE GLOBAL DEFAULT UND
HI_MPI_AI_DisableChn
168:
00000000