- 论坛徽章:
- 0
|
本文简单介绍了使用gdb调试gdb和kgdb的方法,供各位对gdb源代码和gdb Remote Serial Protocol分析感兴趣的朋友参考。示例系统为FreeBSD6.1。我把这种调试方法比喻为螳螂捕蝉,黄雀在后,即螳螂版gdb调试蝉版目标程序,黄雀版gdb又去调试螳螂版gdb。
1、准备螳螂版gdb
相关目录说明:
gdb的工程目录(makefile文件)存放在/usr/src/gnu/usr.bin/gdb目录下;
gdb的源代码文件存放在/usr/src/contrib/gdb目录下;
gdb的目标文件(编译结果)存放在/usr/obj/usr/src/gnu/usr.bin/gdb目录下。
(1)修改makefile
到/usr/src/gnu/usr.bin/gdb目录下编辑Makefile.inc文件,在其中的一堆给CFLAGS变量赋值的地方加上一句:
(2)编译版本(在/usr/src/gnu/usr.bin/gdb目录下)
完成上述步骤之后,会在/usr/obj/usr/src/gnu/usr.bin/gdb目录下生成debug版本的gdb程序。这就是我们的螳螂版gdb。
2、准备蝉版目标程序
为了说明问题,这个“蝉”只需要是一个简单程序就可以了,比如:
- 1 int main(void)
- 2 {
- 3 int a, b, c;
- 4 a = 1;
- 5 b = 2;
- 6 c = a + b;
- 7 return c;
- 8 }
复制代码
带-g选项将其编译为debug版本,假设编译结果名为a.out,这就是我们的蝉。
3、准备黄雀版gdb - 这个就不用准备了,用系统自带的gdb即可
4、螳螂捕蝉
用我们刚才编译出来的螳螂版gdb调试蝉版a.out。
- [~]$ /usr/obj/usr/src/gnu/usr.bin/gdb/gdb/gdb a.out
- GNU gdb 6.1.1 [FreeBSD]
- Copyright 2004 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i386-marcel-freebsd"...
- (gdb)
复制代码
5、黄雀在后
另开一个终端,用ps或者top命令看看刚才运行的螳螂版gdb的PID是多少。
- 2670 p0 I+ 0:00.24 /usr/obj/usr/src/gnu/usr.bin/gdb/gdb/gdb a.out
复制代码
我们可以看到,对于我们的例子,螳螂版的gdb的PID是2670。
现在,按以下命令运行黄雀版gdb。
- [~]$ gdb -p 2670 /usr/obj/usr/src/gnu/usr.bin/gdb/gdb/gdb
- GNU gdb 6.1.1 [FreeBSD]
- Copyright 2004 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i386-marcel-freebsd"...
- Attaching to program: /usr/obj/usr/src/gnu/usr.bin/gdb/gdb/gdb, process 2670
- Reading symbols from /lib/libm.so.4...done.
- Loaded symbols for /lib/libm.so.4
- Reading symbols from /lib/libreadline.so.6...done.
- Loaded symbols for /lib/libreadline.so.6
- Reading symbols from /lib/libncurses.so.6...done.
- Loaded symbols for /lib/libncurses.so.6
- Reading symbols from /usr/lib/libgnuregex.so.3...done.
- Loaded symbols for /usr/lib/libgnuregex.so.3
- Reading symbols from /lib/libc.so.6...done.
- Loaded symbols for /lib/libc.so.6
- Reading symbols from /usr/lib/libthread_db.so...done.
- Loaded symbols for /usr/lib/libthread_db.so
- Reading symbols from /libexec/ld-elf.so.1...done.
- Loaded symbols for /libexec/ld-elf.so.1
- 0x2834f20b in poll () from /lib/libc.so.6
- (gdb)
复制代码
至此,螳螂版gdb取得了蝉版a.out的控制权,而黄雀版gdb又取得了螳螂版gdb的控制权,我们现在就可以开始调试螳螂版的gdb了。
6、调试过程示例
(1) 我们首先在黄雀版gdb里面给螳螂版gdb设个断点,就选择gdb在用户输入s(单步调试)命令之后的处理函数step_1():
- (gdb) b step_1
- Breakpoint 1 at 0x8072cb5: file /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/infcmd.c, line 581.
- (gdb)
复制代码
设好断点之后,就可以让螳螂版gdb继续运行了:
(2) 现在我们回到螳螂版gdb的终端,黄雀版gdb已经把控制权交还给了它,于是,我们可以开始用它来调试蝉版a.out了。先在main()开始处设个断点,然后让它运行到这个断点处:
- (gdb) b main
- Breakpoint 1 at 0x80484a8: file foo.c, line 4.
- (gdb) run
- Starting program: /usr/home/ysfp/a.out
- Breakpoint 1, main () at foo.c:4
- 4 a = 1;
- (gdb)
复制代码
(3) 现在我们就可以在螳螂版gdb中使用s命令来触发黄雀版gdb对它的调试了:
执行上述命令之后,螳螂版gdb就失去了响应。我们转到黄雀版gdb的终端:
- (gdb) c
- Continuing.
- Breakpoint 1, step_1 (skip_subroutines=136577025, single_inst=136722816, count_string=0x0)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/infcmd.c:581
- 581 {
- (gdb)
复制代码
这正是我们刚才在黄雀版gdb里面给螳螂版gdb设的断点,现在由于我们在螳螂版gdb里面输入了s命令,它已经停止在了s命令的处理函数step_1处,等待黄雀版gdb的下一个指令。
我们可以来看看螳螂版gdb在step_1()之前的调用栈:
- (gdb) bt
- #0 step_1 (skip_subroutines=136577025, single_inst=136722816, count_string=0x0)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/infcmd.c:581
- #1 0x080c77b3 in execute_command (p=0x8240001 "", from_tty=1)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/top.c:743
- #2 0x08089ede in command_handler (command=0x8240000 "s")
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/event-top.c:500
- #3 0x0808a523 in command_line_handler (rl=0x8282540 "P%(\b\200%(\b")
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/event-top.c:793
- #4 0x2826897f in rl_callback_read_char () from /lib/libreadline.so.6
- #5 0x08089877 in rl_callback_read_char_wrapper (client_data=0x0)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/event-top.c:166
- #6 0x0808b230 in handle_file_event (event_file_desc=0)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/event-loop.c:721
- #7 0x0808ace6 in process_event () at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/event-loop.c:334
- #8 0x0808b495 in gdb_do_one_event (data=0x0) at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/event-loop.c:371
- #9 0x080c72f0 in catcher (func=0x80c73f0 <do_catch_errors>, func_uiout=0x827ed00, func_args=0xbfbfe8d0, func_val=0xbfbfe8c8,
- func_caught=0xbfbfe8cc, errstring=0x0, gdberrmsg=0x0, mask=6)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/top.c:430
- #10 0x080c743a in catch_errors (func=0, func_args=0x0, errstring=0x81d9a96 "", mask=6)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/top.c:535
- #11 0x08154d53 in tui_command_loop (data=0x0) at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/tui/tui-interp.c:150
- #12 0x08071ece in current_interp_command_loop () at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/interps.c:277
- #13 0x080710cf in captured_command_loop (data=0x0) at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/main.c:97
- #14 0x080c72f0 in catcher (func=0x80c73f0 <do_catch_errors>, func_uiout=0x827ed00, func_args=0xbfbfe9f0, func_val=0xbfbfe9e8,
- func_caught=0xbfbfe9ec, errstring=0x0, gdberrmsg=0x0, mask=6)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/top.c:430
- #15 0x080c743a in catch_errors (func=0, func_args=0x0, errstring=0x81d9a96 "", mask=6)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/top.c:535
- #16 0x08071777 in captured_main (data=0x0) at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/main.c:805
- #17 0x080c72f0 in catcher (func=0x80c73f0 <do_catch_errors>, func_uiout=0x821d120, func_args=0xbfbfec10, func_val=0xbfbfec08,
- func_caught=0xbfbfec0c, errstring=0x0, gdberrmsg=0x0, mask=6)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/top.c:430
- #18 0x080c743a in catch_errors (func=0, func_args=0x0, errstring=0x81d9a96 "", mask=6)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/top.c:535
- #19 0x08071d07 in gdb_main (args=0x0) at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/main.c:814
- #20 0x080710bd in main (argc=0, argv=0x0) at /usr/src/gnu/usr.bin/gdb/gdb/../../../../contrib/gdb/gdb/gdb.c:35
- (gdb)
复制代码
在螳螂版gdb和黄雀版gdb之间发生的其它故事,就跟普通的gdb调试一摸一样了。
7、用黄雀版gdb调试螳螂版kgdb
调试螳螂版kgdb和调试螳螂版gdb的方法大同小异。在我们前面的编译过程中,在生成螳螂版gdb的同时,也会在/usr/obj/usr/src/gnu/usr.bin/gdb/kgdb目录下生成螳螂版kgdb。
至于如何使用kgdb通过串口调试远程FreeBSD机器,我已经在另外一篇文章中进行了介绍(http://bbs.chinaunix.net/viewthread.php?tid=822692&extra=page%3D1 )。此处假设目标机已经准备就绪,我们直接使用螳螂版kgdb调试目标机:
- #
- # /usr/obj/usr/src/gnu/usr.bin/gdb/kgdb/kgdb -r /dev/cuad0 /usr/src/sys/i386/compile/DEBUG_KERNEL/kernel.debug
- [GDB will not be able to debug user-mode threads: /usr/lib/libthread_db.so: Undefined symbol "ps_pglobal_lookup"]
- GNU gdb 6.1.1 [FreeBSD]
- Copyright 2004 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i386-marcel-freebsd".
- Switching to remote protocol
- kdb_enter (msg=0x2a "") at ../../../kern/subr_kdb.c:270
- 270 }
- #0 kdb_enter (msg=0x2a "") at ../../../kern/subr_kdb.c:270
- 270 }
- (kgdb)
复制代码
然后在另外一个终端中查看螳螂版kgdb的PID,并通过黄雀版gdb对其进行调试,在发送远程gdb RSP协议包的函数remote_send()处设置断点,然后继续螳螂版kgdb的运行:
- #
- # ps
- ......
- PID TT STAT TIME COMMAND
- 2725 p0 I+ 0:04.53 /usr/obj/usr/src/gnu/usr.bin/gdb/kgdb/kgdb -r /dev/cuad0 /usr/src/sys/i386/compile/DEBUG_KERNEL/kernel.de
- ......
- #
- # gdb -p 2725 /usr/obj/usr/src/gnu/usr.bin/gdb/kgdb/kgdb
- GNU gdb 6.1.1 [FreeBSD]
- Copyright 2004 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i386-marcel-freebsd"...
- Attaching to program: /usr/obj/usr/src/gnu/usr.bin/gdb/kgdb/kgdb, process 2725
- Reading symbols from /lib/libkvm.so.3...done.
- Loaded symbols for /lib/libkvm.so.3
- Reading symbols from /lib/libm.so.4...done.
- Loaded symbols for /lib/libm.so.4
- Reading symbols from /lib/libreadline.so.6...done.
- Loaded symbols for /lib/libreadline.so.6
- Reading symbols from /lib/libncurses.so.6...done.
- Loaded symbols for /lib/libncurses.so.6
- Reading symbols from /usr/lib/libgnuregex.so.3...done.
- Loaded symbols for /usr/lib/libgnuregex.so.3
- Reading symbols from /lib/libc.so.6...done.
- Loaded symbols for /lib/libc.so.6
- Reading symbols from /libexec/ld-elf.so.1...done.
- Loaded symbols for /libexec/ld-elf.so.1
- 0x283717dd in read () from /lib/libc.so.6
- (gdb)
- (gdb) b remote_send
- Breakpoint 1 at 0x80962ed: file /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/remote.c, line 3895.
- (gdb) c
- Continuing.
复制代码
转到螳螂版kgdb的终端,我们执行一个s(单步)命令。注意,我们现在是在调试远程机器,因此我们在螳螂版kgdb中输入的这些运行控制命令都要以gdb RSP协议包的方式发送给目标机处理,因此就会触发我们在黄雀版gdb中给它设置的断点。
现在,螳螂版kgdb已经因为遇到断点而失去了响应,我们转到黄雀版gdb的终端,看到以下信息:
- (gdb) c
- Continuing.
- Breakpoint 1, remote_send (buf=0x824ecb0 "p\006&\b", sizeof_buf=8)
- at /usr/src/gnu/usr.bin/gdb/libgdb/../../../../contrib/gdb/gdb/remote.c:3895
- 3895 putpkt (buf);
- (gdb)
复制代码
我们单步跟踪进去,然后查看remote_send()函数的入参buf中的内容:
- (gdb) s
- 3894 {
- (gdb) s
- 3895 putpkt (buf);
- (gdb) x/8bx buf
- 0xbfbfd850: 0x67 0x00 0x07 0x08 0x08 0xf5 0x37 0x28
- (gdb)
复制代码
remote_send()的入参buf中的内容就是要发送给目标机的RSP协议包的数据部分,这是一个字符串。我们可以看到,这个字符串的长度只有一个字节,数值为0x67,这是字母g的ascii值。根据RSP协议,字符g表示调试机向目标机请求所有通用寄存器的内容。实际上,对于一个s命令而言,也会触发调试机和目标机之间的多次RSP协议交互,这里给出的只是其中的一次而已。剩下的工作就是据此进行RSP协议数据和gdb源代码的分析了。限于篇幅,本文就此打住。
[ 本帖最后由 雨丝风片 于 2006-10-17 17:41 编辑 ] |
|