从程序集调用Win32的Sleep函数会产生访问冲突错误

时间:2013-02-26 01:50:37

标签: c++ winapi assembly x86 calling-convention

我正在使用MASM和Visual C ++,我正在使用x64进行编译。这是我的C ++代码:

// include directive
#include "stdafx.h"
// external functions
extern "C" int Asm();
// main function
int main()
{

    // call asm
    Asm();
    // get char, return success
    _getch();
    return EXIT_SUCCESS;
}

和我的汇编代码:

extern Sleep : proc
; code segment
.code
    ; assembly procedure
    Asm proc
        ; sleep for 1 second
        mov ecx, 1000   ; ecx = sleep time
        sub rsp, 8      ; 8 bytes of shadow space
        call Sleep      ; call sleep
        add rsp, 8      ; get rid of shadow space
        ; return
        ret
    Asm endp
end

使用断点,我已经确定了发生访问冲突的代码行:紧跟在汇编代码中的ret语句之后。

额外信息:

  • 我正在使用 fastcall 约定将我的参数传递给Sleep(即使它被声明为 stdcall ),因为{{ 3}},x64将始终使用 fastcall 约定。

  • 当我删除Asm相关代码时,我的Sleep程序编译并执行,没有任何错误。

  • 即使我尝试使用 stdcall 约定来调用Sleep,我仍然会收到访问冲突错误。

很明显,我的问题是,如何摆脱访问冲突错误,我做错了什么?

修改:

这是在C ++中为Sleep(500);生成的程序集:

mov         ecx,1F4h  
call        qword ptr [__imp_Sleep (13F54B308h)]

这个生成的程序集让我感到困惑......它看起来像fastcall,因为它将参数移动到ecx中,但同时它不会创建任何阴影空间。我不知道这意味着什么:
qword ptr [__imp_Sleep (13F54B308h)]

再次编辑main的完整反汇编。

int main()
{
000000013F991020  push        rdi  
000000013F991022  sub         rsp,20h  
000000013F991026  mov         rdi,rsp  
000000013F991029  mov         ecx,8  
000000013F99102E  mov         eax,0CCCCCCCCh  
000000013F991033  rep stos    dword ptr [rdi]  
Sleep(500); // this here is the asm generated by the compiler!
000000013F991035  mov         ecx,1F4h  
000000013F99103A  call        qword ptr [__imp_Sleep (13F99B308h)]  
// call asm
Asm();
000000013F991040  call        @ILT+5(Asm) (13F99100Ah)  
// get char, return success
_getch();
000000013F991045  call        qword ptr [__imp__getch (13F99B540h)]  
return EXIT_SUCCESS;
000000013F99104B  xor         eax,eax  
}

1 个答案:

答案 0 :(得分:5)

如果Asm()是正常的C / C ++函数,例如:

void Asm()
{
    Sleep(1000);
}

以下是我的x64编译器为它生成的内容:

Asm proc
    push rbp          ; re-aligns the stack to a 16-byte boundary (CALL pushed 8 bytes for the caller's return address) as well as prepares for setting up a stack frame
    sub rsp, 32       ; 32 bytes of shadow space
    mov rbp, rsp      ; finalizes the stack frame using the current stack pointer
    ; sleep for 1 second
    mov ecx, 1000     ; ecx = sleep time
    call Sleep        ; call sleep
    lea rsp, [rbp+32] ; get rid of shadow space
    pop rbp           ; clears the stack frame and sets the stack pointer back to the location of the caller's return address
    ret               ; return to caller
Asm endp

MSDN says

  

调用者负责为被调用者分配参数空间,并且必须始终为4个寄存器参数分配足够的空间,即使被调用者没有那么多参数。

有关x64如何使用堆栈的更多信息,请查看以下页面:

Stack Allocation