Assembly 为什么我们使用堆栈指针来执行此操作?

Assembly 为什么我们使用堆栈指针来执行此操作?,assembly,stack,Assembly,Stack,我刚刚开始汇编,我在代码中找到了这些指令和类似的指令: sub esp , something mov esp, dword ptr [esp + something] 为什么要这样做?我听说是关于堆栈框架的初始化。你能解释一下吗,或者给我指出一些关键词让我去寻找吗?乍一看英特尔x86“Something”通常是所有局部(堆栈分配)变量的总长度,如果它出现在汇编子例程的开头。由于堆栈向下增长(即朝较低的地址),这为它们保留了空间。 你确定第二行就是你写的吗?esp已经指向空闲区域,因此在例程

我刚刚开始汇编,我在代码中找到了这些指令和类似的指令:

sub esp , something
mov esp, dword ptr [esp + something] 

为什么要这样做?我听说是关于堆栈框架的初始化。你能解释一下吗,或者给我指出一些关键词让我去寻找吗?

乍一看英特尔x86“Something”通常是所有局部(堆栈分配)变量的总长度,如果它出现在汇编子例程的开头。由于堆栈向下增长(即朝较低的地址),这为它们保留了空间。 你确定第二行就是你写的吗?esp已经指向空闲区域,因此在例程递归调用自身或调用另一个函数之前,可以将参数推送到局部变量下方的堆栈上。我不认为在esp被调整以适应本地变量之后立即将某些内容加载到esp中有什么意义,除非它用于允许(下一个)被调用方访问调用方的堆栈帧,就像在Pascal中有嵌套函数一样。

每个C(除了一些
内联
函数,而不仅仅是C函数)函数有它的开始和结束。开头通常如下所示:

push ebp
mov ebp, esp
sub esp, x
第一行推送旧堆栈帧。由于在程序运行期间调用了许多函数,所以当调用另一个函数时,函数可以在堆栈上保存它们的堆栈帧。堆栈帧(通常存储在
EBP
寄存器中)是每个局部变量寻址的基础。假设您有以下代码:

int main() {
    __volatile int localVariable = 0x10;
    printf("%d");
}
变量
localVariable
可以静态地存储在某个位置(例如在地址
0x410000
上),但如果再次调用
main
,则该地址上的值将被覆盖。因此,需要动态分配之类的东西。这就是堆栈帧的用途;保存上一个堆栈帧,并相对于堆栈指针的实际位置为局部变量“分配”位置。在这种情况下,
localVariable
应始终处于位置
EBP sizeof(int)

第二行和第三行实际上是分配“新”内存的代码。第三行从
ESP
(堆栈向下扩展)->中减去一些值,这就是函数不覆盖其变量的原因;每个功能都有自己的位置。第二行保存旧的堆栈指针,所以当被调用函数从其堆栈帧返回时,它将保存上一个函数的堆栈指针

函数的标准结尾是

mov esp, ebp
pop ebp
ret
或 离开 ret 第二个示例中的
leave
是第一个示例中前两行的替代,因为函数经常返回到前一个函数的执行

寻址
EBP-something
通常意味着访问被调用(当前)函数的局部变量

寻址
EBP+someting
通常意味着访问传递给函数的参数


我必须注意,
EBP
中存储的地址实际上并不指向参数,而是指向函数的返回地址,该地址是调用函数时由
call
指令推送的。存储在
EBP+4
上的值可以是第一个变量(旧的做法,例如用于具有可变参数数的函数,如
printf
),与最后一个变量相同(对于Java来说是典型的,它通过创建新数组并只传递对它的引用来处理可变计数参数-
对象…值)

您的示例看起来有点不寻常,但此链接将解释堆栈帧设置:另请参见: