返回

Ftrace 钩取 arm64 内核函数时如何避免死循环?

Linux

使用 Ftrace 钩取 arm64 内核函数:如何避免死循环?

引言

Ftrace 是 Linux 内核强大的调试工具,允许用户钩取和跟踪内核函数执行。在 arm64 架构中使用 Ftrace 时,可能会遇到死循环问题。本文将分析这个问题并提供解决方案,以帮助读者成功地使用 Ftrace。

问题

在 arm64 Linux 5.7 中钩取 kmem_cache_alloc 等内核函数时,钩取的函数总是陷入死循环。即使使用 ftrace_set_filter_ip 来阻止死循环,也无法奏效。

原因分析

指令差异:

在 arm64 架构中,Ftrace 使用 BRANCH_TO 指令来钩取函数,而 BRANCH_TO 指令的大小为 4 个字节。与 x86-64 中的 MCOUNT 指令不同,MCOUNT 指令的大小为 5 个字节。

地址错误:

在提供的代码中,钩取函数的地址被设置为 hook.address + MCOUNT_INSN_SIZE,即钩取函数的地址加上 5 个字节。但在 arm64 中,钩取函数的地址应该加上 4 个字节,而不是 5 个字节。

解决方案

要解决这个问题,我们需要将钩取函数的地址设置为 hook.address + BRANCH_INSN_SIZE,即钩取函数的地址加上 4 个字节。

代码修改:

struct ftrace_hook hook;
hook.address = kallsyms_lookup_name(hook.name);

// BRANCH_INSN_SIZE 为 4
*((unsigned long*)&original_do_fork) = hook.address + BRANCH_INSN_SIZE;

fh_ftrace_thunk:

static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip,
                                    struct ftrace_ops *ops, struct pt_regs *regs) {
    if (!within_module(parent_ip, THIS_MODULE)) regs->pc = (unsigned long)hook->function;
}

结论

使用 Ftrace 钩取 arm64 内核函数时,需要考虑 BRANCH_TO 指令的大小为 4 个字节。确保正确设置钩取函数的地址可以防止死循环并允许成功钩取内核函数。

常见问题解答

1. 为什么在 arm64 架构中 Ftrace 的指令大小不同?

不同的指令集架构(ISA)有不同的指令大小,以适应各自的体系结构设计。

2. 除了死循环之外,Ftrace 在 arm64 中还会遇到其他问题吗?

取决于特定的场景和内核版本,Ftrace 可能会遇到其他问题,例如未解析的符号或与特定硬件的兼容性问题。

3. 如何确定要使用的 Ftrace 指令大小?

可以通过检查内核头文件或参考 Ftrace 文档来确定特定架构的 Ftrace 指令大小。

4. 除了 kmem_cache_alloc,还可以使用 Ftrace 钩取哪些其他内核函数?

Ftrace 可以钩取任何内核函数,包括系统调用、内核调度程序和 I/O 操作。

5. Ftrace 钩取有哪些潜在的用途?

Ftrace 钩取对于性能分析、调试和故障排除非常有用。它允许用户跟踪函数调用、测量执行时间和识别代码瓶颈。