Bochs调试常用命令
1 Bochs简介
Bochs
(发音:box)是一个开源的模拟器和调试工具。Bochs
支持对一系列指令集架构和硬件进行模拟:
- 指令集架构:
Bochs
支持对x86
、x86-64
(即基于x86
架构的64位扩展)、IBM PC
等指令架构进行模拟和调试, - 硬件:
Bochs
支持对处理器(包括保护模式)、内存、硬盘、显示器、以太网、BIOS、IBM PC等等常见硬件的仿真。
许多操作系统在Bochs
上运行,包括DOS
、某些版本的Microsoft Windows
、AmigaOS 4
、BSD
、Linux
、MorphOS
、Xenix
和Rhapsody
(Mac OS X
的前身)。
Bochs
能也能够在许多操作系统上运行,例如Windows
、Windows Mobile
、Linux
、Mac OS X
、iOS
以及PlayStation 2
Bochs
主要用于操作系统开发:
- 一方面,当调试的操作系统崩溃时,运行中的
Bochs
崩溃,但是运行Bochs
的操作系统不会崩溃,修改代码后就可以重新调试了,因此Bochs
可以调试仿真操作系统 - 另一方面,
Bochs
支持指令级别的单步运行和模拟硬件。
但是也可以当做一个虚拟机来运行其他操作系统,或者运行不兼容的旧的软件(如电脑游戏)
Bochs
的优点在于能够模拟与当前主机不同指令集架构的主机,例如在SPARC
系统里模拟x86
主句。而缺点是Bochs
的速度却慢得多。
Bochs
一个开源x86
虚拟机软件。在它的实现中定义了各种数据结构来模拟硬件,用软件模拟硬件缺点是速度比较慢,毕竟全是软件来模拟,要在软件中模拟各种中断,能不慢吗。不过它的功能非常强大,而且可移植性强,原则上只要是GCC
支持的平台,这个平台上就可以编译、运行Bochs
,从而保证了Bochs
在各平台上的畅通无阻。
所以总的来说,Bochs
是做系统开发常用的虚拟机,调试系统内核很方便。
2 安装Bochs
安装Bochs
一般是从源码编译安装,因为我们需要在当前的平台上模拟其他平台。
2.1 配置Bochs
选择安装目录
因为我们可能需要多个版本、不同功能的Bochs
,所以我们可以在编译的时候指定安装目录,注意,必须是绝对路径
./configure --prefix=安装目录
开启调试器
Bochs
包含两部分:虚拟机
和调试器
,而调试器属于可选功能,所以在配置Bochs
时需要使用--enable-debugger
和`标志开启调试模块。例如:
./configure --enable-debugger # 还有后面的选项
需要注意调试功能在 2.5.4 版本以上才有,确保下载的代码是高于这个版本的。
开启反汇编
光能单步调试没用,还得开启反汇编功能,配置的时候使用--enable-disasm
开启反汇编功能
./configure --enable-disasm # 后面还有别的选项
注意,新版本中--enable-disasm
已经被移除了,开启了--enable-debugger
自动就会打开反汇编,至少我用的2.7
版本是这样的。
开启IO调试
为了能够调试外部硬件,需要开启IO调试功能,配置的时候加上--enable-iodebug
标志
./configure --enable-iodebug # 后面还有别的选项
支持x86调试
我们需要模拟x86
指令集架构,所以需要配置的时候加上--enable-x86-debugger
标志
./configure --enable-x86-debugger # 后面还有别的选项
支持gdb调试
上面的--enable-debugger
是使用Bochs
自带的调试器来调试,当然Bochs
自带的调试器没有GDB
好用。所以我们也可以在配置的时候加上--enable-gdb-stub
来支持GDB
调试
./configure --enable-gdb-stub # 后面还有别的选项
注意,--enable-gdb-stub
和--enable-debugger
只能开一个,不然会报错。
支持图形化窗口
Linux
平台上编译默认是不会开启图形化窗口的。想要开启图形化窗口,需要在编译的时候加上---with-x
以支持x windows
作为图形显示服务器,或者使用--with-x11
支持x11
图形显示服务。
./configure --with-x --with-x11 # 后面还有别的选项
注意,这x11
是在Linux
上的图形显示。MacOS
上需要使用sdl
或者sdl2
,即
./configure --with-sdl2
开启图形窗口后,效果如下
常用配置如下
Bochs 2.7
下使用下面的命令配置:
Linux
上
./configure --prefix=安装目录 --enable-debugger --enable-iodebug --enable-x86-debugger --with-x --with-x11
MacOS
上
./configure --predix=安装目录 --enable-debugger --enable-iodebug --enable-x86-debugger --with-sdl2
2.2 编译、安装
编译安装使用下面的命令即可:
make
make install
2.3 例子
下面是MacOS
的安装例子
mkdir ../bochs-test
./configure --prefix=/Users/jack/project/bochs/bochs-test/ --enable-debugger --enable-iodebug --enable-x86-debugger --with-sdl2
make
make install
tree ../bochs-test -L 3
3 Bochs常用调试命令
大体上协Bochs
的调试命令分为:
Debugger Control
类Execution Control
类Breakpoint Management
类CPU and Memory Contents
类
3.1 查看寄存器
汇编代码中,调试最常用的功能就是查看寄存器的内容。
r
查看通用寄存器sreg
查看段寄存器creg
查看控制寄存器dreg
查看调试寄存器info cpu
查看所有寄存器
3.2 查找和定位代码
次常用的肯定是控制代码执行流程,代码执行到想要仔细跟踪的那个部分。
b 内存地址
设置断点如
b 0x7c00
,在线性地址0x7c00处设置断点。info break
查看设置过的断点c
继续执行代码,直到遇到断点或者按下Ctrl+D
一般设置断点后,想让代码恢复执行,就使用这个命令。
s
单步执行单步执行一行代码,和高级语言调试器的step into按钮类似,遇到函数调用会跳转到函数内部执行。单步执行命令也可以带参数,指定执行的次数,如
s 100
就是单步执行100次。n
执行下一行它和单步执行类似,单步执行遇到循环和函数时会跳转到内部,而n命令会执行完循环和函数,类似于step over,这样在遇到大量的循环或者较长的函数时,可以用n命令来执行到下一行。
n命令能跳转到下一行是因为loop或者call执行有明显的结束标记(前者通过cx寄存器,后者通ret指令),如果遇到用jmp语句写的循环这种情况,没有明显结束标记的,可以用下面的u命令反汇编代码的地址,找到循环的下一行指令的地址,然后给该地址加一个断点就能达到同样的效果。
u
反汇编代码直接使用u命令会反汇编当前执行的指令,它可以加参数,
u /反汇编数量 起始地址
,如u /20 0x7c00
就是从0x7c00处开始,反汇编20条指令,如果没有起始地址就是从当前地址开始。它还可以反汇编一个范围的代码,
u 起始地址 结束地址
,如u 0x7c00 0x7cff
就是反汇编0x7c00到0x7cff的代码。
3.3 查看内存
x /nuf 地址
查看线性地址处的内存内容。xp /nuf 地址
查看物理地址处的内存内容。
n 指定要显示的内存单元的数量
u 显示的内存单元的大小,如下参数之一:
b 单个字节
h 半个字(2 字节)
w 一个字(4 字节)
f 打印的格式。如下类型之一:
x 按照十六进制形式打印
d 按照十进制形式打印
u 以无符号的10进制打印
o 按照八进制形式打印
t 按照二进制行是打印
3.4 Tips
Bochs还有一个比较有用的设计就是,当你输入指令后,直接按回车键(Enter Key)会重复上一次的命令。比如上一个命令是单步执行s
,此时直接按回车键就相当于s
的功能。
4 调试指令手册
4.1 执行控制
命令 | 解释 |
---|---|
c / cont / continue |
继续执行 |
s / step [count] |
执行count条指令,如果不指定参数,默认值为1 |
s / step [cpu] [count] |
对于对称多处理器结构模拟,在cpu上执行count条指令,count的默认值为1 |
s / step all [count] |
对于对称多处理器结构模拟,所有cpu上都执行count条指令,count的默认值为1 |
Ctrl+C |
停止执行,返回到命令行提示符 |
Ctrl+D |
如果在空行上执行,则退出调试器 |
q / quit / exit |
退出调试器,继续执行 |
4.2 断点操作
命令 | 解释 |
---|---|
① vbreak / `vb segment:offset |
设置虚拟地址指令断点,segment和offset指段地址和段内偏移量 |
② lbreak / lb addr |
在线性地址指令上设置断点 |
③ pbreak / bp [*] addr |
在物理地址上设置断点 |
④ break / b [*] addr |
同上。 * 符号是兼容GDB命令,为可选参数 |
info break |
显示当前所有断点状态 |
bpe n |
开启断点 |
bpd n |
关闭断点 |
delete / del / d n |
删除断点 |
注意:① ② ③ ④ 的命令都可以设置条件,即都可以变成条件断点。具体做法是在命令之后添加if condition
,如vbreak 0x008:0x001 if "条件表达式"
4.3 内存观察点
内存观察点类似于内存的监听器,当指定地址的内存产生读取或者写入事件时,会产生中断。
命令 | 解释 |
---|---|
watch read/r addr |
在物理地址addr上插入读观察点 |
watch write/w addr |
在物理地址addr上插入一个写观察点 |
watch |
显示当前内存观察点的状态 |
watch stop |
当遇到观察点时,停止模拟执行(默认) |
watch continue |
在遇到观察点时,不要停止模拟执行 |
unwatch addr |
移除指定物理地址上的观察点 |
unwatch |
移除所有的观察点 |
trace-mem on/off |
开启/关闭 内存访问追踪 |
4.4 内存操作
命令 | 解释 |
---|---|
x /nuf addr |
在线性地址addr处检查内存内容,nuf的解释在前面列出 |
xp /nuf addr |
在物理地址 addr处查看内存内容,nuf的解释在前面列出 |
setpmem addr datasize value |
在内存位置addr处设置datasize大小内存,值为 value |
writemem filepath addr datasize |
从线性地addr处dump出datasize个字节到文件filepath中 |
crc addr1 addr2 |
显示物理地址范围 addr1到addr2之间内容的 CRC值 |
4.5 查看信息
命令 | 解释 |
---|---|
r / reg / regs / registers |
查看通用寄存器内容 |
fp / fpu |
查看FPU寄存器内容 |
mmx |
查看MMX寄存器内容 |
sse / xmm |
查看SSE寄存器内容 |
ymm |
查看AVX寄存器内容 |
sreg / dreg / creg |
查看 段 / 调试 / 控制 寄存器内容 |
info cpu |
查看所有寄存器内容 |
info eflags |
查看标志寄存器内容 |
info break |
显示当前所有断点状态 |
info tab |
显示分页地址转换 |
info device |
显示指定设备的状态 |
4.6 寄存器操作
命令 | 解释 |
---|---|
set reg = expr |
修改reg寄存器值为expr |
注意:只能修改通用寄存器和指令寄存器。不能够修改标志寄存器,段寄存器,浮点寄存器和SIMD寄存器。
4.7 反汇编
命令 | 解释 |
---|---|
u addr1 addr2 |
在给定的线性地址范围内反汇编指令,包含start处指令,不包含end处指令 |
u switch-mode |
在Intel和 AT&T两种汇编风格之间切换 |
u size = n |
设定反汇编命令的位数,使用0,16,32。值0意思是使用当前的CS段寄存器,默认值是0 |
set u on |
每次停止执行时就自动反汇编当前的指令 |
set u off |
和上一条相反,不自动反汇编 |
4.8 指令跟踪
命令 | 解释 |
---|---|
trace on |
反汇编每一条执行的指令。引起异常的指令都没有真正执行,因此也不会被跟踪 |
trace off |
关闭指令跟踪功能 |
4.9 指令编程环境
Bochs的instrument功能,提供了运行时的各种钩子函数。它也是可选功能,在编译安装时需要开启--enable-instrumentation
选项指定。
./configure [...] --enable-instrumentation
./configure [...] --enable-instrumentation="instrument/stubs"
自定义的代码要创建一个独立的目录,例如”instrument/myinstrument”,将”instrument/stubs”目录拷贝进去,然后使用如下的指令设定:
./configure [...] --enable-instrumentation="instrument/myinstrument"
指令命令:
instrument [command] 用[command]调用BX_INSTR_DEBUG_CMD指令回调
4.10 show指令
命令 | 解释 |
---|---|
show |
打印当前的符号信息显示模式 |
show mode |
当处理器切换模式时打印 |
show int |
当产生中断时打印 |
show call |
当产生调用时打印 |
show ret |
当函数返回时打印 |
show off |
关闭打印 |
show dbg-all |
开启所有显示标志 |
show dbg-none |
关闭所有显示标志 |
4.11 其他命令
命令 | 解释 |
---|---|
ptime |
打印当前的时间(从开始模拟到现在的ticks) |
sb delta |
在未来执行中插入一个时间断点delta。delta是一个64位的整数,跟着字母”L”,例如1000L |
sba time |
在时间time处插入一个时间断点 。time 同上面的delta |
print-stack [num] |
打印栈顶端的num个字 |
modebp |
触发CPU模式转换断点 |
ldsym [global] filename [offset] |
从文件filename加载符号。 |
5 Bochs配置
Bochs
在运行的时候是需要bochrc
这个配置文件的,所以下面给出来我常用的Bochs
配置
# bochrc.source
###############################################
# Configuration file for Bochs
###############################################
# 第一步,首先设置Bochs在运行过程中能够使用的内存,本例为32MB。
# 关键字为:megs
megs: 32
# 第二步,设置对应真实机器的BIOS和VGA BIOS.
# 对应两个关键字为:romimage 和 vgaromimage
# LINUX: romimage: file=/usr/share/bochs/BIOS-bochs-latest
# LINUX: vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
# MACOS: romimage: file=/opt/homebrew/share/bochs/BIOS-bochs-latest
# MACOS: vgaromimage: file=/opt/homebrew/share/bochs/VGABIOS-lgpl-latest
# 设置图形显示库
# MACOS: display_library: sdl2
# 第三步,设置Bochs所使用的磁盘,软盘的关键字为floppy。
# 若只有一个软盘,则使用floppya即可,若有多个,则为floppya,floppyb...
#floppya: 1_44=a.img, status=inserted
# 第四步,选择启动盘符。
#boot: floppy
boot: disk
# 第五步,设置日志文件的输出。
log: log.bochsrc.disk
# 第六步,开启或关闭某些功能。
# 下面是关闭鼠标,并打开键盘。
mouse: enabled=0
# LINUX: keyboard: keymap=$BXSHARE/keymaps/x11-pc-us.map
# MACOS: keyboard: keymap=$BXSHARE/keymaps/sdl2-pc-us.map
# 硬盘设置
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="PATH", mode=flat, cylinders=CYLINDERS, heads=HEADS, spt=SPT
#gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
相应的,用来生成配置信息的脚本是
# genrc.sh
# check system
sys=$(uname -s)
case $sys in
Linux)
os=LINUX
;;
Darwin)
os=MACOS
;;
FreeBSD)
os=LINUX
;;
*)
os=UNKNOWN
;;
esac
if [ "$os" == "UNKNOWN" ]; then
echo "Unknown OS, exit..."
exit
fi
# get disk image
shell_folder=$(cd "$(dirname $0)" || exit; pwd)
cd $shell_folder
bin_file=$(grep -d skip . * | grep -e 'Binary' | grep -v ':' | awk '{print $3}')
bin_info=($(bximage -func=info $bin_file -q | awk '/geometry/{print $3}' | awk -F '/' '{print $1,$2,$3}'))
echo "Binary file detected: $bin_file, C/H/S: ${bin_info[0]}/${bin_info[1]}/${bin_info[2]}"
echo "Using $os config, writing config to bochsrc.run"
sed \
-e "s/# $os: //g"\
-e "s/PATH/$bin_file/g"\
-e "s/CYLINDERS/${bin_info[0]}/g"\
-e "s/HEADS/${bin_info[1]}/g"\
-e "s/SPT/${bin_info[2]}/g"\
$shell_folder/bochsrc.source > $shell_folder/bochsrc
echo 'Run `bochs -f bochsrc` to start bochs'
# bochsrc -f bochsrc
用来编译的脚本是
shell_folder=$(cd "$(dirname $0)" || exit; pwd)
# get disk image
shell_folder=$(cd "$(dirname $0)" || exit; pwd)
cd $shell_folder
bin_file=$(grep -d skip . * | grep -e 'Binary' | grep -v ':' | awk '{print $3}')
if [ -z $bin_file ]; then
read -p "Binary file not detected, enter the name to create [hd60M.img]:" bin_file
if [ -z $bin_file ]; then
bin_file="hd60M.img"
fi
echo "Generating $bin_file..."
bximage -q -func=create -hd=60 -imgmode=flat $shell_folder/$bin_file
else
bin_info=($(bximage -func=info $bin_file -q | awk '/geometry/{print $3}' | awk -F '/' '{print $1,$2,$3}'))
echo "Binary file detected: $bin_file, C/H/S: ${bin_info[0]}/${bin_info[1]}/${bin_info[2]}"
fi
echo "You can run genrc.sh to auto-config bochsrc"
echo "Compiling..."
mkdir -p $shell_folder/exe
nasm $shell_folder/a/boot/mbr.S -f bin -o $shell_folder/exe/mbr.bin
dd if=$shell_folder/exe/mbr.bin of=$bin_file bs=512 count=1 conv=notrunc
echo "Done, run genrc.sh to auto-config"
所以要生成配置文件的话,就是
tree ./
# 编译
compile.sh
# 生成配置信息
./genrc.sh
tree ./
# 调试
bochs -f bochrc