我使用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?
答案 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
此处$esp
为0xbffff7c0
,$eip
为0x00001870
(我使用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)
基本上会发生什么是返回地址被覆盖,当你试图返回那个垃圾地址时,堆栈没有意义。