H5W3
当前位置:H5W3 > 其他技术问题 > 正文

通过帧指针 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 PC
0x0000000100a27c3c
(lldb) dis -s 0x0000000100a27c3c
swiftDemo`Dog.eat():
0x100a27c3c <+20>: ldp    x29, x30, [sp], #0x10
0x100a27c40 <+24>: ret
 

使用FP实现

首先拿到x29(FP)寄存器所存地址,即当前方法的栈底:

(lldb) register read x29
fp = 0x000000016f3dd620
 

然后加8个字节得到父方法的x30(LR)(链接寄存器)之前所存的地址:

(lldb) memory read 0x000000016f3dd620+0x8
0x16f3dd628: 3c 7c a2 00 01 00 00 00 70 d7 3d 6f 01 00 00 00  <|......p.=o....
 

读取0x16f3dd628内容,可得父方法LR所存地址为0x0000000100a27c3c

(lldb) dis -s 0x0000000100a27c3c
swiftDemo`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

本文地址:H5W3 » 通过帧指针 FP进行方法回溯

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址