通常,您应该在将 的值移入EBP
之前将 的当前值压ESP
入堆栈EBP
。 EBP
是 32 位平台上的“被调用者保存”寄存器,这意味着如果要在函数中修改它,则必须先保存它。
如果您希望您的函数返回堆栈指向的位置(或函数调用后它将返回的位置)的值,那么最好的做法是:
void* get_stack_addr()
{
void* stack_ptr = NULL;
//on 32-bit systems
//EBP is pointing at top of stack frame
//EBP + 4 is the return instruction address
//EBP + 8 is the value that was in ESP before function call for a function with no arguments
__asm__
(
"movl %%ebp, %0\n\t"
"addl $8, %0\n\t"
: "=r" (stack_ptr)
);
return stack_ptr;
}
这种方式EAX
现在保存在调用之前堆栈指向的值的地址get_stack_addr()
。如果你只是ESP
在函数中返回值,你实际上不知道你指向哪里,因为编译器经常在 C/C++ 函数中填充堆栈以保持正确的堆栈对齐。它还经常在堆栈上为所有局部变量保留空间,这又会导致堆栈的计算失败。通过使用EBP
指向栈帧顶部的 ,可以在 32 位平台上准确计算函数调用前栈的值。最后,我们将返回值放入,EAX
因为在 C/C++ 的大多数 OS 应用程序二进制接口上,EAX
保存函数的返回值,而不是EBP
.
还有一件事......如果您想要调用实际函数的堆栈上的参数开始get_stack_addr()
,然后更改movl %%ebp, %0\n\t
为movl (%%ebp), %0)\n\t
. 这样,您现在就获得了先前的堆栈帧基指针(即,调用者的堆栈帧基指针),并且通过向该地址添加 +8 的值,您将获得存储参数的开头在返回地址上方,或者您正在查看指向当前函数调用者的堆栈帧的地址(即前一个堆栈帧)。
作为增强功能"leal 8(%%ebp), %0\n\t"
可以替换:
"movl %%ebp, %0\n\t"
"addl $8, %0\n\t"
该leal
指令将EBP的值加 8并将结果存储在输出操作数中。