作为UNIX/Linux下使用广泛的调试器,gdb不仅提供了丰富的命令,还引入了对脚本的支持:一种是对已存在的脚本语言支持,比如python,用户可以直接书写python脚本,由gdb调用python解释器执行;另一种是命令脚本(command file),用户可以在脚本中书写gdb已经提供的或者自定义的gdb命令,再由gdb执行。在这篇文章里,我会介绍一下如何写gdb的命令脚本。(一) 自定义命令gdb支持用户自定义命令,格式是:
define commandName statement ...... end
其中statement
可以是任意gdb命令。此外自定义命令还支持最多10个输入参数:$arg0
,$arg1
?……?$arg9
,并且还用$argc
来标明一共传入了多少参数。
下面结合一个简单的C程序(test.c),来介绍如何写自定义命令:
#include int global = 0;int fun_1(void){ return 1;}int fun_a(void){ int a = 0; printf("%d\n", a);}int main(void){ fun_a(); return 0;}
首先编译成可执行文件:
gcc -g -o test test.c
接着用gdb进行调试:
[root@linux:~]$ gdb testGNU gdb (GDB) 7.6Copyright (C) 2013 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-unknown-linux-gnu".For bug reporting instructions, please see:...Reading symbols from /data2/home/nanxiao/test...done.(gdb) b fun_aBreakpoint 1 at 0x4004d7: file test.c, line 12.(gdb) rStarting program: /data2/home/nanxiao/testBreakpoint 1, fun_a () at test.c:1212 int a = 0;(gdb) bt#0 fun_a () at test.c:12#1 0x0000000000400500 in main () at test.c:18
可以看到使用bt
(backtrace
缩写)命令可以打印当前线程的调用栈。我们的第一个自定义命令就是也实现一个backtrace
功能:
define mybacktrace btend
怎么样?简单吧,纯粹复用gdb提供的命令。下面来验证一下:
(gdb) define mybacktraceType commands for definition of "mybacktrace".End with a line saying just "end".>bt>end(gdb) mybacktrace#0 fun_a () at test.c:12#1 0x0000000000400500 in main () at test.c:18
功能完全正确!
接下来定义一个赋值命令,把第二个参数的值赋给第一个参数:
define myassign set var $arg0 = $arg1end
执行一下:
(gdb) define myassignType commands for definition of "myassign".End with a line saying just "end".>set var $arg0 = $arg1>end(gdb) myassign global 3(gdb) p global$1 = 3
可以看到global
变量的值变成了3
。
对于自定义命令来说,传进来的参数只是进行简单的文本替换,所以你可以传入赋值的表达式,甚至是函数调用:
(gdb) myassign global fun_1()(gdb) p global$2 = 1
可以看到global
变量的值变成了1
。
除此以外,还可以为自定义命令写帮助文档,也就是执行help
命令时打印出的信息:
document myassign assign the second parameter value to the first parameterend
执行help
命令:
(gdb) document myassignType documentation for "myassign".End with a line saying just "end".>assign the second parameter value to the first parameter>end(gdb) help myassignassign the second parameter value to the first parameter
可以看到打印出了myassign
的帮助信息。
(二) 命令脚本首先对于命令脚本的命名,其实gdb没有什么特殊要求,只要文件名不是gdb支持的其它脚本语言的文件名就可以了(比如.py)。因为这样做会使gdb按照相应的脚本语言去解析命令脚本,结果自然是不对的。
其次为了帮助用户写出功能强大的脚本,gdb提供了如下的流程控制命令:(1)条件命令:if...else...end
。这个同其它语言中提供的if
命令没什么区别,只是注意结尾的end
。(2)循环命令:while...end
。gdb同样提供了loop_break
和loop_continue
命令分别对应其它语言中的break
和continue
,另外同样注意结尾的end
。
另外gdb还提供了很多输出命令。比方说echo
命令,如果仅仅是输出一段文本,echo
命令特别方便。此外还有和C语言很相似的支持格式化输出的printf
命令,等等。
脚本文件的注释也是以#
开头的,这个同很多其它脚本语言都一样。
最后指出的是在gdb中执行脚本要使用source命令,例如:“source xxx.gdb”。
(三) 一个完整的例子
最后以一个完整的gdb脚本(search_byte.gdb)做例子,来总结一下本文提到的内容:
define search_byte if $argc != 3 help search_byte else set $begin_addr = $arg0 set $end_addr = $arg1 while $begin_addr <= $end_addr if *((unsigned char*)$begin_addr) == $arg2 printf "Find it!The address is 0x%x\n", $begin_addr loop_break else set $begin_addr = $begin_addr + 1 end end if $begin_addr > $end_addr printf "Can't find it!\n" end endenddocument search_byte search a specified byte value(0 ~ 255) during a memory usage: search_byte begin_addr end_addr byteend
这个脚本定义了search_byte
命令,目的是在一段指定内存查找一个值(unsigned char
类型):需要输入内存的起始地址,结束地址和要找的值。命令逻辑可以分成3个部分:(a) 首先判断输入参数是不是3个,如果不是,就输出帮助信息;(b) 从起始地址开始查找指定的值,如果找到,打印地址值并退出循环,否则把地址加1,继续查找;(c) 如果在指定内存区域没有找到,打印提示信息。
另外这个脚本还定义了search_byte
的帮助信息。
仍然以上面的C程序为例,来看一下如何使用这个gdb脚本:
[root@linux:~]$ gdb testGNU gdb (GDB) 7.6Copyright (C) 2013 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-unknown-linux-gnu".For bug reporting instructions, please see:...Reading symbols from /data2/home/nanxiao/test...done.(gdb) p &global$1 = (int *) 0x600900 (gdb) p global$2 = 0(gdb) source search_byte.gdb(gdb) search_byte 0x600900 0x600903 0Find it! The address is 0x600900(gdb) search_byte 0x600900 0x600903 1Can't find it!
可以看到global
的值是0
,起始地址是0x600900
,结束地址是0x600903
。在global
的内存区域查找0
成功,查找1
失败。
受篇幅所限,本文只是对gdb命令脚本做了一个粗浅的介绍,旨在起到抛砖引玉的效果。如果大家想更深入地了解这部分知识,可以参考gdb手册的相关章节:Extending GDB (https://sourceware.org/gdb/onlinedocs/gdb/Extending-GDB.html)。最后向大家推荐一个github上的.gdbinit文件:https://github.com/gdbinit/Gdbinit/blob/master/gdbinit,把这个弄懂,相信gdb脚本文件就不在话下了。
参考文献:(1)Extending GDB (https://sourceware.org/gdb/onlinedocs/gdb/Extending-GDB.html);(2)捉虫日记(http://www.ituring.com.cn/book/909)。
(全文完)如果您喜欢此文请点赞,分享,评论。
原创文章转载请注明出处:如何写gdb命令脚本 小额赞助本站::我要赞助
看天,看雪,安安静静,不言不语都是好风景。