GDB

来自osdev
跳到导航 跳到搜索

GDB 已经成为Linux和其他免费Unices上的标准调试器。 它是源代码级调试器,而不是机器级调试器,如Bochs调试器;这可能是优点,也可能是优点,这取决于你的视角。

虽然调试系统代码不是其预期用途,但它做得很好; 它可以直接与一些模拟器一起使用,根本不需要修改你的代码,也可以在串行线上使用。 其中一些选项涉及在操作系统中实现远程串行协议存根(stub桩)。

远程调试

我们对GDB的远程调试设施感兴趣。 摘自gdb手册, “如果你试图调试在无法以通常方式运行GDB的计算机上运行的程序,则使用远程调试通常很有用。 例如,你可以在操作系统内核上使用远程调试,或者在没有足够强大的通用操作系统来运行全功能调试器的小型系统上使用远程调试。”

对于远程调试,应该在需要调试的远程程序中实现远程存根。 这意味着内核应该包含远程存根,以便在调试会话期间与主机GDB对话。 这需要更改内核源代码,如果在测试(真实)机器上运行内核并从另一台机器进行调试,那么这是必须的。 如果你正在使用模拟器(Bochs或QEMU),则可以使用编译到模拟器中的GDB存根。

实现GDB存根

要调试(使用GDB)在真实机器上运行的内核,内核需要包含一个GDB存根。 对于i386平台,gdb源代码包含gdb-stub.c的参考实现。 它需要内核中的以下三个函数来从串行端口读取/写入并设置异常处理程序。

   getDebugChar(); //从串行端口读取字符
   putDebugChar(int); //向串口写入字符
   exceptionHandler(int exception_number, void *exception_address); // 设置异常处理程序

在内核中实现上述函数,包含gdb-stub.c编译,并在内核启动期间调用以下函数以连接到gdb主机。

   InitSerialPort(sys_gdb_port, 9600, UART_DATA_BIT_8, UART_PARITY_NONE, UART_STOP_BIT_1);   /*设置串口*/
   set_debug_traps();   /* 设置异常处理程序 */
   kprintf("Waiting for GDB(0x%X) : ", sys_gdb_port );
   __asm__("int3");   /*与GDB主机同步的断点异常*/

现在启动你的测试机器,你的内核将等待GDB主机连接。

使用模拟器存根

QEMU -只需使用-s -S命令行开关启动QEMU,GDB就可以连接到QEMU

Bochs -为了使GDB能够与 Bochs对接,需要使用 --enable-gdb-stub 参数配置Bochs。 bochsrc(或正在使用的任何配置脚本)需要将gdbstub行设置为类似“gdbstub:enabled=1, port=1234, text_base=0,data_base=0,bss_base=0”的内容。

启动GDB

配置模拟器/内核后,它将等待来自GDB的连接。 主机上的GDB可以这样启动:

$ gdb YOUR-KERNEL
.
.
.
(gdb) target remote :1234
Remote debugging using :1234
0x0000fff0 in ?? ()
(gdb)

如果你正在调试在真实机器上运行的内核,那么使用target remote /dev/tty2代替网络端口。

如果它在 "target remote" 行之后提示你,询问是否杀死已经在调试中的程序,请回答 “n”。

如果你使用的是64位内核,则可能需要使用set命令设置地址大小。

gdbinit文件

将常用命令放在你工作目录中的gdbinit文件里。 例如:

 file kernel.bin
 target remote localhost:1234

这将在你启动gdb时连接到GDB-stub,并从kernel.bin加载符号。


Emacs中的GDB

Emacs与GDB集成良好。 尝试使用M-x gdb,当它要求你提供gdb的命令行参数时,使用--annotate=3。 这将允许Emacs在你放置断点并单步遍历内核时跟随。

虚拟串行端口

如果你在内核中具有gdb存根并在模拟器中运行内核,则可以使用COM端口重定向器来创建虚拟串口。 创建虚拟串行端口后,该对中的一个端口应用于模拟器; 另一个应该在gdb中指定用于远程调试。

com0com可以在http://com0com.sourceforge.net/中使用,而在Linux中可以使用伪终端来创建虚拟串口对。

另见

外部链接

de:GDB