“__kprobes”的使用及其工作原理是什么?

4

在参考Linux内核的内存模块时,有些函数对我来说不是很清楚。其中一个函数如下所示:

static inline int __kprobes notify_page_fault(struct pt_regs *regs)
{
int ret = 0;

/* kprobe_running() needs smp_processor_id() */
if (kprobes_built_in() && !user_mode_vm(regs)) {
    preempt_disable();
    if (kprobe_running() && kprobe_fault_handler(regs, 14))
        ret = 1;
    preempt_enable();
}

return ret;
} 

我对返回类型和函数名之间的 "__kprobes" 很困惑。当我查看 compiler.h 中 "__kprobes" 的初始化时,我发现如下:

/*Ignore/forbid kprobes attach on very low level functions marked by 
this attribute:*/
#ifdef CONFIG_KPROBES
# define __kprobes  __attribute__((__section__(".kprobes.text")))
#else
# define __kprobes
#endif

我知道在编译时,__kprobe 将被其定义部分所替代。

问题:

1.) 什么是__attribute__((__section__(".kprobes.text")))的意义?

2.) 当在 "function_name" 前使用它时,它在编译时和运行时都会做什么?

我阅读了关于kprobe的资料,发现与断点和回溯有关。 我对 kprobe 的理解是它可帮助调试器创建回溯和断点。请问能否用简单的语言解释一下它的工作原理,并纠正我的错误认识。

1个回答

9

简而言之

  1. __attribute__((__section__(".kprobes.text"))) 会将函数放置在一个单独的区域中,kprobes 在这个区域中无法找到该函数地址,从而避免了无限断点。
  2. 你必须在 "函数名" 前使用它,以将整个 "函数名" 符号放置在单独的区域中。

真实答案

kprobes(内核探测)是 Linux 内核动态跟踪机制。它允许您在几乎任何内核函数上插入断点、调用您的处理程序,然后继续执行。它通过运行时对内核映像进行所谓的内核探针/kprobe 的修补来工作 - 参见struct kprobe。这个探针将允许您将控制传递给您的处理程序,而该处理程序通常用于跟踪。

那么,在幕后发生了什么:

  • 定义要中断的地址和要传递引用的处理程序,创建您的struct kprobe
  • 使用register_kprobe注册探针。
  • 内核 kprobe 子系统从您的 probe 中查找地址。
  • 然后 kprobe:
    • 在给定地址处插入 CPU 指令中断(对于 x86,是int 3
    • 添加一些包装代码以保存上下文(寄存器等)
    • 添加更多的代码以帮助您访问函数参数或返回值。
  • 现在,当内核执行到达那个接受探测的地址时:
    • 进入 CPU 陷阱
    • 保存上下文
    • 通过notifier_call_chain将控制传递给您的处理程序
    • ......
    • 最后恢复上下文

这就是它的工作原理。正如您所看到的,它是一个真正巧妙而肮脏的技巧,但有些内核函数是如此低级别,以至于对它们进行探测毫无意义。notify_page_fault 就是其中之一 - 作为 notifier_call_chain 的一部分,在将控制传递给您的处理程序时使用。

因此,如果您在notify_page_fault 进行了探测,您将得到无限循环的断点,这不是您想要的。您真正需要的是保护那种函数,而 kprobes 通过将它放在单独的.kprobes.text 区域来实现此目的。这将防止对这些函数进行探测,因为 kprobe 不会在该区域中查找地址。这就是__attribute__((__section__(".kprobes.text"))) 的作用。


内核kprobe子系统从您的探针中找到地址。内核如何找到实际地址? - Jonggyu Park

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接