以下为本文所使用的系统环境与版本:

  • macOS Ventura 13.4.1 (c) - 英特尔芯片
  • GDB 13.2
  • QEMU 8.0.4

# GDB 调试 QEMU

该方法不局限于调试 mbr/loader,也可以用来调试内核。步骤如下:

首先运行 qemu,并在 qemu 启动后立刻监听 GDB 连接端口(qemu 不会执行任何指令):

# Linux
qemu -s -S <harddrive.img>
# Mac 调试实模式
qemu-system-i386 -s -S <harddrive.img>
# Mac 调试保护模式
qemu-system-x86_64 -s -S <harddrive.img>

此时 qemu 会监听 1234 端口并等待 GDB 的连接,当 GDB 连接后,将调试控制权交给 GDB,我们可以用如下命令来让 GDB 连接 1234 端口(如果是远程调试的话,需要填写相应 URL 与端口):

gdb
(gdb) target remote localhost:1234

# 调试内核

调试内核时为了能有源代码进行对应,我们需要将将调试用到的符号信息也编译出来,有两种方法:

  • 使用 GCC 编译源代码时加入 -g 参数,这样会将所有符号信息打包进内核程序中(程序会比较大)
  • 使用 objcopy 来将符号信息单独打包到一个文件中

objcopyGNU Binutils 包的一部分,在 Mac 上并没有链接到 bin 下,所以不能直接使用,可以用以下步骤来手动建立链接。

# 1. 找到 `objcopy` 可执行文件的位置
mdfind -name objcopy
# 2. 将可执行文件链接到 `/usr/local/bin` 中
ln -s /usr/local/Cellar/binutils/2.41/bin/objcopy /usr/local/bin/objcopy

然后就可以使用 objcopy 来将调试符号输出到单独文件 kernel.sym 中:

objcopy --only-keep-debug kernel.elf kernel.sym

也可以在 GDB 中直接引入符号文件(可以是可执行文件,只要包含了 debug 信息即可):

(gdb) symbol-file kernel.elf  ;kernel.elf is the actual unstripped kernel image in this case

# 调试实模式

由于 GDB 并不原生支持实模式的调试,所以对于实模式下本身应为 16 位的二进制也会按 32 位去反汇编,导致出现问题,幸好已经有大神写过相应的脚本了,我们直接使用就可以了。

首先需要下载以下三个文件(操作系统从 0 到 1 系列对应的 github 项目中已经全部包含)

wget https://raw.githubusercontent.com/qemu/qemu/master/gdb-xml/i386-32bit.xml
wget https://raw.githubusercontent.com/mhugo/gdb_init_real_mode/master/gdbinit_real_mode.txt
echo '<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i8086</architecture><xi:include href="i386-32bit.xml"/></target>' > target.xml

首先使用 qemu -s -S <harddrive.img> 启动 qemu,然后再使用以下命令即可正确调试实模式了

gdb -ix "gdb_init_real_mode.txt" -ex "set tdesc filename target.xml" -ex "target remote localhost:1234" -ex "br *0x7c00" -ex "c"

这个命令其实并不复杂,用 -ix 参数加载 gdb 脚本,就是该脚本实现了对实模式的支持,同时对 gdb 做了增强,会自动输出一些信息(寄存器、栈、代码等待)。

至于后面的 -ex 参数,其实就是直接使得 gdb 启动后依次执行这些命令:

  • target remote localhost:1234 :连接 qemu 并进行调试
  • br *0x7c00 :在 0x7c00 处下断点,其实就是 mbr 开始的地方,也就是我们的代码开头。
  • c :使程序继续执行,直到触发断点,接着程序会停在 0x7c00 处。

注意:在 mbr/loader 的调试中,由于没有源代码,我们需要用 ni/si 来执行下一条指令。

调试实模式的示意图如下:

gdb调试实模式

# 参考

  • https://ternet.fr/gdb_real_mode.html
  • https://wiki.osdev.org/Kernel_Debugging
  • https://stackoverflow.com/questions/32955887/how-to-disassemble-16-bit-x86-boot-sector-code-in-gdb-with-x-i-pc-it-gets-tr

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Gality 微信支付

微信支付

Gality 支付宝

支付宝