我正在玩gcc -S
以了解内存和堆栈的工作原理。在这些戏剧中,我发现了一些我不清楚的事情。你能帮我理解原因吗?
当调用函数集被调用的参数时,它使用mov
到esp
而不是push
。不使用push
的优势是什么?
与其堆栈相关的函数将参数指向ebp + (N + offset)
(其中N是为返回地址保留的大小)。我希望看到esp - offset
更容易理解。将ebp
作为基本点到处使用的原因是什么?我知道这些是平等但无论如何?
main
一开始有什么神奇之处?为什么必须以这种方式初始化esp
?
and esp,0xfffffff0
谢谢,
答案 0 :(得分:7)
我假设您在32位环境下工作,因为在64位环境中,参数在寄存器中传递。
问题1
也许你在这里传递一个浮点参数。您无法直接推送这些,因为32位运行时中的push
指令一次推送4个字节,因此您必须分解该值。有时更容易从esp
中减去8并将8字节的四字移动到[esp]
。
问题2
ebp
经常用于在32位代码中索引堆栈帧中的参数和本地。这允许即使在堆栈指针移动时帧内的偏移也是固定的。例如,考虑
void f(int x) {
int a;
g(x, 5);
}
现在,如果您只使用esp
访问了堆栈框架内容,则a
位于[esp]
,返回地址位于[esp+4]
和x
将在[esp+8]
。现在让我们生成调用g
的代码。我们必须先推5然后推x
。但是在推动5之后,x
与esp
的偏移已经改变了!这就是使用ebp
的原因。通常在输入函数时,我们会推送ebp
的旧值来保存它,然后将esp
复制到ebp
。现在ebp
可用于访问堆栈帧内容。当我们处于传递论证的中间时,它不会移动。
问题3
此and
指令将esp
的最后4位清零,将其与16字节边界对齐。由于堆栈向下增长,这很好而且安全。