gdb与stange错误:??。这是什么意思?

时间:2013-07-05 02:03:09

标签: c gdb

我使用gcc编译我的程序,然后在a.out文件上运行gcc。 当我在gdb中运行程序时,我收到此错误:

Program received signal SIGSEGV, Segmentation fault.
0xbffff118 in ?? ()
(gdb) backtrace
#0  0xbffff118 in ?? ()
#1  0x00000000 in ?? ()

这是什么意思? 在我之前,当我试图正常运行该程序时,我收到了这个错误:

*** stack smashing detected ***: ./benchmark terminated
Aborted (core dumped)

所以我读到这是对缓冲区溢出的保护,我仔细检查了所有应该有的东西,所以我使用标志禁用了这个:

-fno-stack-protector

编辑:我没有发布代码,因为我想弄清楚如何使用gdb而不是让我的代码正常工作。所以我是gdb的新手,但我现在已经成功使用了几次。我仍然无法弄清楚是什么?意味着,在什么情况下gdb可以指向我而不是我的代码中的相应调用,这会导致错误以及为什么帧1的地址为0x0?

2 个答案:

答案 0 :(得分:4)

当你得到这样的垃圾回溯时,它几乎肯定意味着你的堆栈被某种方式粉碎,实际的返回地址和堆栈帧指针已被覆盖。

0xbffff118几乎肯定是堆栈中的地址。我相信在许多x86 Linux编译器上,编译器在虚拟地址0xc0000000启动堆栈,并从那里向下增长,因此任何以0xbfff开头的地址都很可能是堆栈地址。

通常,指令指针永远不应该在堆栈内。通常发生的方式是指向堆栈的值会覆盖存储在堆栈中的返回地址,然后当前函数返回时,它将返回到覆盖的值。如果堆栈是不可执行的,就像它应该的那样,这将立即引发信号;如果堆栈是可执行的,那么它可能会在以后的几个指令之前崩溃,除非你被恶意利用,在这种情况下你将会遇到不好的时间。

一旦你的堆栈被粉碎,backtrace命令就没有用了,就像你发现的那样。找出正在发生的事情的最好方法是手动检查堆栈并搜索可能的返回地址和帧指针。您可以使用x命令转储内存区域(运行help x以获取详细信息)。我喜欢使用x/<NUMBER>wx转储为4字节的十六进制值。所以,这里是如何从堆栈指针$esp开始转储一堆数据:

(gdb) x/64wx $esp
0xbffff7c0: 0x00000073  0xbffff9e9  0x0000000b  0x00000012
0xbffff7d0: 0xbffff9e8  0x0be04aa0  0xbffff7f8  0x000018aa
0xbffff7e0: 0x0be04aa0  0xbffff9e8  0x00000000  0x00000002
0xbffff7f0: 0xbffff9e7  0x09a0bb10  0xbffff818  0x000018aa
0xbffff800: 0x09a0bb10  0xbffff9e7  0x00000002  0x0000000e
0xbffff810: 0xbffff9e6  0x015377f0  0xbffff838  0x000018aa
0xbffff820: 0x015377f0  0xbffff9e6  0x00000008  0x00000011
0xbffff830: 0xbffff9e5  0x01537860  0xbffff858  0x000018aa
0xbffff840: 0x01537860  0xbffff9e5  0x00000003  0x0000000f
0xbffff850: 0xbffff9e4  0x001ddbc0  0xbffff878  0x000018aa
0xbffff860: 0x001ddbc0  0xbffff9e4  0x00000018  0x00000017
0xbffff870: 0xbffff9e3  0x00177c50  0xbffff898  0x000018aa
0xbffff880: 0x00177c50  0xbffff9e3  0xbffff8b8  0x00000000
0xbffff890: 0xbffff9e2  0x00176050  0xbffff8b8  0x000018aa
0xbffff8a0: 0x00176050  0xbffff9e2  0xbffff9e1  0x0000000c
0xbffff8b0: 0xbffff9e1  0x00174920  0xbffffb08  0x00001b8a

此处$esp0xbffff7c0$eip0x00001870(我使用p/x $eip命令获得,但也可以使用info regs {1}}获取所有寄存器)。因此,每个堆栈帧都将成为堆栈中较高的指针(0xbfff....),后跟一个类似于0x00001870的地址。在内存转储中寻找这些,我们可以非常肯定这些是堆栈帧:

0xbffff7f8  0x000018aa
0xbffff818  0x000018aa
0xbffff838  0x000018aa
(etc.)

这是我从一个高度递归的程序中抓取的一个例子,这就是为什么返回地址都是一样的。一旦找到一些没有被破坏的好堆栈帧,你可以自己跟随帧指针:

(gdb) x/2wx 0xbffff7f8
0xbffff7f8: 0xbffff818  0x000018aa
(gdb) x/2wx 0xbffff818
0xbffff818: 0xbffff838  0x000018aa
(gdb) x/2wx 0xbffff838
0xbffff838: 0xbffff858  0x000018aa
(gdb) x/2wx 0xbffff858
0xbffff858: 0xbffff878  0x000018aa
...

然后,如果您想将指令地址转换为符号名称,您可以再次使用x命令,如果您有调试符号,gdb将很乐意打印符号名称:

(gdb) x 0x000018aa
0x18aa <add_word+154>:  0x5d18c483

这是一个快速入门,介绍如何在堆栈被粉碎时获得实际有用的堆栈跟踪。当然,最好首先避免这种情况。我强烈建议您使用-Wall -Wextra -Werror(以及-pedantic编译代码,如果可以的话),当然不要使用-fno-stack-protector,除非您确实 < / em>有充分理由这样做。

答案 1 :(得分:0)

一旦堆栈出现乱码,就无法判断发生了什么...... 在纯粹的c中,通常很难粉碎堆栈,你是在进行任何组装还是使用gotos / labels?那些是我见过的典型路线......

基本上会发生什么是返回地址被覆盖,当你试图返回那个垃圾地址时,堆栈没有意义。