2012-03-21 33 views
93

我有以下的堆栈跟踪。是否有可能做出有用的调试呢?GDB损坏的堆栈帧 - 如何调试?

Program received signal SIGSEGV, Segmentation fault. 
0x00000002 in ??() 
(gdb) bt 
#0 0x00000002 in ??() 
#1 0x00000001 in ??() 
#2 0xbffff284 in ??() 
Backtrace stopped: previous frame inner to this frame (corrupt stack?) 
(gdb) 

从哪里开始看代码的时候我们得到一个Segmentation fault和堆栈跟踪不是那么有用吗?

注:如果我张贴代码,然后将这样的专家会给我答案。我想从SO的指导中找到答案,所以我不在这里发布代码。道歉。看到

+0

也许你的程序跳下到杂草 - 您可以恢复堆栈指针什么? – 2012-03-21 17:36:50

+1

要考虑的另一件事是如果帧指针设置正确。你是在没有优化的情况下建立或传递像'-fno-omit-frame-pointer'这样的标志吗?另外,对于内存损坏,如果它是您的选择,'valgrind'可能是更合适的工具。 – FatalError 2012-03-21 17:36:51

回答

128

那些虚假不会忽略(0x00000002等)实际上是PC值,而不是SP值。现在,当你得到这样的SEGV的,用假(很小)PC的地址,它是由于通过一个虚假的函数指针调用99%的时间。请注意,C++中的虚拟调用是通过函数指针实现的,所以虚拟调用的任何问题都可以用相同的方式显示出来。

间接调用指令只是推动PC呼叫到堆栈后,然后设置PC目标值(假在这种情况下),因此,如果这发生了什么事,你可以很容易地通过手动撤消将PC从堆栈中弹出。在32位x86代码,你只是做:

(gdb) set $pc = *(void **)$esp 
(gdb) set $esp = $esp + 4 

采用64位x86代码,你需要

(gdb) set $pc = *(void **)$rsp 
(gdb) set $rsp = $rsp + 8 

然后,你应该能够做一个bt,并找出其中的代码确实是。

其他1%的时间,错误将由于覆盖堆栈,通常通过溢出存储在堆栈上的数组。在这种情况下,您可能会通过使用类似valgrind

+2

真棒回答,谢谢! – 2012-05-09 11:23:04

+0

当你没有运行程序时,有没有办法获得bt,你只需要核心转储? – George 2014-02-05 17:26:52

+3

@George:'GDB可执行corefile'将打开GDB与可执行文件和核心文件,在这一点上,你可以做'bt'(或者上面的命令,然后'bt')... – 2014-03-27 18:58:26

6

看看你的一些其他的寄存器,如果他们中的一个在他们缓存的堆栈指针。从那里,你可能能够检索一个堆栈。另外,如果这是嵌入式的,通常栈被定义在一个非常特殊的地址。使用它,你有时也可以得到一个体面的堆栈。这一切都假定,当你上升到多维空间,你的程序没有吐遍布沿途的记忆...

21

假设堆栈指针是有效的...

可能无法确切地知道SEGV从回溯中出现 - 我认为前两个堆栈帧被完全覆盖。 0xbffff284看起来像一个有效的地址,但接下来的两个不是。对于在堆栈仔细一看,你可以尝试以下方法:

GDB $ X/32ga $ RSP

或变体(更换32与另一个号码)。这将从巨型(g)大小的堆栈指针开始打印出一些字(32),格式为地址(a)。请输入'help x'获取更多格式信息。

与插入检测前哨一些“printf”式的代码可能不是一个坏主意,在这种情况下。

+0

非常有帮助,谢谢 - 我有一个堆栈,只返回三个帧,然后点击“Backtrace停止:与此帧相同的前一帧(损坏的堆栈?)”;我之前在CPU异常处理程序中的代码中完成了与此类似的操作,但除了'info symbol'外,不记得如何在gdb中执行此操作。 – leander 2013-03-08 19:05:24

+13

32位ARM器件的FWIW:'x/256wa $ sp' =) – leander 2013-03-08 19:05:58

+1

@leander您能告诉我什么是X/256wa?我需要它用于64位ARM。一般来说,如果你能解释它是什么,这将是有帮助的。 – 2015-04-17 12:15:46

34

这样的工具来获得更清晰的情况。如果情况非常简单,那么Chris Dodd's answer是最好的。它看起来像是通过一个NULL指针跳转。

但是,在程序崩溃之前,程序在脚,膝,脖子和眼睛中拍摄时会覆盖堆栈,弄乱框架指针和其他恶魔。如果是这样,那么解开哈希不太可能会显示你土豆和肉。

更有效的解决方案将是在调试器下运行的程序,和跨过功能,直到程序崩溃。一旦确定崩溃的功能,再次启动并进入该功能,并确定它调用哪个功能导致崩溃。重复,直到找到唯一违规的代码行。 75%的时间,修复将是显而易见的。

在情况另外25%,代码的所谓的问题的行是一个红色的鲱鱼。它会对(无效)条件做出反应,在—之前建立许多行,之前可能有数千行。如果是这样的话,选择最好的当然取决于很多因素:主要是你的代码和经验的理解它:

  • 也许设置一个调试监视点或插入关键变量诊断printf的会导致必要阿哈!
  • 也许改变与不同的输入的测试条件将提供比调试更深入的了解。
  • 也许第二一双眼睛会迫使你检查你的假设或收集的证据被忽视。
  • 有时候,只需要吃饭和思考收集的证据。

祝你好运!

+11

如果第二双眼睛不可用,那么橡皮鸭已经被证明可以作为替代品。 – Matt 2012-03-21 18:52:33

+1

注销缓冲区的末尾也可以做到这一点。它可能不会在您将缓冲区的末尾写入的地方崩溃,但是当您退出该功能时,则会死亡。 – phyatt 2016-09-23 20:15:58