通过帧指针 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

以上是 通过帧指针 FP进行方法回溯 的全部内容, 来源链接: utcz.com/a/24471.html

回到顶部