bochs 中文文档 下载 App

地址断点——物理断点、虚拟断点和 Bootloader 入口下断

三种断点命令

命令含义断点类型触发时机
b <addr>默认断点自动检测看 addr 值选物理或虚拟
pb <addr>Physical Breakpoint物理地址任何时刻 CPU 取指到该物理地址
vb <addr>Virtual Breakpoint虚拟地址CPU 取指到该虚拟地址(由当前 CR3 翻译)

b 命令的自动检测逻辑

当你写 b 0x7c00 时,Bochs 会:

  1. 检查当前 CPU 模式
  2. 如果是实模式(CS 基址 + IP):把 addr 当作物理地址
  3. 如果是保护模式且开启分页(CR0.PG=1):把 addr 当作虚拟地址
  4. 不确定时提示你用 pb 或 vb 显式指定

OS 开发场景(特别是启动早期),显式用 pb 更安全——因为此时分页还没开(CR0.PG=0),虚拟地址就是物理地址,没有歧义。等内核启动了分页再用 vb。

经典场景:Bootloader 入口断点

Bochs OS 开发实验的最经典第一条命令:

bochs:1> pb 0x7c00
bochs:2> c

为什么是 0x7c00?

真实 PC 上,BIOS 上电后做 POST、枚举硬件、找引导设备。找到可引导设备后,会读取设备的首扇区(引导扇区,512 字节)到物理地址 0x7c00,然后设置 CS=0x0000、IP=0x7c00,跳转到这里执行。

这就是你的 Bootloader 的加载位置。在 0x7c00 下物理断点,BIOS 刚跳过来调试器就停住——你可以看到 Bootloader 的第一条指令。

示例调试器输出:

bochs:1> pb 0x7c00
bochs:2> c
(0) Breakpoint 1, 0x0000000000007c00 in ???
Next at t=12345
(0) [0x0000000000007c00] 0000:7c00: (jmp 0x7c18)
bochs:3> u
0000000000007c00: jmp .+0x16       (0x7c18)
0000000000007c02: ...
bochs:4> info registers
...
cs=0x0000  ds=0x0000  es=0x0000  ss=0x0000
eip=0x000000007c00
...

虚拟地址断点:分页开启后

内核初始化分页后,虚拟地址 ≠ 物理地址。此时用 vb 下断点——内核代码编译时通常有固定的虚拟地址(如 0xc0100000 或 0xffffffff80100000):

bochs:1> vb 0xc0100000
bochs:2> c

调试器会在 CR3 翻译后的物理地址上设断点。如果 CR3 换了(进程切换),断点目标也会跟着变——这正是你想要的:无论内核页表怎么换,只要虚拟地址 0xc0100000 被取指就停住。

vb 的陷阱

vb 依赖当前 CR3 指向的页表。如果你在内核早期(CR3 还没初始化或正在切换 CR3)就设 vb,断点可能不会触发。此时用 pb + 手动算物理地址更可靠。

物理地址计算公式(内核线性映射场景):

物理地址 = 虚拟地址 - 内核偏移
# 比如内核在 0xc0000000 之上有 3GB 偏移
# 虚拟 0xc0100000 → 物理 0x00100000

算出物理地址后用 pb 下断点就不受页表变化影响。

单步断点:执行一次自动删除

sb(single breakpoint)设置一个一次性断点——触发后自动删除:

bochs:1> sb 0x7c10       # 下次跑到 0x7c10 就停,之后不影响
bochs:2> c

适合"我只想在某处停一次看看"的场景,用完自动清理。

断点管理

bochs:1> info break      # 列出所有断点
Num Type       Disp Enb Address
1   breakpoint  keep y   0x0000000000007c00 in ???
bochs:2> d 1             # 删除 1 号断点
bochs:3> lb              # 删除所有断点(clear all breakpoints)

Disp 列是"Disposition"——keep 表示每次触发后保留,del 表示触发一次后删除(就是 sb 的行为)。

条件断点(Bochs 支持有限)

Bochs 调试器不支持 GDB 那种复杂的条件断点(如 break 0x7c00 if eax==0)。但有一个变通方法:

在断点触发后立即检查寄存器:

bochs:1> pb 0x7c00
bochs:2> c
# 断点触发,检查 EAX
bochs:3> info registers eax
eax=00000000
# 如果不是期望的值,继续
bochs:4> c

Bochs 3.0+ 版本引入了少量条件支持(b addr if <condition>),但功能远不如 GDB 完整。OS 开发者通常的做法是用 Bochs 下断点、快速查看、继续跑——不追求自动化条件。

内存断点(watchpoint)

Bochs 还支持内存访问断点(watchpoint):

bochs:1> watch read 0x7c00      # 有人读 0x7c00 时停住
bochs:2> watch write 0x1000     # 有人写 0x1000 时停住
bochs:3> watch access 0x2000    # 任何访问 0x2000 时停住

对于调试"某个变量被谁改了"这类问题特别有用——内核的页表项被篡改、设备驱动的状态寄存器被意外重置等场景。

info watch 列出所有 watchpoint,unwatch <num> 删除。