利用bochs调试内核

来源:互联网 发布:淘宝官方自营店可靠吗 编辑:程序博客网 时间:2024/04/29 21:53
                                                    利用bochs调试内核(主要是在windows下调试)
Bochs具有非常强大的操作系统内核调试功能。这也是本文选择Bochs作为首选实验环境的主要原因之一。有关Bochs调试功能的说明参见前面14.2节,这里基于Linux 0.11内核来说明Windows环境下Bochs系统调试操作的基本方法。

14.8.1 运行Bochs调试程序
我们假设Bochs系统已被安装在目录“C:\Program Files\Bochs-2.1.1\”中,并且Linux 0.11系统的Bochs配置文件名称是bochsrc-hd.bxrc。现在在包含内核Image文件的目录下建立一个简单的批处理文件run.bat,其内容如下:

"C:\Program Files\Bochs-2.1.1\bochsdbg" -q -f bochsrc-hd.bxrc

其中bochsdbg是Bochs系统的调试执行程序。运行该批处理命令即可进入调试环境。此时Bochs的主显示窗口空白,而控制窗口将显示以下类似内容:

C:\Documents and Settings\john1\桌面\Linux-0.11>"C:\Program Files\Bochs-2.1.1\bo
chsdbg" -q -f bochsrc-hd.bxrc

========================================================================                      Bochs x86 Emulator 2.1.1                          February 08, 2004========================================================================00000000000i[     ] reading configuration from bochsrc-hd.bxrc00000000000i[     ] installing win32 module as the Bochs GUI00000000000i[     ] Warning: no rc file specified.00000000000i[     ] using log file bochsout.txtNext at t=0(0) context not implemented because BX_HAVE_HASH_MAP=0[0x000ffff0] f000:fff0 (unk. ctxt): jmp f000:e05b             ; ea5be000f0<bochs:1>

此时Bochs调试系统已经准备好开始运行,CPU执行指针已指向ROM BIOS中地址0x000fffff0处的指令处。其中'<bochs:1>'是命令输入提示符,其中的数字表示当前的命令序列号。在命令提示符'<bochs:1>'后面键入'help'命令,可以列出调试系统的基本命令。若要了解某个命令的具体使用方法,可以键入'help'命令并且后面跟随一个用单引号括住的具体命令,例如:“help 'vbreak'”,如下面所示。

<bochs:1> helphelp - show list of debugger commandshelp 'command'- show short command description-*- Debugger control -*-   help, q|quit|exit, set, instrument, show, trace-on, trace-off,   record, playback, load-symbols, slist-*- Execution control -*-   c|cont, s|step|stepi, p|n|next, modebp-*- Breakpoint management -*-   v|vbreak, lb|lbreak, pb|pbreak|b|break, sb, sba, blist,   bpe, bpd, d|del|delete-*- CPU and memory contents -*-   x, xp, u|disas|disassemble, r|reg|registers, setpmem, crc, info, dump_cpu,   set_cpu, ptime, print-stack, watch, unwatch, ?|calc<bochs:2> help 'vbreak'help vbreakvbreak seg:off - set a virtual address instruction breakpoint<bochs:3>

为了让Bochs直接模拟执行到Linux的引导启动程序开始处,我们可以先使用断点命令在0x7c00处设置一个断点,然后让系统连续运行到0x7c00处停下来。执行的命令序列如下:

<bochs:3> vbreak 0x0000:0x7c00<bochs:4> c(0) Breakpoint 1, 0x7c00 (0x0:0x7c00)Next at t=4409138(0) [0x00007c00] 0000:7c00 (unk. ctxt): mov ax, 0x7c0             ; b8c007<bochs:5>

此时,CPU执行到boot.s程序开始处的第1条指令处,Bochs主窗口将显示出“Boot From floppy...”等一些信息。现在,我们可以利用单步执行命令's'或'n'(不跟踪进入子程序)来跟踪调试程序了。在调试时可以使用Bochs的断点设置命令、反汇编命令、信息显示命令等来辅助我们的调试操作。下面是一些常用命令的示例:

