android studio下,如何减少编译生成的jni动态库的大小。

来源:互联网 发布:万圣节照相软件 编辑:程序博客网 时间:2024/06/06 02:03

用androidstudio编译jni生成动态库发现比原先的要大。

(1)用readelf -SW "动态库名"可以看到动态库里的信息;发现有需要.debug信息段。

(2)用命令arm-linux-androideabi-strip --strip-all "动态库名";对动态库中的.debug信息进行裁剪。

(3)在android studio中的Application,mk选项中添加  APP_OPTIM := release(release版本);debug为debug版本。


转:如何减小动态库大小。

ELF文件类型:

ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。它自最早在 System V 系统上出现后,被 xNIX 世界所广泛接受,作为缺省的二进制文件格式来使用。可以说,ELF是构成众多xNIX系统的基础之一。

ELF文件有三种类型:

  • 可重定位的对象文件(Relocatable file)

    由汇编器汇编生成的 .o 文件

  • 可执行的对象文件(Executable file)

    可执行应用程序

  • 可被共享的对象文件(Shared object file)

    动态库文件,也即 .so 文件

ELF文件内容

  • .text: 已编译程序的机器代码。
  • .rodata: 只读数据,比如printf语句中的格式串和开关(switch)语句的跳转表。
  • .data: 已初始化的全局C变量。局部C变量在运行时被保存在栈中,既不出现在.data中,也不出现在.bss节中。
  • .bss: 未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率在:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。
  • .symtab: 一个符号表(symbol table),它存放在程序中被定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过-g选项来编译一个程序,得到符号表信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的表目。
  • .rel.text: 当链接噐把这个目标文件和其他文件结合时,.text节中的许多位置都需要修改。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非使用者显式地指示链接器包含这些信息。
  • .rel.data: 被模块定义或引用的任何全局变量的信息。一般而言,任何已初始化全局变量的初始值是全局变量或者外部定义函数的地址都需要被修改。
  • .debug: 一个调试符号表,其有些表目是程序中定义的局部变量和类型定义,有些表目是程序中定义和引用的全局变量,有些是原始的C源文件。只有以-g选项调用编译驱动程序时,才会得到这张表。
  • .line: 原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译驱动程序时,才会得到这张表。
  • .strtab: 一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。

例子:ndk编译时对libsails.so减肥:

linux g++编译结果

通过ndk编译,-O2选项,按照linux下gcc的选项,只要没有加-g选项,应该就没有调度信息了,如果我们是通过标准gcc来编译的话,可以看看elf文件内容:
99BAF2C5-3458-4AF3-9FF5-AD8CD135631F.png
他的大小是240KB。
如果是通过-O0选项的话,它的大小是800K:
B67E1D45-7F78-4F9B-BDD2-696B1FC97669.png
如果是通过-O0 -g选项,那么大小就是2M
4663345B-8665-4BA4-BC50-D369B84B5D84.png
我们可以通过readelf查看libsails.so其中的信息:
readelf -SW libsails.so:
96F7885D-9AFF-48EA-B5C7-BC3D828EFECE.png

对比不加-g和加了-g选项的elf文件,我们可以知道它增加了一些.debug_***的section:
.debug_aranges, .debug_info, .debug_abbrev, .debug_line, .debug_str, .debug_range,它们在调度时可以让我们定位到具体某一行代码。
其中我们可以看到这几个之中占用比较大的是.debug_info:0x9a85f(632.9KB), .debug_str:0x60c70(396KB),这几个占用了1M空间。正好是libsails从800k到2000K的大小变化;

而用NDK独立编译链来编译libsails.so,同时以-O0,不加-g选项:

3A79F7F0-BCF8-4D84-BD53-421106BF829F.png
可以发现它比linux标准g++的800k多出了好几倍,那么多在了哪里?
它的架构信息说明是armv5:
862C8341-0A24-40E0-9E59-0E94CBA02A5D.png
它section信息:
EA08AE6C-1206-4ADB-802F-2818ECE511D1.png
它的.text section的大小从0x26150(156k)变成了0x8e5b4(583K),增加了400k,并且很奇怪的是它增加了几几个.debug信息!!!!而明明我们没有用-g选项!
其中.debug_info占用了1.6M

通过strip –strip-debug选项去掉调试信息

如果我们通过strip去掉.debug信息
2ED968D0-EA6A-46E7-8700-54BDF5DB9957.png
7B7D6EB2-C4AF-47CD-8475-E5C28A90C223.png

通过strip -strip-all 去掉所有的符号表和重定位信息

也就是说,g++与ndk的g++编译出来的文件相比,除了debug信息之外,也要大600K,如果想要近一步减少大小,通过把它的.symtab也给去掉。
52DABCB9-591A-40A7-A886-5102AF66A9BA.png
A3728E9C-18C6-4537-B850-975595813439.png
减少到了900K,而此时的elf信息:
B93B155F-BF1C-449E-9058-F47B3BD9DF7A.png
它少了.symtab和.strtab两个section,.symtab就是符号表,当我们调试程序时,它能告诉我们在哪个函数中出错了.

至少为什么当我们在ndk中,没有指定-g而生成的so文件中确包含了debug信息就还不知道为什么,不过我们可以在生成libsails.so库时,通过g++的选项去掉它:
2CAB474B-EE80-4779-BB95-B8BD3F28FAB8.png
增加一个-s选项就可以了,它相当于我们在生成的库时strip —strip-all libsails.so的效果。不过这个选项现在在标准的g++中好像已经过期了。

当然通过-O1 -O2之类的选项,也可以减少包大小,下面通过-O2可以减少到630K:
80BAB53B-52EF-4300-BAB7-0B1FFDF4BE5E.png



0 0