我正在写一份总结堆栈的报告。如果你点击我的个人资料,你会看到我已经这样做了一段时间。现在,我有一些麻烦,因为在GDB上它向我展示了与visual studio不同的东西。
因此,我不太确定我对基指针和堆栈指针的理解,如果我错了,我希望有人能引导我向正确的方向。
对于x86计算机,堆栈通常是向下增长的(从较高的内存地址到较低的内存地址)。
所以当程序开始时,我们调用main函数。
-
一般来说,在每个函数调用的入口处,在当前esp位置创建一个堆栈,这就是我们所说的"堆栈的顶部"。这是正确的吗?
-
当旧的ebp被推入堆栈时,它是否被推到esp最初指向的位置?
-
之后,esp将向下移动到指向一个空内存位置,这是正确的吗?
-
最后,esp总是变化的,向下移动指向下一个可用的内存空间。对吗?
-
esp是每字节移动,还是每4字节移动?
我知道有很多问题。但是谢谢你的时间!
谢谢您的回复,先生!
@iSciurus
我很困惑为什么每个人都定义esp指向最近被推入堆栈的条目。
对于x86,由于堆栈向下增长,从您的解释来看,esp将首先指向堆栈的最低地址。当我看汇编代码时,我们有
0x080483f4 <+0>: push %ebp
0x080483f5 <+1>: mov %esp,%ebp
0x080483f7 <+3>: sub $0x10,%esp
esp减16个字节。这就是这个函数调用栈的大小。局部变量位于返回地址(ebp-4、ebp-8等)之后。那么esp在这里的总体目的是什么?据我所知,当我们试图访问小于该地址的地址时,就会发生堆栈溢出。
最后一件事是:当我们说堆栈的顶部时,我们指的是最低的地址(对于x86)吗?
这是我脑海中的画面(向下延伸)
[Parameter n ]
...
[Parameter 2 ]
[Parameter 1 ]
[Return Address ] 0x002CF744
[Previous EBP ] 0x002CF740 (current ebp)
[Local Variables ]
-- ESP
很抱歉有这么长的问题。但是我真的很感谢你的帮助。
-
为了正确,堆栈帧是在当前esp位置创建的,而不是堆栈本身。堆栈在线程启动时创建一次,每个线程都有自己的堆栈,它只是进程内存空间中的一个区域。堆栈帧实际上是在每个函数入口处创建的,它是线程堆栈中的一个区域。
-
不,它被压入地址[old_rsp - 4](或者在x64中[old_rsp - 8]),因为esp是堆栈的顶部,指向最低使用的地址。堆栈中的下一个DWORD(或QWORD)是空闲的,ebp被压入那里。
-
是的,这通常是通过sub esp, value
完成的 。首先,esp向下移动,指向堆栈中最低的已使用地址,而不是下一个可用空间。其次,要记住esp可以指向任何地方,而不仅仅指向堆栈:除非你使用与堆栈相关的指令,如push/pop.
Esp按机器字长移动:在x86中每4字节移动一次,而在x64中每8字节移动一次。
esp的总体目的几乎总是相同的:存储堆栈的顶部。所有与堆栈相关的指令,比如pop/push,都使用esp作为它们的参数。在x86中,ebp和esp都用于存储有关堆栈帧的信息(相应的是底部和顶部)。也许你对这个冗余感到困惑。然而,在x64中,只有rsp用于基于堆栈的参数,rbp是一个通用寄存器。
如何处理堆栈中的缓冲区溢出呢?当代码试图写高于数组(或结构体或其他)的最后一个元素时,通常会发生缓冲区溢出。堆栈向下增长,但数组向上增长。当我们写得更高时,我们可以访问返回地址、SEH处理程序以及调用者的内部变量。
是的,当我们说top时,我们指的是最低的地址。因此,大多数调试器以相反的顺序显示堆栈:
-- ESP
[Local Variables ]
[Previous EBP ] 0x002CF740 (current ebp)
[Return Address ] 0x002CF744
[Parameter 1 ]
[Parameter 2 ]
...
[Parameter n ]
在这里,ESP指向所有数据之上的值,看起来更像"顶部"。虽然它仍然是使用率最低的地址。