1

私は 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ブレークポイントを使用して、アクセス違反が発生するコード行を特定しました。アセンブリ コードのステートメントの直後です。

追加情報:

  • が読んだことから、 x64 は常に fastcall 規則を使用するため、fastcall規則を使用してパラメーターを渡しています( stdcallSleepとして宣言されていますが) 。

  • 関連するコードを削除Asmすると、プロシージャはエラーなしでコンパイルおよび実行されます。Sleep

  • stdcall規約で呼び出しを試みてもSleep、アクセス違反エラーが発生します。

明らかに、私の質問は、どうすればアクセス違反エラーを取り除くことができるかということです。何が間違っているのでしょうか?

編集:

Sleep(500);これは、 C++ で生成されたアセンブリです。

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

この生成されたアセンブリは私を混乱させます...パラメーターをecxに移動するため、fastcallのように見えますが、同時にシャドウスペースを作成しません。そして、これが何を意味するのかわかりません:
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  
}
4

1 に答える 1

6

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 は次のように述べています

呼び出し元は、呼び出し先にパラメーター用のスペースを割り当てる責任があり、呼び出し先にそれほど多くのパラメーターがない場合でも、常に 4 つのレジスタ パラメーターに十分なスペースを割り当てる必要があります。

x64 がスタックを使用する方法の詳細については、次のページをご覧ください。

スタック割り当て

于 2013-02-26T02:56:38.600 に答える