如何调试 Linux 内核模块的 init_module() 调用?

3

我正在进行Linux内核开发的第一步。

我有一些代码生成了一个.ko内核模块,我使用insmod安装它。我想要一种调试安装模块时发生的情况的方法,但是我遇到了一些困难。

  1. 我需要调试对init_module的调用。当我运行insmode时是否会调用此函数?

  2. 我尝试使用insmod "/my/url/fil.ko" -m来调试发生的情况,但每次都会出现错误-1 Unknown symbol in module,而在/cat/log/message中,我可以看到错误unknown parameter -m

  3. 你知道是否有一种使用GDB进行调试的方法吗?

3个回答

4

是的,当您使用 insmod 将模块加载到内核时,init_module 函数会立即被调用。您可以添加一个 printk 语句来验证在插入模块后是否已打印。

您无法传递诸如 -m 之类的参数来调试内核模块。

您只能使用 MODULE_PARAMS 传递旨在在您编写的内核模块中处理的参数。


嗨,Amarnath。感谢您的回复。那么,“insmod -m”的确切语法是什么,它是如何工作的?man页面说它用于在加载模块时将所有printk调用输出到控制台。 - Abruzzo Forte e Gentile
1
我相信从内核2.6开始,insmod的-m开关支持已被删除。您可以在此处找到更多信息:http://linux.derkeiler.com/Mailing-Lists/Kernel/2003-09/3268.html - Amarnath Revanna

2

QEMU + GDB逐步调试module_init

在尝试module_init之前,请确保能够一般性地使用QEMU + GDB进行内核模块调试:如何使用QEMU调试Linux内核模块?

module_init比较困难,因为我们不知道内核模块在加载前会被加载到哪里。

然后,这里有两种非理想但可用的技术来进入module_init

  1. Find the module load address, and reuse it later.

    The module load location is deterministic after each boot, so we can find:

    Then, add them up, and tell GDB to break at that point.

  2. Step into the module_init call.

    On kernel 4.16, first break at:

    do_init_module
    

    Then step until:

    ret = fn();
    

    Then step into that, and you fall inside the module_init function.

这个QEMU + Buildroot设置可以方便地测试这两种方法。


0

GDB无法固有地捕获模块的init和exit函数上的断点,就像它可以在其他专为服务于设备节点系统调用的函数上 (例如打开、读取或写入设备) 一样。我怀疑这与存储在进程地址空间的哪个部分有关,但我不确定。

进一步复杂化问题的是,模块每次加载时都会在随机地址上加载 - 不管是使用nokaslr还是静态构建还是动态插入到内核中(使用nokaslr,内核的核心部分将始终加载到相同的地址空间中,但模块不会)。因此,您不能简单地加载模块,在init符号的当前地址上设置断点,然后重新加载模块。然而,有一个解决方案:

就像Ciro在他的答案的第二种方法中所说的那样,您需要在内核核心的do_init_module函数上设置断点,每次加载任何模块时都会调用该函数。由于它是我提到的那个核心内核代码的一部分,gdb将知道它的正确位置。从那里开始,而不是反复步进(可能数百次,根据我的尝试),请按照我发现的这篇最新网帖的说明进行操作:

https://sysprogs.com/VisualKernel/documentation/kernelsymbols/


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