打开一终端执行:
qemu -m 512 -kernel bzImage -append "root=/dev/sda kgdboc=ttyS0,115200 kgdbwait" -boot c -hda busybox.img -k en-us -net nic -net tap,ifname=tap0,script=no -serial tcp::4321,server
显示等待调试端链接:
QEMU waiting for connection on: tcp:0.0.0.0:4321,server。
再打开一终端
$cd linux-2.6.35.9
$gdb vmlinux
调试客户端:
(gdb) target remote localhost:4321
Remote debugging using localhost:4321
kgdb_breakpoint (new_dbg_io_ops=0xc07c27e0)
at kernel/debug/debug_core.c:967
warning: Source file is more recent than executable.
967 wmb(); /* Sync point after breakpoint */
开启运行:
(gdb) c
Continuing.
一直到虚拟机启动。
让虚拟机进入调试模式:
#echo g > /proc/sysrq-trigger
在调试端下断点:
(gdb)b sys_init_module
(这个函数名在源文件中找不到,是由宏定义SYSCALL_DEFINE3展开的)
让虚拟机开启运行:
(gdb) c
Continuing.
在虚拟机中加载模块:
#insmod globalmem.ko
此时调试端中断在函数sys_init_module中:
Breakpoint 1, sys_init_module (umod=0xb777c008, len=134933, uargs=0x81c4348 "")
at kernel/module.c:2618
2618 if (!capable(CAP_SYS_MODULE) || modules_disabled)
下断:
(gdb) b add_sect_attrs
Breakpoint 2 at 0xc017251d: file kernel/module.c, line 1160.
执行:
(gdb) c
Continuing.
Breakpoint 2, add_sect_attrs (mod=0xe0a356c0, nsect=34, secstrings=0xe0a2b621 "",
sechdrs=0xe0a2b754) at kernel/module.c:1160
1160 for (i = 0; i < nsect; i++)
用where显示调用栈:
(gdb) where
#0 add_sect_attrs (mod=0xe0a356c0, nsect=34, secstrings=0xe0a2b621 "", sechdrs=0xe0a2b754)
at kernel/module.c:1160
#1 0xc0174029 in load_module (umod=<value optimized out>, len=<value optimized out>,
uargs=<value optimized out>) at kernel/module.c:2552
#2 0xc01742e2 in sys_init_module (umod=0xb78b6008, len=134933, uargs=0x81c4348 "")
at kernel/module.c:2622
下断第1192行:
(gdb) b 1192
Breakpoint 4 at 0xc01725db: file kernel/module.c, line 1192.
按c开启运行:
(gdb) c
Continuing.
Breakpoint 4, add_sect_attrs (mod=<value optimized out>, nsect=<value optimized out>,
secstrings=<value optimized out>, sechdrs=0xe0a2b754) at kernel/module.c:1192
1192 *(gattr++) = &(sattr++)->mattr.attr;
此时打印 sattr ->name:
(gdb) p sattr ->name
$4 = 0xdf412b60 ".note.gnu.build-id"
按若干次c之后:
(gdb) p sattr ->name
$2 = 0xdf8b25e8 ".text"
(gdb) p /x sattr ->address
$4 = 0xe0a35000
(gdb) p sattr ->name
$8 = 0xdf8b2600 ".data"
(gdb) p /x sattr ->address
$10 = 0xe0a356b8
在gdb中增加调试信息:
add-symbol-file /home/gudujian/06/globalmemDriver/globalmem.ko 0xe0a35000 -s .data 0xe0a356b8
此时就可以对globalmem.ko模块中的符号,正常下断点:
(这里仅仅使用模块的两个节.data,.text如果再使用其它节那么比较麻烦,如果有脚本可以做上面的事情就很好了)。
(gdb) b globalmem_init
Breakpoint 5 at 0xe0a35448: file /home/gudujian/06/globalmemDriver/globalmem.c, line 195.
按c让程序开始运行:
(gdb) c
Continuing.
Breakpoint 5, globalmem_init () at /home/gudujian/06/globalmemDriver/globalmem.c:195
195 dev_t devno = MKDEV(globalmem_major, 0);
此时程序中断在新加载模块的module_init函数中:globalmem_init。
此时因为编译这个内核模块时带有调试信息 EXTRA_CFLAGS=-g -O0
所以可以正常调试linux模块。
小技巧:
)当不知道一个指针指向具体哪个函数的时候可以用打印的方式;
比如: ret = info->fbops->fb_check_var(var, info);
可以打印: printk("info->fbops->fb_check_var is %x\n",info->fbops->fb_check_var);
得到这个函数指针的地址之后一切就好办了,可以用 cat /proc/kallsyms,或者用
map文件。或者用addr2line工具得到对应的具体哪个函数。