美文网首页
IOS之二进制重拍

IOS之二进制重拍

作者: steveCheng | 来源:发表于2022-09-16 09:26 被阅读0次

原理

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文件

相关文章

网友评论

      本文标题:IOS之二进制重拍

      本文链接:https://www.haomeiwen.com/subject/sscdortx.html