LinkMap安装包瘦身

LinkMap是什么

LinkMap的输出是一个纯文本格式的文件,里面包含重要的编译信息及报错信息,这也是Apple用来分析你的应用的主要方式。


内容剖析

文件内容如下:
Image

Path 是工程文件路径地址。
Arch 是编译的架构信息,上图是arm64架构,其实Xcode还可以构建armv7,i386等架构的内容。
Object files 是整个工程文件的编译文件列表,其中包含了framework,lib.a等动、静态库,以及c++相关的文件内容。同时我们也可以称它为Mach-O文件


顺着LinkMap文件接着往下看,我们会找到如下内容:

Image


Sections

__TEXT段节名含义

1. __text: 代码节,存放机器编译后的代码
2. __stubs: 用于辅助做动态链接代码(dyld).
3. __stub_helper:用于辅助做动态链接(dyld).
4. __objc_methname:objc的方法名称
5. __cstring:代码运行中包含的字符串常量,比如代码中定义`#define kGeTuiPushAESKey        @"DWE2#@e2!"`,那DWE2#@e2!会存在这个区里。
6. __objc_classname:objc类名
7. __objc_methtype:objc方法类型
8. __ustring:
9. __gcc_except_tab:
10. __const:存储const修饰的常量
11. __dof_RACSignal:
12. __dof_RACCompou:
13. __unwind_info:

__DATA段节名含义

1. __got:存储引用符号的实际地址,类似于动态符号表
2. __la_symbol_ptr:lazy symbol pointers。懒加载的函数指针地址。和__stubs和stub_helper配合使用。具体原理暂留。
3. __mod_init_func:模块初始化的方法。
4. __const:存储constant常量的数据。比如使用extern导出的const修饰的常量。
5. __cfstring:使用Core Foundation字符串
6. __objc_classlist:objc类列表,保存类信息,映射了__objc_data的地址
7. __objc_nlclslist:Objective-C 的 +load 函数列表,比 __mod_init_func 更早执行。
8. __objc_catlist: categories
9. __objc_nlcatlist:Objective-C 的categories的 +load函数列表。
10. __objc_protolist:objc协议列表
11. __objc_imageinfo:objc镜像信息
12. __objc_const:objc常量。保存objc_classdata结构体数据。用于映射类相关数据的地址,比如类名,方法名等。
13. __objc_selrefs:引用到的objc方法
14. __objc_protorefs:引用到的objc协议
15. __objc_classrefs:引用到的objc类
16. __objc_superrefs:objc超类引用
17. __objc_ivar:objc ivar指针,存储属性。
18. __objc_data:objc的数据。用于保存类需要的数据。最主要的内容是映射__objc_const地址,用于找到类的相关数据。
19. __data:暂时没理解,从日志看存放了协议和一些固定了地址(已经初始化)的静态量。
20. __bss:存储未初始化的静态量。比如:`static NSThread *_networkRequestThread = nil;`其中这里面的size表示应用运行占用的内存,不是实际的占用空间。所以计算大小的时候应该去掉这部分数据。
21. __common:存储导出的全局的数据。类似于static,但是没有用static修饰。比如KSCrash里面`NSDictionary* g_registerOrders;`, g_registerOrders就存储在__common里面

Symbols

后面的内容是代码、函数、类相关的详细信息。
Address是地址信息。
Size是所占安装包大小。


怎么获取LinkMap文件

Image
我们只需要在编译设置里面把Write Link Map File设置为YES,就可以了。
上面的Path to Link Map File就是文件输出地址。


Mach-o安装包检测

  • 在LinkMap文件中统计所有的.o文件。
  • 通过的Mach-O文件序列号,统计函数、变量等安装包Size。

什么是序列号呢?如下图中的3865这个数字,这个就是序列号。
Image
正如图中,把所有序列号为3865的内容的Size相加,就可以得出每个Mach-O文件的安装包大小。

  • 把每个Mach-O文件Size相加,就可以得出安装包的__TEXT段的详情信息。
  • 把不同app版本的Mach-O文件Size进行比较,就可以得出安装包变化情况。

更多说明
Mach-O文件包含(.m.h)以及(.c.cpp)等文件,一个.m文件中可能定义了多个类,那么Mach-O文件Size就需要把所有包含的类都统计在内。


无用函数检测

  • 在LinkMap中找出所有使用的函数(类名+函数名+安装包Size)。下面是Python正则表达式内容:
re.search(r'[-+]\[\w+\s[\w:]+\]',line)
  • 对找到的函数(进行白名单过滤,过滤掉如下方法),得到函数列表A
系统方法、代理方法、JSAPI等脚本内容
  • 可执行文件中找出工程中所有使用的函数,得到函数列表B
otool -V -s __DATA __objc_selrefs 项目.app/Frameworks/framework名称.framework
/framework名称 | open -f
  • 把在函数列表A内,但不在函数列表B内的函数记录下来,这些就是无用函数

说明
函数列表B如下图:
Image

可执行文件指工程文件编译出.app文件中的可运行文件,如果代码已经封装成了Framework,那么其地址可能是项目.app/Frameworks/framework名称.framework/framework名称,如果没有封装成Framework,其地址可能是项目.app/项目,其中项目指是app名称。

__objc_selrefs已经在上面的内容提到,指的是 :

引用到的objc方法

总结:

当然,我们也可以再根据每个函数的安装包大小进行排序。这里最难的地方是白名单过滤,需要维护一份有用的白名单列表。


无用类检测

  • 可执行文件中找出所有声名的类
otool -V -s __DATA __objc_classlist 项目.app/Frameworks/framework名称.framework
/framework名称 | open -f
  • 可执行文件中找出所有使用的类
otool -V -s __DATA __objc_classrefs 项目.app/Frameworks/framework名称.framework
/framework名称 | open -f
  • 把在声名的类但不在使用的类中的类记录下来,得出无用类地址
  • LinkMap文件中找出地址对应的类名。
  • 白名单过滤类名(过滤如下类,主要是系统类,以及脚本、请求回包类等)。
UI、AP、JS、REQ、RSP等相关类。

说明
__objc_classlist已经在上面的内容提到,指的是(结果如下图) :

objc类列表,保存类信息,映射了__objc_data的地址

Image


__objc_classrefs已经在上面的内容提到,指的是 (结果如下图):

引用到的objc类

Image


备注

otool内容参考。下面是相关脚本文件下载链接:
无用函数扫描脚本.py
无用类扫描脚本.py

python脚本文件中引用到了xlwt,请参考安装方法

2018-06-14 17:11100