此文檔的目標系統爲freescale MPC8349E-mITX,調試工具爲BDI2000, 目標系統採用2.6內核,對其他採用POWERPC,MIPS,ARM的芯片的系統亦具有參考意義。 此文檔中內核調試爲了簡化,採用目標系統中的UBOOT初始化目標板,並通過UBOOT或者BDI2000加載內核到目標板的RAM中。
1. BDI2000配置:
下面是MPC8349E-mITX的BDI2000配置文件,
- ; BDI-2000 Configuration file for the MPC8349E-mITX
- ; Tip: If after a reset, the BDI-2000 fails to halt at 0x100,
- ; you may need to power-down the board for a few seconds.
- [INIT]
- ; we use UBOOT to initialize the board
- [TARGET]
- CPUTYPE 8349
- JTAGCLOCK 1
- ;STARTUP RESET
- STARTUP RUN
- BREAKMODE HARD
- STEPMODE HWBP
- BOOTADDR 0x00000100
- ;If you're getting "Writing to workspace failed" errors during flash operations,
- ;then try uncommenting this line instead. This moves the flash window to
- ;high memory, leaving low memory available for DDR.
- RCW 0xb060a000 0x04040000 ;Set the HRCW to boot the image at 0xFE000000
- MMU XLAT ;0xc0000000
- PTBASE 0xf0 ;
- [HOST]
- IP 192.168.7.90
- FILE $u-boot.bin
- LOAD MANUAL
- PROMPT 8349E-mITX-GP>
- DUMP itx-dump.bin
- [FLASH]
- CHIPTYPE AM29BX16
- CHIPSIZE 0x800000
- BUSWIDTH 16
- ;WORKSPACE 0x1000
- FORMAT BIN 0xfe000000
- ;flash_image.bin is an image file of an entire 8MB flash region.
- ;Flash this file at 0xfe0000000 to restore all of flash.
- ;ERASE 0xFE000000 0x10000 127 ; 127 sectors @ 64KB each
- ;ERASE 0xFE7F0000 0x2000 8 ; 8 sectors @ 8KB each
- ;FILE $flash_image.bin
- ;Use these lines if you just want to flash U-Boot
- ERASE 0xfe000000 0x10000 4; Erase 384KB, each sector is 64KB
- FILE mpc8349e/u-boot131-mitx-gp.bin
- [REGS]
- FILE $reg8349e.def
以上配置文件的【HOST】段的IP要改爲主機IP,關鍵的字段MMU XLAT 和PTBASE 是POWERPC和MIPS經常需要設置的,關於PTBASE的具體設置,超出本文範圍,詳細情況請參考BDI2000的手冊
2.內核修改和配置
爲了能夠調試內核,需要在內核中的Makefile中增加如下調試選項:
CFLAGS 增加C代碼調試選項-g –ggdb
AFLAGS 增加彙編代碼調試選項:-Wa,-L -gdwarf-2
去掉CFLAGS編譯選項中-fomit-frame-pointer
GCC的-fomit-frame-pointer選項是優化函數棧回溯(stack backtrace)的,我們調試的時候需要提供函數回溯能力,所以我們要去掉這個選項,當然,有的系統系統不受它的影響,或者說它不起作用,爲了統一,我們統一去掉它。
相對個案來說,我是做如下改動的:
- -- Makefile 2008-07-08 03:07:38.000000000 +0800
- +++ Makefile.debug 2008-07-08 03:06:04.000000000 +0800
- …
- -CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE)
- +ifdef CONFIG_DEBUG_INFO
- +
- + CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE) -g -ggdb
- -CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs /
- + CFLAGS := $(CPPFLAGS) -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs /
- -fno-strict-aliasing -fno-common
- -AFLAGS := -D__ASSEMBLY__
- + AFLAGS := -D__ASSEMBLY__ -Wa,-L -gdwarf-2
- +else
- + CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE)
- + CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs /
- + -fno-strict-aliasing -fno-common
- + AFLAGS := -D__ASSEMBLY__
- +
- +endif
- …
- @@ -491,27 +500,33 @@
- # Defaults vmlinux but it is usually overridden in the arch makefile
- all: vmlinux
- -ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
- -CFLAGS += -Os
- -else
- -CFLAGS += -O2
- -endif
- include $(srctree)/arch/$(ARCH)/Makefile
- -ifdef CONFIG_FRAME_POINTER
- -CFLAGS += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,)
- -else
- -CFLAGS += -fomit-frame-pointer
- -endif
- ifdef CONFIG_UNWIND_INFO
- CFLAGS += -fasynchronous-unwind-tables
- endif
- -ifdef CONFIG_DEBUG_INFO
- -CFLAGS += -g
- -endif
- +#ifdef CONFIG_DEBUG_INFO
- +CFLAGS += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,)
- +CFLAGS += -g -ggdb
- +CFLAGS += -O
- +#else
- +
- +# ifdef CONFIG_FRAME_POINTER
- + CFLAGS += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,)
- +# else
- + CFLAGS += -fomit-frame-pointer
- +# endif
- +
- +# ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
- + CFLAGS += -Os
- +# else
- + CFLAGS += -O2
- +# endif
- +
- +#endif
通過以上修改後,系統的的調試信息簡單的通過CONFIG_DEBUG_INFO宏來控制了,那麼CONFIG_DEBUG_INFO宏又是從哪裏來的呢?它其實是從內核的配置文件.config裏面來的。
一般內核通過make menuconfig做配置的時候,都有
Kernel hacking --->
[*] Kernel debugging [*] Compile the kernel with debug info [*] Force gcc to inline functions marked 'inline' (2.6比較新的內核有這一項) [*] Include BDI-2000 user context switcher (有的系統直接提供了這個選項,它和BDI2000的PTBASE設置配合) |
通過保存後,以上選項會生成如下配置選項:
CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO=y CONFIG_FORCED_INLINING=y CONFIG_BDI_SWITCH=y |
值得關注的是PowerPC內核中CONFIG_BDI_SWITCH,它到底在內核中怎樣起作用的?
我們看arch/powerpc/kernel/head_32.S的關鍵代碼:
… /* Load up the kernel context */ 2: bl load_up_mmu
#ifdef CONFIG_BDI_SWITCH /* Add helper information for the Abatron bdiGDB debugger. * We do this here because we know the mmu is disabled, and * will be enabled for real in just a few instructions. */ lis r5, abatron_pteptrs@h ori r5, r5, abatron_pteptrs@l stw r5, 0xf0(r0) /* This much match your Abatron config */ lis r6, swapper_pg_dir@h ori r6, r6, swapper_pg_dir@l tophys(r5, r5) stw r6, 0(r5) #endif /* CONFIG_BDI_SWITCH */
/* Now turn on the MMU for real! */ … |
它在MMU真正時能之前先增加了BDI2000幫助信息。在arch/powerpc/kernel/head_32.S的最後通過abatron_pteptrs保留了8個自己的空間給BDI2000用於保存2個頁表指針,如下:
/* Room for two PTE pointers, usually the kernel and current user pointers * to their respective root page table. */ abatron_pteptrs: .space 8 |
3. 內核調試
通過以上的準備工作,就可以進行內核和模塊的調試了,內核調試步驟如下:
說明:下面的步驟中
8349E-mITX-GP> 表示BDI2000的命令行窗口
[root@newhost misc-modules]# 表示開發主機
DDD> 或GDB> 表示是開發主機上的DDD的調試窗口中
root@mpc8349emitxgp:~# 表示目標系統中
1. 獲取恰當的斷點設置位置:
[shyi@newhost pro50_mpc8349_kernel]$ cat System.map |grep start_kernel
c03b05dc T start_kernel #得到start_kernel的虛擬地址
2.設置斷點,加載內核,啓動DDD的連接
8349E-mITX-GP>reset
8349E-mITX-GP>halt
8349E-mITX-GP>bi 0xc03b05dc (這個值是由System.map中的start_kernel的地址而來的)
8349E-mITX-GP>go
- TARGET: stopped #提示系統進入斷點了
8349E-mITX-GP>info
Target CPU : MPC83xx (e300c1)
Target state : debug mode
Debug entry cause : instruction address breakpoint
Current PC : 0xc03b05dc
Current CR : 0x44044022
Current MSR : 0x00001032
Current LR : 0x00003438
8349E-mITX-GP>
# 這時串口可看打到打印信息如:
Uncompressing Kernel Image ... OK
Booting using the fdt at 0xc00000
Loading Device Tree to 007fc000, end 007fefff ... OK
圖形系統中啓動DDD
[root@newhost scull]# cd /opt/pro50/montavista/pro/devkit/ppc/83xx/target/root/examples/misc-modules
[root@newhost misc-modules]# ddd --debugger ppc_83xx-gdb –gdb /home/shyi/workspace/pro50_mpc8349_kernel/vmlinux
(gdb)target remote 192.168.7.64:2001 (其中192.168.7.64:2001爲BDI2000的IP和調試端口)
8349E-mITX-GP>ci
8349E-mITX-GP>break soft #改變爲軟斷點方式
這時候可以在DDD>圖形界面裏面最右邊點擊鼠標右鍵來設置斷點,如圖:
(注意:系統有些地方不能停住,需要在合適的位置來設置斷點)
(gdb)cont
這時候系統就會停止在斷點設置的地方,接下來就可以進行內核斷點調試了,如下圖:
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
4.內核模塊的調試
使用LDD3的jit.c模塊進行調試的演示,DDD(或者說GDB)GDB的初始化腳本放置在~/.gdbinit:
其中.gdbinit的內容如下:
define lsmod printf "Address/t/tModule/n" set $m=(struct list_head *)&modules set $done=0 while ( !$done ) # list_head is 4-bytes into struct module set $mp=(struct module *)((char *)$m->next - (char *)4) printf "0x%08X/t%s/n", $mp, $mp->name if ($mp->list->next == &modules) set $done=1 end set $m=$m->next end end
define addmodulesymbols set $myModule=(struct module*) $arg0 set $myAddr=$myModule->module_core add-symbol-file $arg1 $myAddr end document addmodulesymbols
Adds the symbols for a module to the kernel.equires two parameters: addmodulesymbols <0xAddress> <.ko-file> end |
內核模塊調試前面的步驟和內核調試完全一致,先要在start_kernel的地方設置斷點,然後讓內核能進行調試,接下來:
# 按DDD的<Cont>按鈕繼續內核的運行
在內核起來之後à
root@mpc8349emitxgp:~# cd /root/examples/misc-modules
root@mpc8349emitxgp:~/examples/scull# insmod ./jit.ko
然後在DDD下按<CTRL+C>à
(gdb) lsmod
Address Module
0xD106FB00 jit
0xD25EE500 ipv6
(gdb) addmodulesymbols 0xd106fb00 ./jit.ko
add symbol table from file "./jit.ko" at
.text_addr = 0xd106e000
(注意啓動DDD的時候要在此調試模塊的目錄下,否則要指定jit.ko在主機上的絕對路徑位置)
(gdb) b jit_currentime
(gdb)cont
在目標平臺輸出終端上à
root@mpc8349emitxgp:~/examples/misc-modules# cat /proc/currentime
此時執行停住了,接下來我就可以在DDD中跟蹤驅動的執行了。如下圖: