Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103643896
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-17 20:40:47

作者:snow888  出处: 
本文简单介绍了使用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变量赋值的地方加上一句:

CODE:
CFLAGS+= -g
(2)编译版本(在/usr/src/gnu/usr.bin/gdb目录下)

CODE:
make clean
make
完成上述步骤之后,会在/usr/obj/usr/src/gnu/usr.bin/gdb目录下生成debug版本的gdb程序。这就是我们的螳螂版gdb。

2、准备蝉版目标程序
为了说明问题,这个“蝉”只需要是一个简单程序就可以了,比如:

CODE:
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。

CODE:
[~]$ /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是多少。

CODE:
2670  p0  I+     0:00.24 /usr/obj/usr/src/gnu/usr.bin/gdb/gdb/gdb a.out
我们可以看到,对于我们的例子,螳螂版的gdb的PID是2670。
现在,按以下命令运行黄雀版gdb。

CODE:
[~]$ 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():

CODE:
(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继续运行了:

CODE:
(gdb) c
Continuing.
(2) 现在我们回到螳螂版gdb的终端,黄雀版gdb已经把控制权交还给了它,于是,我们可以开始用它来调试蝉版a.out了。先在main()开始处设个断点,然后让它运行到这个断点处:

CODE:
(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对它的调试了:

CODE:
(gdb) s
执行上述命令之后,螳螂版gdb就失去了响应。我们转到黄雀版gdb的终端:

CODE:
(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()之前的调用栈:

CODE:
(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 , 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 , 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 , 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机器,我已经在另外一篇文章中进行了介绍(%3D1 )。此处假设目标机已经准备就绪,我们直接使用螳螂版kgdb调试目标机:

CODE:
#
# /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的运行:

CODE:
#
# 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中给它设置的断点。

CODE:
(kgdb) s
[New Thread 0]
现在,螳螂版kgdb已经因为遇到断点而失去了响应,我们转到黄雀版gdb的终端,看到以下信息:

CODE:
(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中的内容:

CODE:
(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源代码的分析了。限于篇幅,本文就此打住。
阅读(169) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~