IOS之二进制重拍

原理

App包数据并不是在启动的时候一次全部加载到内存中的,而是类似于懒加载的方式,以每页16KB的数据进行分页加载。启动的时刻,也是缺页加载次数最多的时刻。因为启动用到的类和方法,并不是全部集中在某几页数据中,而是根据编译顺序,分散到不确定的分页数据中。我们做二进制重拍,也就是要让启动用到的函数,集中到最前边的几张表中,减少分页加载的次数,也就节约了启动时间。

那么为什么减少分页加载的次数,可以节省启动时间呢?

这是因为,每页数据加载到内存中,还需要进行重绑定的过程,因为ASLR(地址空间布局随机化),每次启动后指针地址值并不是MachO中编译后的地址,还需要加上这个随机偏移地址,也就是rebase(重绑定)的过程。启动时刻加载的分页越多,重绑定的地址也就越多,拖慢了应用的启动时间。

查看排列顺序

① 打开编译配置 Build Settings —> Write Link Map File —> 修改成 YES。

② 打开编译后生产的 .app 文件,在上两层目录找 Intermediates.noindex

Intermediates.noindex/xxx(项目根目录名称).build/Debug-iphoneos/xxx(项目根目录名称).build/xxx-LinkMap-normal-arm64.txt

这个txt就是默认的排列顺序,我们配置.order后,这个文件的排列顺序会按照.order给定的顺序编译后排序。

配置.order 文件

打开 Build Setting,搜索 Order File 配置生成的.order文件路径。

.order里主要写启动时调用的方法(获取在后面会说)


 利用插桩获取重排符号

注意的是:获取到符号表后,以下配置我们就可以删除掉了。

插桩获取符号可以参考LLVM官网的介绍。

原理就是在每一个OC的方法、函数、block 的汇编代码中,插入一条__sanitizer_cov_trace_pc_guard汇编指令,读取pc寄存器的指令指针,回调到我们自己添加的C函数__sanitizer_cov_trace_pc_guard,然后根据指针恢复出OC的方法名、函数名、block。

配置pc寄存器跟踪配置

Build Settings —> Other C Flags —> 添加 -fsanitize-coverage=trace-pc-guard


引入插桩函数在AppDelegate里面

#include <stdint.h>

#include <stdio.h>

#include <sanitizer/coverage_interface.h>

#import <dlfcn.h>

#import <libkern/OSAtomic.h>

/*

 - start:起始位置

 - stop:并不是最后一个符号的地址,而是整个符号表的最后一个地址,最后一个符号的地址=stop-4(因为是从高地址往低地址读取的,且stop是一个无符号int类型,占4个字节)。stop存储的值是符号的

 */

void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,

                                                    uint32_t*stop) {

    staticuint64_tN;

    if(start == stop || *start)return;

    printf("INIT: %p - %p\n", start, stop);

    for(uint32_t*x = start; x < stop; x++) {

        *x = ++N;

    }


}

/*

 可以全面hook方法、函数、以及block调用,用于捕捉符号,是在多线程进行的,这个方法中只存储pc,以链表的形式


 - guard 是一个哨兵,告诉我们是第几个被调用的

 */

void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {

//    if (!*guard) return;//将load方法过滤掉了,所以需要注释掉



    //获取PC

    /*

     - PC 当前函数返回上一个调用的地址

     - 0 当前这个函数地址,即当前函数的返回地址

     - 1 当前函数调用者的地址,即上一个函数的返回地址

    */

    void *PC = __builtin_return_address(0);


    Dl_infoinfo;

    dladdr(PC, &info);

    printf("%s \n", info.dli_sname);

}

然后把打印出来的方法写入到order文件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容