为什么我们需要在调用函数时递减堆栈指针

时间:2014-10-01 07:05:40

标签: assembly x86 stack stack-pointer

我一直在研究装配已经有一段时间了,我开始掌握它,但是我似乎无法理解的一件事就是为什么我们需要减少堆栈指针以留下漫游对于局部变量,请看一下这段代码:(用64位GNU编译器编译的代码,AT& T语法)

pushq   %rbp

movq    %rsp, %rbp

subq    $48, %rsp

call    __main
movl    $0, -4(%rbp)
movl    $4, -8(%rbp)
movl    -8(%rbp), %edx
movl    -4(%rbp), %eax
addl    %edx, %eax
movl    %eax, -12(%rbp)
movl    -4(%rbp), %edx
movl    -12(%rbp), %eax
addl    %eax, %edx
movl    -8(%rbp), %eax
addl    %edx, %eax
movl    %eax, -16(%rbp)
addq    $48, %rsp
popq    %rbp
ret

在这个小程序中,我可以想象在不需要将esp减少到48的情况下完成所有这些操作。我可以使用基指针将值从堆栈移到堆栈上,并且只需将esp指向相同的位置就绪弹出ebp并返回。 有人可以澄清为什么有必要为局部变量留下“空间”。 谢谢!! 如果这看起来像一个愚蠢的问题我道歉

4 个答案:

答案 0 :(得分:2)

您是否希望函数调用的每个函数都必须非常了解将变量放在堆栈中的位置?

很多函数调用其他函数 - 递减堆栈指针是你的函数说“我正在使用堆栈的这个位”的方式


“leaf”方法 - 从不调用其他函数的方法 - 确实可以用你建议的样式编写 - 因为没有其他代码片段可以自己使用堆栈。

答案 1 :(得分:2)

如果发生中断,地址低于RSP的任何东西都是合理的游戏 - 操作系统会在不问你的情况下擦除它(即用自己的数据替换)。中断一直发生。 Ergo - 你必须保留你在RSP或以下的所有东西。

此外,调用其他函数会推送返回地址。除非RSP上方有空白,否则会覆盖您的数据。

答案 2 :(得分:0)

从堆栈指针中减去常量是如何为局部变量分配空间。编译器或汇编程序员将知道这些变量的位置,作为rbp的负偏移,或来自rsp的正(或零)偏移。

你展示的例子有点奇怪,因为它执行了几次添加,将结果存储在堆栈的局部变量数据中,然后将一个常量添加回堆栈指针,有效地释放所有这些局部变量(它确实在eax中留下一笔钱)。同时看一下这个例子,"最低"使用的地址是rbp-16的4字节数据,因此从esp中减去20就足够了(在这种情况下)。

如果使用_alloca()之类的东西会变得更复杂,因为它会从堆栈中分配可变数量的内存。

此外,rbp的使用是可选的。它被用作"帧指针"在示例代码中,但是一些编译器有一个禁用帧指针的选项,在这种情况下,编译器只使用rsp(或esp,如果在32位模式下)来跟踪局部变量,释放rbp(或ebp)来使用作为通用寄存器。

答案 3 :(得分:-2)

它用于记忆函数,它在递归函数调用中非常有用。