如何在C或Python代码中控制gdb而不使用GDB Python API?

14 投票
3 回答
5797 浏览
提问于 2025-04-16 22:15

我正在尝试用Python或C语言写一个程序,能够通过gdb来调试C代码。

我看过Tom的解决方案从Python调用和控制GDB。不过这些方案基本上都是关于如何在Python中脚本化gdb的。因为我打算用arm-gdb来调试嵌入式程序,所以我不能在我的gdb中启用Python脚本功能。

我的目标是创建一个gdb的高级抽象。比如说,启动gdb,设置一些断点,然后在我的代码中继续执行。我也看了一些关于gdb/mi接口的资料。但是,有人能告诉我如何使用gdb/mi接口来创建一个gdb进程,并通过C或Python代码与gdb进行通信吗?(幸运的是,我的arm-gdb支持gdb/mi接口)。

3 个回答

0

那用一下 http://www.noah.org/python/pexpect/ 呢?这个是 Python 版本的 http://en.wikipedia.org/wiki/Expect,它非常适合用来自动化处理一些外部命令的任务。

2

你提到的链接主要是关于“如何从GDB调用Python”,但你其实是在问“如何从Python或C调用GDB”。这里推荐使用GDB/MI接口,这个是最合适的选择。Eclipse、Emacs和KDevelop等工具都使用GDB/MI来简化调试界面。我个人在使用KDevelop时,曾经用过三种不同的交叉编译的gdb版本,分别针对ARM、AVR和H8S。MI协议的设计是为了让软件能够解析,所以它的语法非常规范。

在谷歌搜索中,我找到了一个Python GDB封装工具,这个可以帮助你入门。

5

如上面评论中所承诺的,我把我的(早期、不完整、几乎肯定有bug的)Ruby项目发布到了http://github.com/mcarpenter/rubug

这里有个例子(你可以在examples/breakpoint找到)。函数check_for_crash是一个回调函数,可能在程序调用factorial后被调用。这个断点需要一个函数名(fac;前面的冒号表示这是一个Ruby符号,在这里可以理解为一种轻量级的字符串)。

EXE = 'factorial'

def check_for_crash(gdb, event)
  case event.type
  when :command_response
    raise RuntimeError, 'oops' unless
  [ :done, :running ].include? event.response.result
  when :breakpoint
    puts 'Breakpoint reached'
    pp event
    gdb.continue
  when :exit
    puts 'Exit'
    gdb.stop_event_loop
    exit
  end
end

gdb = Rubug::Gdb.new
resp = gdb.file EXE
gdb.register_callback(method :check_for_crash)
gdb.break(:fac)
gdb.run '5 > /dev/null'
gdb.start_event_loop

需要提醒你的是,代码可能有点...乱。目前(这是我停下来的地方)大部分功能都不能正常工作(因为我在工作中途更新了gdb,见下面的语法部分)。

不过,目录中有一堆示例可能会对你有帮助。要尝试运行它们,你需要做类似这样的事情:

rake clean
rake grammar
rake make 
cd examples/simple_fuzzer
ruby -I ../../lib -r rubygems simple_fuzzer.rb

考虑到写这段代码时的情况,如果你有选择的话,建议使用ruby1.8(当时我不太喜欢1.9,可能在1.9下会有字符串编码的问题)。

响应的解析是通过treetop完成的,http://treetop.rubyforge.org,这是一种PEG解析器。看着语法,我相信可以简化。你需要使用gem install ...来安装这个(以及其他需要的gem)。

如果你想用Python的方式来做,以下是一些额外的建议:

文档

除了“使用GDB调试”(第22章)外,几乎没有其他文档。我把这个PDF和第22章作为单独文件放到了docs部分。

异步

这个协议是异步的(起初我以为这是命令/响应类型的协议,这个想法是错误的)。如果我重新实现这个,我可能会使用像event machine或libevent这样的东西,而不是自己写select()循环。

语法

语法有点...混乱。虽然文档(27.2.2)说响应“由零个或多个带外记录组成,后面可选地跟一个结果记录”:

`output -> ( out-of-band-record )* [ result-record ] "(gdb)" nl`

你需要知道,由于任何东西都可以随时到达,read()在套接字上显然可以返回异步/结果/更多异步/终止符(!)。例如,我在当前的gdb中看到:

=thread-group-started,id="i1",pid="1086"
=thread-created,id="1",group-id="i1"
^running
*running,thread-id="all"
(gdb)

^开头的行是结果记录,其他的都是异步(然后是终止符)。这似乎是规范中的一个相当重要的缺陷。

速度

我的主要关注点是安全性,我对MI(机器接口)感兴趣,用于自动模糊测试、二进制检查等。出于这个目的,GDB/MI太慢了(在调试器中启动程序的成本)。你的体验可能会有所不同。

MI / CLI映射

在标准的gdb CLI命令集中,有些东西我无法用MI命令实现。我有一些类似这样的骨架代码:

gdb = Gdb::MI.new
gdb.cli(:file, '/bin/ls')
gdb.cli(:set, :args, '> /dev/null')
gdb.cli(:run)
gdb.cli(:quit)

(我觉得这对我们这些不精通MI但对gdb有了解的用户来说很清晰)。我现在记不清那些有问题的东西是什么了(自从我看这个已经过去一年了),但如果我想起来了,我会回来更新这个。

替代方案

当我刚开始这条路时,我发现了Jamis Buck的一篇博客文章:http://weblog.jamisbuck.org/2006/9/25/gdb-wrapper-for-ruby。这个文章把gdb命令行会话包装在popen()中,这让我有点不安。特别是人们可能会认为它不够稳定,因为gdb对CLI输出的稳定性没有保证。你可能会(或可能不会)更喜欢这种方法。

如果你在Windows上,PyDbg / PeiMei可能会引起你的兴趣:http://code.google.com/p/paimei/

你可能还会喜欢这本书《灰帽Python:黑客的Python编程》(Seitz)。同样,这本书主要是针对Windows的,但可能会给你带来灵感。

撰写回答