如何调试虚拟机上运行的内核模块?

发布于 2025-01-19 05:50:05 字数 399 浏览 0 评论 0原文

我想我曾经读过有关此的信息,但现在找不到。 我正在QEMU ARM64虚拟机上运行Linux-5.4.188。因为我从源构建了内核,所以我可以通过连接到在远程计算机上运行的Linux内核程序(QEMU虚拟机)来调试(分析)内核。要测试使用我们设备的应用程序(设备模型也在QEMU中),我针对内核和Linux应用程序编辑了一个设备驱动程序,并且可以执行驱动程序并运行应用程序。
现在有些问题,在运行应用程序时我感到恐慌。我可以调试Linux内核本身,但是我不知道内核模块已加载位置,因此调试器无法调试驱动程序模块。如何调试设备驱动程序? (甚至是应用程序?如果我有一天需要)。我记得首先获得内核模块的加载地址,并相对于该加载的地址进行驱动程序图像的附加符号文件,可以进行内核模块调试。我认为这是驾驶员开发人员将永远做的事情。请告诉我我该怎么做。如果可能的话,它将为我节省很多天。

I think I have once read about this but can't find it now.
I'm running linux-5.4.188 on a qemu arm64 virtual machine. Because I built the kernel from the source, I can debug(analyze) the kernel by attaching to the linux kernel program running on a remote machine(qemu virtual machine). To test an application which uses our device(the device model is in qemu too), I compiled a device driver against kernel 5.4.188 and the linux application and can do insmod the driver and run the application.
Now something is wrong and I have panic while running the application. I can debug linux kernel itself, but I don't know where the kernel module was loaded, so the debugger cannot debug the driver module. How can I debug the device driver? (or even the application? in case I need to someday). I remember by first getting the loaded address of the kernel module, and doing add-symbol-file for the driver image relative to that loaded address, it is possible to do kernel module debug. I think this is what driver developers will be doing always. Please tell me how I can do it. If this is possible, it will save many days for me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

森林很绿却致人迷途 2025-01-26 05:50:05

正如 Ian Abbott 所说,我使用 printk 进行内核调试。通常为此我将他的这一行放在/etc/sysctl.conf中。 (对于 ubuntu-20.04 的情况。我不确定它是否也适用于 vanila linux)

kernel.printk = 5 4 1 7

第一个值是控制台日志级别,第二个值是默认日志级别。通过设置默认 4(警告)和控制台日志级别 5(注意),每个默认 printk 都会出现在控制台中(因为 5 高于 4,因此值越小越重要,并且比在控制台中打印的控制台日志级别更重要的消息)请参阅 如何在中显示 printk() 消息控制台?)。
这样你就不必每次都用 dmesg 检查。在start_kernel中的setup_arch函数中可以看到串口初始化前的内核__log_buf。在串口初始化之前,内核消息被写入名为__log_buf的内存中。

我参考了 https://www.kernel .org/doc/html/latest/dev-tools/gdb-kernel-debugging.html 明确指出了我,我还发现了 https://wiki.st.com/stm32mpu/wiki/Debugging_the_Linux_kernel_using_the_GDB非常有用。
内核配置期间的重要事项:
设置 CONFIG_GDB_SCRIPTS=y、CONFIG_DEBUG_INFO_REDUCED=n、CONFIG_FRAME_POINTER=y。
我在内核命令行中使用 nokaslr,但添加了 CONFIG_RANDOMIZE_BASE=n 以确保。

现在是我最初想从我的问题中了解的部分:
在 shell 中(在虚拟机 linux shell 中)执行,
$ls -la /sys/module/<模块名称>/sections
然后您将看到 .text、.data、.bss 等文件。您 cd 到该目录并执行
$cat .text .data .bss
查看每个部分的地址。就我而言,

/sys/module/axpu_ldd_kc/sections # cat .bss .data .text
0xffff800008ca5480
0xffff800008ca5000
0xffff800008ca0000

在 gdb 中(通过 ctrl-c 停止程序后)我做了

add-symbol-file ~/testlin540/axpu_ldd_kc.ko 0xffff800008ca0000 -s .data 0xffff800008ca5000 -s .bss 0xffff800008ca5480

并尝试在 ioctl 函数中设置断点。
(gdb)b axpu_ioctl
当我按下 c(继续)并且启动应用程序时,我可以看到程序停止在 axpu_ioctl 处,并且我可以单步执行代码并查看值。
最近在使用 u-boot 进行内核调试时,经常用

#pragma GCC push_options
#pragma GCC optimize ("O0")

和 包裹一个函数,

#pragma GCC pop_options

以防止某些部分的代码被优化掉。 (有时你也应该对相关的 #include语句执行此操作,以防止编译错误)。

As Ian Abbott said I use printk for kernel debugging. Usually for this I put his this line in /etc/sysctl.conf. (for ubuntu-20.04 case. I'm not sure it's applicable to vanila linux too)

kernel.printk = 5 4 1 7

The first value is the console log level and the second value is default log level. By making default 4 (WARNING) and console log level 5 (NOTICE), every default printk appears in the console (because 5 is higher thatn 4, less value is more important and more important messages than the console log level gets printed in the console. see How can I show printk() message in console?).
This way you don't have to check with dmesg everytime. You can see the kernel __log_buf before the serial port is initialized in setup_arch function in start_kernel. The kernel message are written in memory called __log_buf before serial port is initialized.

I refered to https://www.kernel.org/doc/html/latest/dev-tools/gdb-kernel-debugging.html that stark pointed me to and I also found https://wiki.st.com/stm32mpu/wiki/Debugging_the_Linux_kernel_using_the_GDB very useful.
Important things during the kernel config :
set CONFIG_GDB_SCRIPTS=y, CONFIG_DEBUG_INFO_REDUCED=n, CONFIG_FRAME_POINTER=y.
I use nokaslr in the kernel command line, but added CONFIG_RANDOMIZE_BASE=n to make sure.

Now the part I originally wanted to know from my question :
In the shell (in virtual machine linux shell) do,
$ls -la /sys/module/<module_name>/sections
Then you'll see files like .text, .data, .bss, etc. You cd to that directory and do
$cat .text .data .bss
To see the address of each section. In my case,

/sys/module/axpu_ldd_kc/sections # cat .bss .data .text
0xffff800008ca5480
0xffff800008ca5000
0xffff800008ca0000

and in the gdb, (after stopping the program by ctrl-c) I did

add-symbol-file ~/testlin540/axpu_ldd_kc.ko 0xffff800008ca0000 -s .data 0xffff800008ca5000 -s .bss 0xffff800008ca5480

and I tried setting breakpoint at my ioctl function.
(gdb) b axpu_ioctl
and when I pressed c (continue) and when I started my application, I could see the program stop at the axpu_ioctl and I could single step through the code and see the values.
When I did kernel debug for booting using u-boot recently, I frequently wrapped a function with

#pragma GCC push_options
#pragma GCC optimize ("O0")

and

#pragma GCC pop_options

To prevent some parts of the codes from being optimized away. (sometime you should do it for the related #include <xxx.h> statement too to prevent compile error).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文