使用kgdb調(diào)試linux內(nèi)核及內(nèi)核模塊
1. 幾種內(nèi)核調(diào)試工具比較
kdb:只能在匯編代碼級進行調(diào)試;
優(yōu)點是不需要兩臺機器進行調(diào)試。
gdb:在調(diào)試模塊時缺少一些至關重要的功能,它可用來查看內(nèi)核的運行情況,包括反匯編內(nèi)核函數(shù)。
kgdb:能很方便的在源碼級對內(nèi)核進行調(diào)試,缺點是kgdb只能進行遠程調(diào)試,它需要一根串口線及兩臺機器來調(diào)試內(nèi)核(也可以是在同一臺主機上用vmware軟件運行兩個操作系統(tǒng)來調(diào)試)
使用kdb和gdb調(diào)試內(nèi)核的方法相對比較簡單,這里只描述如何使用kgdb來調(diào)試內(nèi)核。
2.軟硬件準備
環(huán)境:
一臺開發(fā)機developer(192.168.16.5 com1),一臺測試機target(192.168.16.30 com2),都預裝redhat 9;一根串口線
下載以下軟件包:
linux內(nèi)核2.4.23 linux-2.4.23.tar.bz2
kgdb內(nèi)核補丁1.9版 linux-2.4.23-kgdb-1.9.patch
可調(diào)試內(nèi)核模塊的gdb gdbmod-1.9.bz2
3.ok,開始
3.1 測試串口線
物理連接好串口線后,使用一下命令進行測試,stty可以對串口參數(shù)進行設置
在developer上執(zhí)行:
stty ispeed 115200 ospeed 115200 -F /dev/ttyS0
echo hello > /dev/ttyS0
在target上執(zhí)行:
stty ispeed 115200 ospeed 115200 -F /dev/ttyS1
cat /dev/ttyS1
串口線沒問題的話在target的屏幕上顯示hello
3.2 安裝與配置
3.2.1 安裝
下載linux-2.4.23.tar.bz2,linux-2.4.23-kgdb-1.9.patch,gdbmod-1.9.bz2到developer的/home/liangjian目錄
*在developer機器上
#cd /home/liangjian
#bunzip2 linux-2.4.23.tar.bz2
#tar -xvf linux-2.4.23.tar
#bunzip2 gdbmod-1.9.bz2
#cp gdbmod-1.9 /usr/local/bin
#cd linux-2.4.23
#patch -p1 < ../linux-2.4.23-kgdb-1.9.patch
#make menuconfig
在Kernel hacking配置項中將以下三項編譯進內(nèi)核
KGDB: Remote (serial) kernel debugging with gdb
KGDB: Thread analysis
KGDB: Console messages through gdb
注意在編譯內(nèi)核的時候需要加上-g選項
#make dep;make bzImage
使用scp進行將相關文件拷貝到target上(當然也可以使用其它的網(wǎng)絡工具)
#scp arch/i386/boot/bzImage root@192.168.16.30:/boot/vmlinuz-2.4.23-kgdb
#scp System.map root@192.168.16.30:/boot/System.map-2.4.23-kgdb
#scp arch/i386/kernel/gdbstart root@192.168.16.30:/sbin
gdbstart為kgdb提供的一個工具,用于激活內(nèi)核鉤子,使內(nèi)核處于調(diào)試狀態(tài)
{{分頁}}
3.2.2 配置
*在developer機器上
在內(nèi)核源碼目錄下編輯一文件.gdbinit(該文件用以對gdb進行初始化),內(nèi)容如下:
#vi .gdbinit
define rmt
set remotebaud 115200
target remote /dev/ttyS0
end
#
以上在.gdbinit中定義了一個宏rmt,該宏主要是設置使用的串口號和速率
*在target機器上
編輯/etc/grub.conf文件,加入以下行:
#vi /etc/grub.conf
title Red Hat Linux (2.4.23-kgdb)
root (hd0,0)
kernel /boot/vmlinuz-2.4.23-kgdb ro root=/dev/hda1
#
在root目錄下建立一個腳本文件debugkernel,內(nèi)容如下:
#vi debug
#!/bin/bash
gdbstart -s 115200 -t /dev/ttyS1 <<EOF
EOF
#chmod +x debugkernel
這個腳本主要是調(diào)用gdbstart程序設置target機上使用的串口及其速率,并使內(nèi)核處于調(diào)試狀態(tài)
3.3 開始調(diào)試
target上的內(nèi)核或內(nèi)核模塊處于調(diào)試狀態(tài)時,可以查看其變量、設置斷點、查看堆棧等,并且是源碼級的調(diào)試,和用gdb調(diào)試用戶程序一樣
3.3.1 內(nèi)核啟動后調(diào)試
*在target機器上
重啟系統(tǒng),選擇以 2.4.23-kgdb內(nèi)核啟動,啟動完成后運行debugkenel,
這時內(nèi)核將停止運行,在控制臺屏幕上顯示信息,并等待來自developer的
串口連接
#./debug
About to activate GDB stub in the kernel on /dev/ttyS1
Waiting for connection from remote gdb...
*在developer機器上
#cd /home/liangjian/linux-2.4.23
# gdb vmlinux
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
執(zhí)行rmt宏
(gdb) rmt
breakpoint () at kgdbstub.c:1005
1005 atomic_set(&kgdb_setting_breakpoint, 0);
這時target上的內(nèi)核處于調(diào)試狀態(tài),可以查看其變量、設置斷點、查看堆棧等,和用gdb調(diào)試用戶程序一樣
查看堆棧
(gdb) bt
#0 breakpoint () at kgdbstub.c:1005
#1 0xc0387f48 in init_task_union ()
#2 0xc01bc867 in gdb_interrupt (irq=3, dev_id=0x0, regs=0xc0387f98) at
gdbserial.c:158
#3 0xc010937b in handle_IRQ_event (irq=3, regs=0xc0387f98, action=0xce5a9860)
at irq.c:452
#4 0xc0109597 in do_IRQ (regs=
{ebx = -1072671776, ecx = -1, edx = -1070047232, esi = -1070047232, edi
= -1070047232, ebp = -1070039092, eax = 0, xds
= -1070071784, xes = -1070071784, orig_eax = -253, eip = -1072671729, xcs =
16, eflags = 582, esp = -1070039072, xss = -1072671582}) at irq.c:639
#5 0xc010c0e8 in call_do_IRQ ()
查看jiffies變量的值
(gdb) p jiffies
$1 = 76153
如果想讓target上的內(nèi)核繼續(xù)運行,執(zhí)行continue命令
(gdb) continue
Continuing.
3.3.2 內(nèi)核在引導時調(diào)試
kgdb可以在內(nèi)核引導時就對其進行調(diào)試,但并不是所有引導過程都是可調(diào)試的,如在kgdb 1.9版中,它在init/main.c的start_kernel()函數(shù)中插入以下代碼:
start_kernel()
{
......
smp_init();
#ifdef CONFIG_KGDB
if (gdb_enter) {
gdb_hook(); /* right at boot time */
}
#endif
......
}
所以在smp_init()之前的初始化引導過程是不能調(diào)試的。
{{分頁}}
另外要想讓target的內(nèi)核在引導時就處于調(diào)試狀態(tài),需要修改其/etc/grub.conf文件為如下形式:
title Red Hat Linux (2.4.23-kgdb)
root (hd0,0)
kernel /boot/vmlinuz-2.4.23-kgdb ro root=/dev/hda1 gdb gdbttyS=1 gdbbaud=115200
*在target機器上
引導2.4.23-kgdb內(nèi)核,內(nèi)核將在短暫的運行后暫停并進入調(diào)試狀態(tài),打印如下信息:
Waiting for connection from remote gdb...
*在developer機器上
#cd /home/liangjian/linux-2.4.23
# gdb vmlinux
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
執(zhí)行rmt宏
(gdb) rmt
breakpoint () at kgdbstub.c:1005
1005 atomic_set(&kgdb_setting_breakpoint, 0);
查看當前堆棧
(gdb) bt
#0 breakpoint () at kgdbstub.c:1005
#1 0xc0387fe0 in init_task_union ()
#2 0xc01bc984 in gdb_hook () at gdbserial.c:250
#3 0xc0388898 in start_kernel () at init/main.c:443
在do_basic_setup函數(shù)處設置斷點,并讓內(nèi)核恢復運行
(gdb) b do_basic_setup
Breakpoint 1 at 0xc0388913: file current.h, line 9.
(gdb) continue
Continuing.
[New Thread 1]
[Switching to Thread 1]
Breakpoint 1, do_basic_setup () at current.h:9
9 __asm__("andl %%esp,%0; ":"=r" (current) : "0" (~8191UL));
內(nèi)核在do_basic_setup斷點處停止運行后查看當前堆棧
(gdb) bt
#0 do_basic_setup () at current.h:9
(gdb)
3.3.3 內(nèi)核模塊調(diào)試調(diào)試
要想調(diào)試內(nèi)核模塊,需要相應的gdb支持,kgdb的主頁上提供了一個工具gdbmod,它修正了gdb 6.0在解析模塊地址時的錯誤,可以用來正確的調(diào)試內(nèi)核模塊
*在developer機器上
寫了個測試用的內(nèi)核模塊orig,如下:
void xcspy_func()
{
printk("<1>xcspy_funcn");
printk("<1>aaaaaaaaaaan");
}
int xcspy_init()
{
printk("<1>xcspy_init_modulen");
return 0;
}
void xcspy_exit()
{
printk("<1>xcspy_cleanup_modulen");
}
module_init(xcspy_init);
module_exit(xcspy_exit);
{{分頁}}
編譯該模塊:
#cd /home/liangjian/lkm
#gcc -D__KERNEL__ -DMODULE -I/home/liangjian/linux-2.4.23/include -O -Wall -g -c -o orig.o orig.c
#scp orig.o root@192.168.16.30:/root
開始調(diào)試:
# gdbmod vmlinux
GNU gdb 6.0
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
設置符號文件的搜索路徑
(gdb) set solib-search-path /home/liangjian/lkm
執(zhí)行rmt宏
(gdb) rmt
breakpoint () at kgdbstub.c:1005
1005 atomic_set(&kgdb_setting_breakpoint, 0);
設置斷點使得可以調(diào)試內(nèi)核模塊的init函數(shù),查內(nèi)核源碼可知,內(nèi)核是通過module.c文件的第566行(sys_init_module函數(shù)中)mod->init來調(diào)用模塊的init函數(shù)的
(gdb) b module.c:566
Breakpoint 1 at 0xc011cd83: file module.c, line 566.
(gdb) c
Continuing.
[New Thread 1352]
[Switching to Thread 1352]
這時在target機器上執(zhí)行insmod orig.o,developer則相應的在斷點處被暫停,如下
Breakpoint 1, sys_init_module (name_user=0xc03401bc "