关于GDB如何调试Linux内核,请参考我的另一篇文章:
Virtualbox环境通过GDB调试Linux内核_gdb能调试linux内核驱动吗-CSDN博客
调试内核模块与调试内核类似,一点区别是内核模块需要单独编译,编译时注意加入符号信息。在调试机进行远程调试时,需要载入这些符号信息,同时需要指定模块在内核中的地址。
1. 模块编译
下面是一个demo的模块示例 (demo_module.c),主要功能是当在命令行执行cat /proc/demo_proc 时,返回给用户空间一个消息:"Hello from the kernel!\n"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#define PROCFS_NAME "demo_proc"
static struct proc_dir_entry *proc_file;
static ssize_t procfile_read(struct file *file, char __user *buffer, size_t len, loff_t *offset) {
int ret;
char *msg = "Hello from the kernel!\n";
size_t msg_len = strlen(msg);
if (*offset >= msg_len)
return 0;
if (*offset + len > msg_len)
len = msg_len - *offset;
ret = copy_to_user(buffer, msg + *offset, len);
if (ret)
return -EFAULT;
*offset += len;
return len;
}
static const struct proc_ops proc_file_ops = {
.proc_read = procfile_read,
};
static int __init demo_module_init(void) {
printk(KERN_INFO "Demo Module: Initialization\n");
proc_file = proc_create(PROCFS_NAME, 0444, NULL, &proc_file_ops);
if (!proc_file) {
printk(KERN_ALERT "Demo Module: Error creating proc file\n");
return -ENOMEM;
}
return 0;
}
static void __exit demo_module_exit(void) {
proc_remove(proc_file);
printk(KERN_INFO "Demo Module: Exit\n");
}
module_init(demo_module_init);
module_exit(demo_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("taoyuanforrest");
MODULE_DESCRIPTION("A simple example kernel module with a proc file");
MODULE_VERSION("1.0");
Makefile如下:
obj-m += demo_module.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules EXTRA_CFLAGS="-g"
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
其中,EXTRA_CFLAGS="-g" 的作用是编译时加入调试信息。
编译:
make
编译以后会在当前目录生成 demo_module.ko
2. 模块安装
sudo insmod demo_module.ko
查看是否成功:
[root@localhost linux]# lsmod | grep demo_module
demo_module 16384 0
3. 模块调试
首先,查看模块的加载地址:
[root@localhost linux]# cat /proc/modules | grep demo_module
demo_module 16384 0 - Live 0xffffffffa0000000 (O)
这里是0xffffffffa0000000
将编译好的含有调试信息内核模块传到调试机上。
在目标机上开启调试:
echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
echo g > /proc/sysrq-trigger
在调试机上通过 gdb vmlinux启动gdb调试,然后通过 add-symbol-file 加载模块调试信息:
(gdb) add-symbol-file demo_module.ko 0xffffffffa0000000
add symbol table from file "demo_module.ko" at
.text_addr = 0xffffffffa0000000
(y or n) y
Reading symbols from demo_module.ko...done.
现在可以设置断点了,比如
(gdb) b demo_module.c:13
Breakpoint 1 at 0xffffffffa0000007: file /home/linux/demo_module.c, line 13.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0xffffffffa0000007 in procfile_read at /home/linux/demo_module.c:13
连接到目标机:
(gdb) set serial baud 115200
(gdb)
(gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
0xffffffff811276db in kgdb_breakpoint ()
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0xffffffffa0000007 in procfile_read at /home/linux/demo_module.c:13
(gdb) c
Continuing.
在目标机上执行 cat /proc/demo_proc, 此时调试机上会停在断点上。
4. 模块卸载
[root@localhost linux]# sudo rmmod demo_module
[root@localhost linux]# lsmod | grep demo_module
[root@localhost linux]#