二进制重排
缺页中断(pagefault)
clang插桩
这里需要用到 Tracing PCs文档.根据文档里面的提示,在Build Settings 里面搜索 Other C Flags 添加 -fsanitize-coverage=trace-pc-guard。 添加之后运行,发现报错,找不到符号___sanitizer_cov_trace_pc_guard_init和___sanitizer_cov_trace_pc_guard。问题是程序中并没有调用这个函数,那么说明就是添加标记后的回调函数。 按照文档添加这两个方法。 运行后发现还是报错找不到符号__sanitizer_symbolize_pc,那么就将这个函数去掉得到: 去掉函数后运行,发现运行成功了,并且打印如下: 这里的0x104da93d0是start,0x104da9408是 stop,那么这里存的是什么数据呢?打印start 和stop得到下图的结果。这里的数字代表着符号的个数。而stop往前四位里面的就是符号总共的个数,这里就是14个符号。
-fsanitize-coverage=trace-pc-guard
___sanitizer_cov_trace_pc_guard_init
___sanitizer_cov_trace_pc_guard
__sanitizer_symbolize_pc
0x104da93d0
start
0x104da9408
stop
符号的个数
14个符号
添加一个方法后重新运行,发现果然变成了f,也就是15个。 添加一个block看看是否可以被检测到。运行后发现也+1了,变成了10,也就是16。 到这里可以知道,___sanitizer_cov_trace_pc_guard_init里面可以得到符号的总个数,那么___sanitizer_cov_trace_pc_guard里面可以做什么呢?
f
15个
10
16
在___sanitizer_cov_trace_pc_guard里面加个断点后运行,发现进来了很多次,然后在点击屏幕触发touchesBegan后发现,___sanitizer_cov_trace_pc_guard也被调用了。并且发现是被touchesBegan调起的。 重新运行一下,发现___sanitizer_cov_trace_pc_guard依次被[ViewController load],main函数,[AppDelegate application:didFinishLaunchingWithOptions:]等方法调起,那么说明,这个就是启动的顺序(这里拦截的都是当前项目中的函数)。 这里可以知道,___sanitizer_cov_trace_pc_guard是hook一切的回调函数。那么___sanitizer_cov_trace_pc_guard是怎么做到的呢? 这里看到调用___sanitizer_cov_trace_pc_guard的方法的汇编代码 ,这里是touchesBegan。看到汇编代码这里当调用touchesBegan的时候,马上就调用了___sanitizer_cov_trace_pc_guard。那么也就是说只要添加了clang插桩的标记,那么编译器就会在所有方法,函数,block的代码的实现的边缘添加一句代码,也就是调用___sanitizer_cov_trace_pc_guard的代码。 那么应该如何获取符号以及顺序呢? 在___sanitizer_cov_trace_pc_guard里面__builtin_return_address可以拿到上一个函数的地址,那么有了这个地址,就有机会拿到这个函数符号的名字。 importdlfcn。 在___sanitizer_cov_trace_pc_guard里面根据函数的地址调用dladdr函数来获得函数的基本信息。其中结构体Dl_info有以下成员变量:
touchesBegan
[ViewController load]
main
[AppDelegate application:didFinishLaunchingWithOptions:]
__builtin_return_address
dlfcn
dladdr
函数的基本信息
dli_fname
dli_fbase
dli_sname
dli_saddr
这个方法其实有个坑点。 在___sanitizer_cov_trace_pc_guard里面打印线程,然后在viewDidLoad里面添加一个方法到子线程执行,
看到这里打印了线程number为6,那么就说明___sanitizer_cov_trace_pc_guard方法也会跑到子线程去执行,那么在存储数据的时候就会有多线程访问数据的情况,所以这里要用线程安全的方式去存储。
这里定义原子队列和定义符号结构体。 然后在___sanitizer_cov_trace_pc_guard里面创建结构体,并且使结构体入栈,这里会将node存入到symbolist的第一个地方,然后这个node的next属性会存下一个node的地址,形成一个链表。 接下来要将其取出来,就将node取出来,然后获得符号的名字。 运行后发现这里一直重复打印-[ViewController touchesBegan:withEvent:]。这是因为touchesBegan里面有循环,clang回对循环体进行拦截,拦截一次就添加一次,所以这里会一直重复打印-[ViewController touchesBegan:withEvent:]。 要解决这个问题,则需要将other c flag 里面-fsanitize-coverage=trace-pc-guard 改成 -fsanitize-coverage=func,trace-pc-guard 运行后发现不会死循环了,得到下面的结果。 由于是用栈来存储,所以列表是反的,这里要先把这个列表变成正的。还有就是这里的函数没有下划线,要给函数添加上下划线。 所以函数变成了这样: 这里看到有重复的符号,就要去重。 接下来写成文件。 生成文件后,点击 Add Additional Simulator,点开真机里面的Settings里面的 download console,放在想要的地方后选取生成的文件,右击选择显示包内容,点击AppData,点击tmp,看到 ls.order文件。 点击后可以看到这里符号表已经生成了。 到这里,就可以直接使用这个ls.order文件了。将这个文件拷贝到根目录下,然后打开 Write Link Map File后运行验证。 那么swift是否可以进行二进制重排呢? 创建一个swift文件并调用方法。 然后来到Build Settings 的 Other Swift Flags 添加 -sanitize=undefined 和 -sanitize-coverage=func。 运行后发现swift方法也打印了出来。
-[ViewController touchesBegan:withEvent:]
循环
循环体进行拦截
-fsanitize-coverage=func,trace-pc-guard
Add Additional Simulator
download console
AppData
tmp
ls.order
Write Link Map File
Build Settings
Other Swift Flags
-sanitize=undefined
-sanitize-coverage=func