我用gcc编译程序,然后在a.out文件上运行gcc。当我在gdb中运行programm时,出现此错误:
Program received signal SIGSEGV, Segmentation fault.
0xbffff118 in ?? ()
(gdb) backtrace
#0  0xbffff118 in ?? ()
#1  0x00000000 in ?? ()
Run Code Online (Sandbox Code Playgroud)
这是什么意思?同样在我之前,当试图正常运行程序时,我得到了这个错误:
*** stack smashing detected ***: ./benchmark terminated
Aborted (core dumped)
Run Code Online (Sandbox Code Playgroud)
所以我读到这是对缓冲区溢出的保护,我仔细检查了所有内容,应该一切都很好,所以我使用了标志将其禁用:
-fno-stack-protector
Run Code Online (Sandbox Code Playgroud)
编辑:我不发布代码,因为我想弄清楚如何使用gdb而不是让我的代码正常工作。所以我是gdb的新手,但现在我成功使用了几次。我还是不知道是什么?? 意味着在什么情况下gdb不能指向我代码中的相应调用,这会导致错误,为什么帧1的地址为0x0?
当您得到这样的垃圾回溯时,几乎可以肯定意味着您的堆栈被某种方式破坏了,并且实际的返回地址和堆栈帧指针已被覆盖。
该值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
Run Code Online (Sandbox Code Playgroud)
在这里,$espis 0xbffff7c0和$eipis 0x00001870(我使用p/x $eip命令获得了,但是也可以看到它info regs获得所有寄存器)。因此,每个堆栈帧都将是指向堆栈(0xbfff....)更高的指针,后跟一个类似于的地址0x00001870。在内存转储中查找这些内容,我们可以确定这些是堆栈帧:
0xbffff7f8  0x000018aa
0xbffff818  0x000018aa
0xbffff838  0x000018aa
(etc.)
Run Code Online (Sandbox Code Playgroud)
这是我从一个高递归程序中获取的示例,因此这就是返回地址都相同的原因。一旦找到一些未被破坏的良好堆栈框架,您就可以自己跟随框架指针:
(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
...
Run Code Online (Sandbox Code Playgroud)
然后,如果要将指令地址转换为符号名称,则可以再次使用该x命令,并且如果您有调试符号,则gdb将愉快地打印符号名称:
(gdb) x 0x000018aa
0x18aa <add_word+154>:  0x5d18c483
Run Code Online (Sandbox Code Playgroud)
这是在堆栈被粉碎后如何获得实际有用的堆栈跟踪的快速入门。当然,最好首先避免这种情况。我强烈建议您使用-Wall -Wextra -Werror(-pedantic如果可以的话)编译代码,并且-fno-stack-protector除非您有一个非常非常好的理由这样做,否则请不要使用它。