最近在弄一个自己的 hobby OS,作为菜鸟在调试时候积累一些经验,记录一下。
Bochs 调试
Bochs 自带调试功能,但是如果你是 apt 装上的是不行的,下源码来自己编译,编译选项为:
./configure --enable-debugger --enable-disasm
这个我只是尝试过,在 OS 的 loader 阶段可能会用到,当如果进入 C 语言实现部分的代码如何调试?我希望看到 C 的源码级别调试,而不是汇编的。
Bochs + gdb 调试
同样需要在编译的时候加上选项,这个选项必须注意,否则在 gdb 调试的时候会出现”Cannot find bounds of current function”之类的问题。
./configure --enable-debugger --enable-disasm --enable-gdb-stub
诡异的是这个–enable-gdb-stub 选项和上面的 –enable-debugger 选项只能二选一。也行,编译出来后重命名吧。编译完成后在 Bochs 的配置文件.bashrc 中加上这么一行:
gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
另外注意 kernel 的代码也需要加入-g 编译选项。最后在编译完成后的文件是带调试信息的,但是我们在用 Bochs 启动的 img 文件不需要这些,现在比如 kernel.elf 是带编译信息的 kernel
文件,用下面的这个步骤去掉调试信息,据说也可以用 strip 来。
cmd="objcopy -R .pdr -R .comment -R .note -S -O binary kernel.elf kernel.bin"
cat boot.bin setup.bin kernel.bin > ../a.img;
Bochs 使用的是这个 a.img 文件, gdb 载入的是 kernel.elf 文件。
启动 Bochs 后会等待 gdb 连进来 (其实 Qemu 也可以这样进行调试的),查资料过程中发现还可在调试的目录加上.gdbinit,这样每次启动 gdb 就不那么麻烦了:
file ./objs/kernel.elf
target remote localhost:1234
set disassembly-flavor intel
b kmain
一些有用 tips
OS 的代码中经常会有内联汇编,有的时候一条内联过去就崩溃了,所以在 gdb 里需要查看反汇编语句和 registers。下面这些 gdb 指令比较有用:
(gdb) info line main.c:26 (查看 main.c:26 行在目标文件中的位置,为 0x1cbc)
Line 26 of "./kernel/main.c" starts at address 0x1cbc <kmain> and ends at 0x1cc2 <kmain+6>.
(gdb) info line *0x1cbc (上面的反操作)
Line 26 of "./kernel/main.c" starts at address 0x1cbc <kmain> and ends at 0x1cc2 <kmain+6>.
(反汇编 kmain 函数,箭头指向的是当前运行的汇编代码)
(gdb) disas kmain
Dump of assembler code for function kmain:
=> 0x00001cbc <+0>: push ebp
0x00001cbd <+1>: mov ebp,esp
0x00001cbf <+3>: sub esp,0x28
0x00001cc2 <+6>: mov eax,DWORD PTR [ebp+0x8]
0x00001cc5 <+9>: mov ds:0x5ccc,eax
0x00001cca <+14>: call 0x2a29 <init_video>
0x00001ccf <+19>: mov DWORD PTR [esp+0x4],0xb
0x00001cd7 <+27>: mov DWORD PTR [esp],0x4777
0x00001cde <+34>: call 0x2a40 <puts_color_str>
0x00001ce3 <+39>: mov DWORD PTR [esp+0x4],0xa
0x00001ceb <+47>: mov DWORD PTR [esp],0x478d
0x00001cf2 <+54>: call 0x2a40 <puts_color_str>
0x00001cf7 <+59>: cli
0x00001cf8 <+60>: call 0x3876 <time_init>
0x00001cfd <+65>: call 0xc13 <gdt_init>
要正确的看到反汇编最好设置好 gdb 里面的汇编指令集,对于 Nasm 设置”set disassembly-flavor intel”,在.gdbinit 里面弄好就行。
最后 info registers 查看 cpu 寄存器内容,info registers %eax 只查看 eax 内容,而
info all-registers 会把 cpu 的所有寄存器内容显示出来,不过 cr0,cr3 这些貌似没有 :(。看看这里 GDB 参考。