# 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
来将符号信息单独打包到一个文件中
objcopy
是 GNU 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
来执行下一条指令。
调试实模式的示意图如下:
# 参考
- 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