Win64 有呼叫协定,‘呼叫协定 ( calling convention )’。在 Win32 汇编语言中,呼叫 Win API 是以堆栈传递参数,且最右边的参数先推入堆栈,传回值存于 EAX 暂存器里,并且由被呼叫的一方 ( 即副程式或 API,像这种被呼叫的程式称为 callee ) 清除堆栈,此种呼叫协定称为‘STDCALL’。但是在 Win64 却有底下不同:
参数的传递并非完全靠堆栈,而是靠暂存器以及堆栈;精确的说,前四个参数依序放在 ECX、EDX、R8、R9 暂存器里面,第五个以后的参数才放在堆栈里。
主程式 ( 或称为呼叫者,caller ) 必须为副程式或 API ( 或称为被呼叫者,callee )准备好足够的堆栈空间,以容纳这些参数。每个参数占用四字组 ( QWORD ),亦即 8 个字节 ( BYTE )。
即使前四个参数靠暂存器传递给副程式或 API,但是主程式仍然得准备 32 个字节的堆栈空间,以保留给前四个参数存入;甚至连不到四个参数的 API 也得在堆栈准备 32 个字节的空间!假如您没有准备好足够的堆栈空间,程式必定崩溃。
当程式由副程式返回时,由主程式 ( caller,呼叫者 ) 负责清除堆栈,而不是副程式或 API 清除堆栈。
堆栈框必须对齐一个节 ( paragraph ),每个节的大小是 16 个字节,即十六进位的 10H。换句话说,堆栈框必须要在堆栈位址的 WWWWXXXXYYYYZZZ0H 处。或者说,堆栈框所在的堆栈位址必须要能被 10H 整除。
这种呼叫协定称为‘FASTCALL’( 有些文献是说类似 FASTCALL )。举个例子来说明,可能会清楚一点。例如要呼叫 MessageBox API 时,在 Win32 底下为:
INVOKE MessageBox,hWnd,OFFSET lpText,OFFSET lpCaption,MB_OKCANCEL
一行就解决了,但是在 Win64 里,却变成
.CODE
;-------------------------------------------------------------------------------
Main PROC
sub rsp,28h ;保留4个8字节的参数+1个8字节返回值
mov r9,MB_OKCANCEL
mov r8,OFFSET szTitle
mov rdx,OFFSET szText
sub rcx,rcx
call MessageBoxA
add rsp,28h ;主程序负责清除堆栈
ret
Main ENDP
;*******************************************************************************
END
[此贴子已经被作者于2021-7-27 16:08编辑过]