嵌入式asm代码中的运行时检查失败#0

时间:2011-05-05 17:59:34

标签: c++ visual-studio-2010 assembly stack stack-pointer

我对汇编程序有点新意,但我正在尝试使用嵌入式汇编代码从esp堆栈中的C ++方法查找参数。到目前为止,我甚至无法将esp指针复制到ebp,因此我可以在堆栈上保留(如果它发生更改)。即便是这一小段代码也让我失败了:

#include <stdlib.h>

int main(int argc, char* argv[])
{
    __asm
    {
        mov ebp, esp
    }
    system("pause");
    return 0;
}

在我运行之后,我得到:

  

运行时检查失败#0 - ESP的值未在函数调用中正确保存。这通常是调用使用一个调用约定声明的函数的结果,函数指针使用不同的调用约定声明。

不知道该怎么做。请帮助我,并提前感谢。

1 个答案:

答案 0 :(得分:2)

一般情况下,在将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,并将结果存储在输出操作数中。