我有以下堆栈跟踪。是否可以从中进行任何有用的调试?
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专家会给我答案。我想从SO那里得到指导并自己找到答案,所以我不会在这里发布代码。道歉。
答案 0 :(得分:148)
那些伪造的地址(0x00000002等)实际上是PC值,而不是SP值。现在,当你得到这种SEGV时,伪造的(非常小的)PC地址,99%的时间是由于通过伪造的函数指针调用。请注意,C ++中的虚拟调用是通过函数指针实现的,因此虚拟调用的任何问题都可以以相同的方式显示。
间接调用指令只是在调用堆栈后将PC推送到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
等工具更清晰地了解情况答案 1 :(得分:42)
如果情况相当简单,Chris Dodd's answer是最好的。它看起来像是通过NULL指针跳过。
然而,程序可能会在撞击之前在脚,膝盖,颈部和眼睛中射击 - 覆盖叠加,弄乱帧指针和其他邪恶。如果是这样,那么解开散列不太可能向你展示土豆和肉。
更有效的解决方案是在调试器下运行程序,并逐步执行函数直到程序崩溃。一旦识别出崩溃功能,再次启动并进入该功能并确定它调用哪个功能导致崩溃。重复,直到找到单个有问题的代码行。 75%的时间,修复将是显而易见的。
在其他25%的情况下,所谓的违规行代码是红鲱鱼。它将对之前设置许多行的(无效)条件做出反应 - 可能之前有数千行。如果是这种情况,选择的最佳课程取决于许多因素:主要是您对代码的理解和经验:
printf
将导致必要的 A ha! 答案 2 :(得分:25)
假设堆栈指针有效......
可能无法准确知道SEGV从回溯中发生的位置 - 我认为前两个堆栈帧被完全覆盖。 0xbffff284似乎是一个有效的地址,但接下来的两个不是。要仔细查看堆栈,您可以尝试以下操作:
gdb $ x / 32ga $ rsp
或变体(用另一个数字替换32)。这将从巨型(g)大小的堆栈指针开始打印出一些字(32),格式为地址(a)。输入“help x”以获取有关格式的更多信息。
在这种情况下,使用一些标记'printf'来检测代码可能不是一个坏主意。
答案 3 :(得分:6)
查看一些其他寄存器,看看其中一个寄存器是否有堆栈指针缓存在其中。从那里,您可以检索堆栈。此外,如果嵌入了这种情况,通常会在非常特定的地址定义堆栈。使用它,你有时也可以得到一个体面的堆栈。这一切都假设当你跳到超空间时,你的程序并没有在整个记忆中呕吐...
答案 4 :(得分:2)
如果它是一个堆栈覆盖,那么这些值很可能与程序中可识别的东西相对应。
例如,我发现自己正在看堆栈
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x000000000000342d in ?? ()
#2 0x0000000000000000 in ?? ()
和0x342d
是13357,当我为它编写应用程序日志时,结果证明它是一个node-id。这立即帮助缩小了可能发生堆栈覆盖的候选站点。
答案 5 :(得分:0)
有趣...我们在旧的 C 应用程序中遇到了完全相同的事情。十六进制的前 2 个堆栈跟踪值指针是从端口读取的数据字节。我只是碰巧注意到一个,因为它很熟悉。