通过帧指针 FP进行方法回溯
背景
在之前进行app启动优化,使用静态插桩时需要在插入方法中拿到父方法,在看完冬瓜为什么使用汇编可以 Hook objc_msgSend(上)- 汇编基础这篇文章后想使用帧指针FP
来验证下方法回溯。
插桩方法
在插桩时会用到下面这个方法:
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {// If initialization has not occurred yet (meaning that guard is uninitialized), that means that initial functions like +load are being run. These functions will only be run once anyways, so we should always allow them to be recorded and ignore guard
if (sStopCollecting || (!(*guard) && sInitDidOccur)) {
return;
}
*guard = 0;
void *pointer = __builtin_return_address(0);
PointerNode *node = malloc(sizeof(PointerNode));
*node = (PointerNode){pointer, NULL};
OSAtomicEnqueue(sQueue, node, offsetof(PointerNode, next));
}
其中方法
void *pointer = __builtin_return_address(0);
根据传参可以得到各祖先方法,传入0
可以得到父方法。
创建一个结构体
struct Dog {func eat() {
}
}
当调用eat()
时,因为已被插桩所以会先调用__sanitizer_cov_trace_pc_guard
,然后查看PC
中的方法正是eat()
:
(lldb) po PC0x0000000100a27c3c
(lldb) dis -s 0x0000000100a27c3c
swiftDemo`Dog.eat():
0x100a27c3c <+20>: ldp x29, x30, [sp], #0x10
0x100a27c40 <+24>: ret
使用FP实现
首先拿到x29
(FP)寄存器所存地址,即当前方法的栈底:
(lldb) register read x29fp = 0x000000016f3dd620
然后加8个字节得到父方法的x30
(LR)(链接寄存器)之前所存的地址:
(lldb) memory read 0x000000016f3dd620+0x80x16f3dd628: 3c 7c a2 00 01 00 00 00 70 d7 3d 6f 01 00 00 00 <|......p.=o....
读取0x16f3dd628
内容,可得父方法LR
所存地址为0x0000000100a27c3c
(lldb) dis -s 0x0000000100a27c3cswiftDemo`Dog.eat():
0x100a27c3c <+20>: ldp x29, x30, [sp], #0x10
0x100a27c40 <+24>: ret
即对应eat()
方法。
以上原理参考:
图片引自:@xi_lin
获取任意线程调用栈的那些事
Improving App Performance with Order Files桩
为什么使用汇编可以 Hook objc_msgSend(上)- 汇编基础
SanitizerCoverage
以上是 通过帧指针 FP进行方法回溯 的全部内容, 来源链接: utcz.com/a/24471.html