<bochs:8> u /10                                    # 反汇编从当前地址开始的10条指令。00007c00: (                    ): mov ax, 0x7c0             ; b8c00700007c03: (                    ): mov ds, ax                ; 8ed800007c05: (                    ): mov ax, 0x9000            ; b8009000007c08: (                    ): mov es, ax                ; 8ec000007c0a: (                    ): mov cx, 0x100             ; b9000100007c0d: (                    ): sub si, si                ; 29f600007c0f: (                    ): sub di, di                ; 29ff00007c11: (                    ): rep movs word ptr [di], word ptr [si] ; f3a500007c13: (                    ): jmp 9000:0018             ; ea1800009000007c18: (                    ): mov ax, cs                ; 8cc8<bochs:9> info r                                  # 查看当前CPU寄存器的内容eax            0xaa55           43605ecx            0x110001         1114113edx            0x0              0ebx            0x0              0esp            0xfffe           0xfffeebp            0x0              0x0esi            0x0              0edi            0xffe4           65508eip            0x7c00           0x7c00eflags         0x282            642cs             0x0              0ss             0x0              0ds             0x0              0es             0x0              0fs             0x0              0gs             0x0              0<bochs:10> print-stack                            # 显示当前堆栈的内容  0000fffe [0000fffe]  0000  00010000 [00010000]  0000  00010002 [00010002]  0000  00010004 [00010004]  0000  00010006 [00010006]  0000  00010008 [00010008]  0000  0001000a [0001000a]  0000...<bochs:11> dump_cpu                               # 显示CPU中的所有寄存器和状态值。eax:0xaa55ebx:0x0ecx:0x110001edx:0x0ebp:0x0esi:0x0edi:0xffe4esp:0xfffeeflags:0x282eip:0x7c00cs:s=0x0, dl=0xffff, dh=0x9b00, valid=1ss:s=0x0, dl=0xffff, dh=0x9300, valid=7ds:s=0x0, dl=0xffff, dh=0x9300, valid=1es:s=0x0, dl=0xffff, dh=0x9300, valid=1fs:s=0x0, dl=0xffff, dh=0x9300, valid=1gs:s=0x0, dl=0xffff, dh=0x9300, valid=1ldtr:s=0x0, dl=0x0, dh=0x0, valid=0tr:s=0x0, dl=0x0, dh=0x0, valid=0gdtr:base=0x0, limit=0x0idtr:base=0x0, limit=0x3ffdr0:0x0dr1:0x0dr2:0x0dr3:0x0dr6:0xffff0ff0dr7:0x400tr3:0x0tr4:0x0tr5:0x0tr6:0x0tr7:0x0cr0:0x60000010cr1:0x0cr2:0x0cr3:0x0cr4:0x0inhibit_mask:0done<bochs:12>


由于Linux 0.11内核的32位代码是从绝对物理地址0处开始存放的,因此若想直接执行到32位代码开始处,即head.s程序开始处,我们可以在线性地址0x0000处设置一个断点并运行命令'c'执行到那个位置处。
另外,当直接在命令提示符下打回车键时会重复执行上一个命令;按向上方向键会显示上一命令。其他命令的使用方法请参考'help'命令。

14.8.2 定位内核中的变量或数据结构
在编译内核时会产生一个system.map文件。该文件列出了内核Image (bootimage)文件中全局变量和各个模块中的局部变量的偏移地址位置。在内核编译完成后可以使用前面介绍的文件导出方法把system.map文件抽取到主机环境(windows)中。有关system.map文件的详细功能和作用请参见2.10.3节。system.map样例文件中的部分内容见如下所示。利用这个文件,我们可以在Bochs调试系统中快速地定位某个变量或跳转到指定的函数代码处。

...

Global symbols: _dup: 0x16e2c _nmi: 0x8e08 _bmap: 0xc364 _iput: 0xc3b4 _blk_dev_init: 0x10ed0 _open: 0x16dbc _do_execve: 0xe3d4 _con_init: 0x15ccc _put_super: 0xd394 _sys_setgid: 0x9b54 _sys_umask: 0x9f54 _con_write: 0x14f64 _show_task: 0x6a54 _buffer_init: 0xd1ec _sys_settimeofday: 0x9f4c _sys_getgroups: 0x9edc...

同样,由于Linux 0.11内核的32位代码是从绝对物理地址0处开始存放的,system.map中全局变量的偏移位置值就是CPU中线性地址位置,因此我们可以直接在感兴趣的变量或函数名位置处设置断点,并让程序连续执行到指定的位置处。例如若我们想调试函数buffer_init(),那么从system.map文件中可以知道它位于0xd1ec处。此时我们可以在该处设置一个线性地址断点,并执行命令'c'让CPU执行到这个指定的函数开始处,见如下所示。

<bochs:12> lb 0xd1ec                              # 设置线性地址断点。<bochs:13> c                                      # 连续执行。(0) Breakpoint 2, 0xd1ec in ?? ()Next at t=16689666(0) [0x0000d1ec] 0008:0000d1ec (unk. ctxt): push ebx                  ; 53<bochs:14> n                                      # 执行下一指令。Next at t=16689667(0) [0x0000d1ed] 0008:0000d1ed (unk. ctxt): mov eax, dword ptr ss:[esp+0x8] ; 8b442408<bochs:15> n                                      # 执行下一指令。Next at t=16689668(0) [0x0000d1f1] 0008:0000d1f1 (unk. ctxt): mov edx, dword ptr [ds:0x19958] ; 8b1558990100<bochs:16>

程序调试是一种技能,需要多练习才能熟能生巧。上面介绍的一些基本命令需要组合在一起使用才能灵活地观察到内核代码执行的整体环境情况。

原创粉丝点击