您的位置:首页 > 其它

利用bochs调试内核

2009-09-24 11:20 330 查看
最近在研究linux0.11的内核代码,主要是参考赵炯博士的《Linux内核完全注释》一书,以及

www.oldlinux.org

网站。

发现了一些好的资料,想放到自己的博客上面,免得以后想要用的时候找不到了

——————————————————————————————————————————————————————————————————————

14.1 利用bochs调试内核

Bochs具有非常强大的操作系统内核调试功能。这也是本文选择Bochs作为首选实验环境的主要原因之一。有关Bochs调试功能的说明参见前面14.2节,这里基于Linux 0.11内核来说明Windows环境下Bochs系统调试操作的基本方法。

14.1.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.bxrc

00000000000i[ ] installing win32 module as the Bochs GUI

00000000000i[ ] Warning: no rc file specified.

00000000000i[ ] using log file bochsout.txt

Next at t=0

(0) context not implemented because BX_HAVE_HASH_MAP=0

[0x000ffff0] f000:fff0 (unk. ctxt): jmp f000:e05b ; ea5be000f0

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

help

help - show list of debugger commands

help '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

help 'vbreak'

help vbreak

vbreak seg:off - set a virtual address instruction breakpoint

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

vbreak 0x0000:0x7c00

c

(0) Breakpoint 1, 0x7c00 (0x0:0x7c00)

Next at t=4409138

(0) [0x00007c00] 0000:7c00 (unk. ctxt): mov ax, 0x7c0 ; b8c007

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

u /10 # 反汇编从当前地址开始的10条指令。

00007c00: ( ): mov ax, 0x7c0 ; b8c007

00007c03: ( ): mov ds, ax ; 8ed8

00007c05: ( ): mov ax, 0x9000 ; b80090

00007c08: ( ): mov es, ax ; 8ec0

00007c0a: ( ): mov cx, 0x100 ; b90001

00007c0d: ( ): sub si, si ; 29f6

00007c0f: ( ): sub di, di ; 29ff

00007c11: ( ): rep movs word ptr [di], word ptr [si] ; f3a5

00007c13: ( ): jmp 9000:0018 ; ea18000090

00007c18: ( ): mov ax, cs ; 8cc8

info r # 查看当前CPU寄存器的内容

eax 0xaa55 43605

ecx 0x110001 1114113

edx 0x0 0

ebx 0x0 0

esp 0xfffe 0xfffe

ebp 0x0 0x0

esi 0x0 0

edi 0xffe4 65508

eip 0x7c00 0x7c00

eflags 0x282 642

cs 0x0 0

ss 0x0 0

ds 0x0 0

es 0x0 0

fs 0x0 0

gs 0x0 0

print-stack # 显示当前堆栈的内容

0000fffe [0000fffe] 0000

00010000 [00010000] 0000

00010002 [00010002] 0000

00010004 [00010004] 0000

00010006 [00010006] 0000

00010008 [00010008] 0000

0001000a [0001000a] 0000

...

dump_cpu # 显示CPU中的所有寄存器和状态值。

eax:0xaa55

ebx:0x0

ecx:0x110001

edx:0x0

ebp:0x0

esi:0x0

edi:0xffe4

esp:0xfffe

eflags:0x282

eip:0x7c00

cs:s=0x0, dl=0xffff, dh=0x9b00, valid=1

ss:s=0x0, dl=0xffff, dh=0x9300, valid=7

ds:s=0x0, dl=0xffff, dh=0x9300, valid=1

es:s=0x0, dl=0xffff, dh=0x9300, valid=1

fs:s=0x0, dl=0xffff, dh=0x9300, valid=1

gs:s=0x0, dl=0xffff, dh=0x9300, valid=1

ldtr:s=0x0, dl=0x0, dh=0x0, valid=0

tr:s=0x0, dl=0x0, dh=0x0, valid=0

gdtr:base=0x0, limit=0x0

idtr:base=0x0, limit=0x3ff

dr0:0x0

dr1:0x0

dr2:0x0

dr3:0x0

dr6:0xffff0ff0

dr7:0x400

tr3:0x0

tr4:0x0

tr5:0x0

tr6:0x0

tr7:0x0

cr0:0x60000010

cr1:0x0

cr2:0x0

cr3:0x0

cr4:0x0

inhibit_mask:0

done

由于Linux 0.11内核的32位代码是从绝对物理地址0处开始存放的,因此若想直接执行到32位代码开始处,即head.s程序开始处,我们可以在线性地址0x0000处设置一个断点并运行命令'c'执行到那个位置处。

另外,当直接在命令提示符下打回车键时会重复执行上一个命令;按向上方向键会显示上一命令。其他命令的使用方法请参考'help'命令。

14.1.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执行到这个指定的函数开始处,见如下所示。

lb 0xd1ec # 设置线性地址断点。

c # 连续执行。

(0) Breakpoint 2, 0xd1ec in ?? ()

Next at t=16689666

(0) [0x0000d1ec] 0008:0000d1ec (unk. ctxt): push ebx ; 53

n # 执行下一指令。

Next at t=16689667

(0) [0x0000d1ed] 0008:0000d1ed (unk. ctxt): mov eax, dword ptr ss:[esp 0x8] ; 8b442408

n # 执行下一指令。

Next at t=16689668

(0) [0x0000d1f1] 0008:0000d1f1 (unk. ctxt): mov edx, dword ptr [ds:0x19958] ; 8b1558990100

程序调试是一种技能,需要多练习才能熟能生巧。上面介绍的一些基本命令需要组合在一起使用才能灵活地观察到内核代码执行的整体环境情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: