GDB损坏的堆栈帧 – 如何debugging?
我有以下堆栈跟踪。 是否有可能做出任何有用的debugging?
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的指导中find答案,所以我不在这里发布代码。 道歉。
那些虚假地址(0x00000002等)实际上是PC值,而不是SP值。 现在,当你得到这种SEGV,一个伪造的(非常小的)PC地址,99%的时间是由于通过一个虚假函数指针调用的。 请注意,C ++中的虚拟调用是通过函数指针实现的,所以虚拟调用的任何问题都可以以相同的方式显示出来。
间接调用指令只是在调用堆栈后将PC推入堆栈,然后将PC设置为目标值(在这种情况下是伪造的),所以如果发生这种情况,可以通过手动将PC从堆栈中popup。 在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之类的工具来获得更清晰的情况
如果情况相当简单, Chris Dodd的答案是最好的。 它看起来像是通过一个NULL指针跳转。
然而,程序可能会在撞击之前在脚,膝,脖子和眼睛中射出 – 覆盖了堆栈,弄乱了框架指针和其他恶魔。 如果是这样,那么解开散列不可能显示你土豆和肉。
更有效的解决scheme将是在debugging器下运行该程序,并跨越函数直到程序崩溃。 一旦确定崩溃的function,再次启动并进入该function,并确定它调用哪个函数导致崩溃。 重复,直到find唯一违规的代码行。 75%的时间,修复将是显而易见的。
在另外25%的情况下,所谓的违规代码行是一个红鲱鱼。 它会对(无效的)条件做出反应,之前可能会build立许多行,也许以前有数千行。 如果是这样的话,最好的select取决于很多因素:大部分是你对代码和经验的理解:
- 也许设置一个debugging器观察点或插入诊断
printf
的关键variables将导致必要的阿哈! - 也许用不同的input来改变testing条件将比debugging提供更多的见解。
- 也许第二双眼睛会迫使你检查你的假设或收集忽视的证据。
- 有时候,只需要吃饭和思考收集的证据。
祝你好运!
假设堆栈指针是有效的…
从回溯中确切地知道SEGV发生的位置可能是不可能的 – 我认为前两个堆栈帧被完全覆盖。 0xbffff284看起来像一个有效的地址,但接下来的两个不是。 仔细查看堆栈,可以尝试以下操作:
gdb $ x / 32ga $ rsp
或一个变体(用另一个数字replace32)。 这将从巨型(g)大小的堆栈指针开始打印出一些字(32),格式为地址(a)。 请input'help x'获取格式的更多信息。
在这种情况下,用一些哨兵'printf'来testing你的代码可能不是一个坏主意。
看看你的其他一些寄存器,看看它们中的一个是否有caching的堆栈指针。 从那里,你可能能够检索一个堆栈。 另外,如果这是embedded式的,通常栈被定义在非常特定的地址。 使用这个,你也可以有一个体面的堆栈。 这一切都假设,当你跳到超空间,你的程序并没有呕吐沿途的内存…