eBPF Talk: 在 arm64 fentry 里手动做 backtrace
文章目录
对于 fentry
程序而言,在 arm64 上做 backtrace 不像 x86 上那么简单;第一步找到可用的 frame pointer 就很费劲了。
毕竟,找到 frame pointer 之后,就可以通过 frame pointer 逐级回溯,找到调用者的 IP 和 FP。
TL;DR 代码细节请看 pwru PR: Introduce two enhancements for func IP。
bpf R10 寄存器
在 x86 上,bpf R10 寄存器就是 frame pointer,可以直接用来做 backtrace。但在 arm64 上,R10 寄存器是 R25 寄存器而不是 R29 寄存器。
因此,在 fentry
里,该如何找到 frame pointer 呢?
trampoline
和 fentry
的栈分布
直接分析 trampoline
和 fentry
的栈分布,如下:
|
|
可以看到,bpf R10 寄存器跟 fentry
的 FP 之间隔着几个被调用者保存的寄存器。
因此,如果能够知道 fentry
里使用了几个被调用者保存的寄存器,就可以找到 FP。
但是,在 commit 5d4fa9ec5643 (“bpf, arm64: Avoid blindly saving/restoring all callee-saved registers”) 之前,fentry
需要保存的寄存器数量是确定的,是 5 或者 6 个(取决于 commit 66ff4d61dc12 (“bpf, arm64: Fix tailcall hierarchy”))。而在这个 commit 之后,fentry
里保存的寄存器数量由 fentry
程序本身决定,即 fentry
使用了多少被调用者保存的寄存器,就保存多少个。
有没有简单可行的办法来找到 FP 呢?
猜测 trampline
的 FP
如上图,trampoline
的 FP 保存于 trampoline
栈帧的底部,与 bpf prog 的 FP 相隔不远。
那么,假设 fentry
FP 加上一段偏移后,就是 trampoline
的 FP,该如何判断该 FP 就是 trampoline
的 FP 呢?
一个简单一些的办法是,判断读取到的 fp 在 fentry
的 FP 到 fentry
的 FP 加上 256 字节之间;如果是,那么大概率就是 trampoline
的 FP。
|
|
不是很准确,但能用。
如果要拿到 tracee IP 呢?
如上图,在拿到 trampoline
的 FP 之后,就可以直接读取 FP + 8 字节的位置,即可拿到 tracee 的 IP;该 IP 减去 12 字节,就是 tracee 的原始 IP。
详情可以参考 commit b2ad54e1533e (“bpf, arm64: Implement bpf_arch_text_poke() for arm64”)。
总结
在 arm64 上,通过 fentry
实现 backtrace 是比较困难的,但是通过一些技巧,还是可以实现的。
文章作者 Leon Hwang
上次更新 2024-12-16