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

时间:2013-07-15 14:56:30

标签: assembly stack

我刚刚开始组装,我发现这些说明和类似的代码遍布:

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

为什么会这样做?我听说它是​​关于堆栈帧的初始化。你可以解释一下,或者指出我要找的关键词吗?

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

每个C(除了一些inline函数,而不仅仅是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,它处理变量计数参数 - Object... values - 通过创建新数组并仅传递对它的引用)。