[英]Correctly Setting Up Stack Pointer in x64 Assembly on Windows with Masm + LOCALS
我有以下簡單程序,我正在嘗試在使用 MASM 構建的 Windows 10 上構建目標 x64:
WSTR MACRO lbl:req,qstr:VARARG
LOCAL arg,unq,qot,q
lbl LABEL WORD
FOR arg,<qstr>
qot SubStr <arg>,1,1
q = 0
IFIDNI qot,<!'>;'
q = 1
ELSEIFIDNI qot,<!">;"
q = 1
ELSE
DW arg
ENDIF
IF q EQ 1
unq SubStr <arg>,2,@SizeStr(<arg>)-2
% FORC c,<unq>
DW "&c"
ENDM
ENDIF
ENDM
DW 0
ENDM
L MACRO qstr:VARARG
LOCAL sym,seg
seg EQU <.code>
%IFIDNI <@CurSeg>,<_DATA>
seg EQU <.data>
ENDIF
.CONST
ALIGN 4
WSTR sym,qstr
seg
EXITM <OFFSET sym>
ENDM
extrn LoadCursorW: PROC
extrn MessageBoxW: PROC
extrn ExitProcess: PROC
extrn GetModuleHandleW: PROC
extrn RegisterClassExW: PROC
extrn CreateWindowExW: PROC
extrn GetLastError: PROC
extrn DefWindowProcW: PROC
.data
wstr windowClassName,"AsmTestClass",0,0
wstr windowTitle,"AsmTest",0,0
HWND_DESKTOP textequ <0h>
MB_OK textequ <0h>
WM_CREATE textequ <0001h>
WM_DESTROY textequ <0002h>
WM_SIZE textequ <0005h>
WM_PAINT textequ <000fh>
CS_VREDRAW textequ <0001h>
CS_HREDRAW textequ <0002h>
WS_OVERLAPPED textequ <00000000h>
WS_CAPTION textequ <00c00000h>
WS_SYSMENU textequ <00080000h>
WS_MINIMIZEBOX textequ <00020000h>
CW_USEDEFAULT textequ <80000000h>
IDI_APPLICATION textequ <00007f00h>
WINDOW_WIDTH DWORD 800
WINDOW_HEIGHT DWORD 600
WNDCLASSEX STRUCT DWORD
cbSize DWORD ?
style DWORD ?
lpfnWndProc QWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance QWORD ?
hIcon QWORD ?
hCursor QWORD ?
hbrBackground QWORD ?
lpszMenuName QWORD ?
lpszClassName QWORD ?
hIconSm QWORD ?
WNDCLASSEX ENDS
.code
main proc
LOCAL wc:WNDCLASSEX
LOCAL hWnd:QWORD
LOCAL hInstance:QWORD
LOCAL hCursor:QWORD
LOCAL ATOM:WORD
; hInstance = GetModuleHandle(NULL)
mov rcx, 0
call GetModuleHandleW
mov hInstance, rax
; hCursor = LoadCursor(NULL,IDI_APPLICATION)
mov edx, IDI_APPLICATION
xor ecx, ecx
call LoadCursorW
mov hCursor, rax
; Setup Window Class
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_VREDRAW or CS_HREDRAW
lea rax, OFFSET WndProc
mov wc.lpfnWndProc, rax
mov wc.cbClsExtra, 0
mov wc.cbWndExtra, 0
lea rax, hInstance
mov wc.hInstance, rax
mov wc.hbrBackground, 0
mov wc.lpszMenuName, 0
lea rax, hCursor
mov wc.hCursor, rax
lea rax, windowClassName
mov wc.lpszClassName, rax
mov wc.hIconSm, 0
lea rcx, wc
call RegisterClassExW
mov ATOM, ax
; CreateWindowExW
mov QWORD PTR [rsp+88], 0 ; lpParam
lea rax, hInstance
mov QWORD PTR [rsp+80], rax ; hInstance
mov QWORD PTR [rsp+72], 0 ; hMenu
mov QWORD PTR [rsp+64], 0 ; hWndParent
mov edx, WINDOW_HEIGHT
mov DWORD PTR [rsp+56], edx ; nHeight
mov edx, WINDOW_WIDTH
mov DWORD PTR [rsp+48], edx ; nWidth
mov DWORD PTR [rsp+40], CW_USEDEFAULT ; Y
mov DWORD PTR [rsp+32], CW_USEDEFAULT ; X
mov r9d, WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX ; dwStyle
lea r8, windowTitle ; lpWindowName
lea rdx, windowClassName ; lpClassName
xor ecx,ecx ; dwExStyle
call CreateWindowExW
cmp rax, 0
je WindowFailed
jmp WindowSuccess
WindowFailed:
call GetLastError
; to-do check error
WindowSuccess:
mov hWnd, rax
mov rcx, HWND_DESKTOP ; hWnd
lea rdx, L("Hello x64 World!",0,0) ; lpText
lea r8, L("Win64 Demo",0,0) ; lpCaption
mov r9d, MB_OK ; uType
call MessageBoxW
mov ecx, eax ; uExitCode
call ExitProcess
main endp
WndProc proc
LOCAL hWnd:QWORD
LOCAL uMsg:DWORD
LOCAL wParam:QWORD
LOCAL lParam:QWORD
LOCAL result:QWORD
mov lParam, r9
mov wParam, r8
mov uMsg, edx
mov hWnd, rcx
; msg handler
cmp uMsg,WM_CREATE
je create
cmp uMsg,WM_PAINT
je paint
cmp uMsg,WM_DESTROY
je destroy
cmp uMsg,WM_SIZE
je resize
; default
call DefWindowProcW
mov result,rax
jmp finish
create:
mov result, 0
jmp finish
paint:
mov result, 0
jmp finish
destroy:
mov result, 0
jmp finish
resize:
mov result, 0
jmp finish
finish:
mov rax, result
ret
WndProc endp
End
Masm 會自動添加以下代碼:
main proc
push rbp
mov rbp,rsp
add rsp,0FFFFFFFFFFFFFF90h
..
main endp
WndProc proc
push rbp
mov rbp,rsp
add rsp,0FFFFFFFFFFFFFFD8h
...
leave
ret
WndProc endp
然而,當 WndProc 是例行程序時,我們調用 DefWindowProcW 時觸發訪問沖突
我懷疑這是因為堆棧指針設置不正確,但 masm 似乎正在為此添加代碼。 如何正確執行此操作?
masm添加的代碼
add rsp,0FFFFFFFFFFFFFFD8h
它相當於sub rsp,40。 它不能被 16 整除,在我的代碼中添加 sub rsp,8 可以修復崩潰。
WndProc proc
LOCAL hWnd:QWORD
LOCAL uMsg:DWORD
LOCAL wParam:QWORD
LOCAL lParam:QWORD
LOCAL result:QWORD
sub rsp, 8
是解決此問題的最佳方法,還是 masm 有辦法自動計算正確值/或預測需要提前更正的內容?
您可以使用它來將 rsp 向下舍入到 16 字節邊界:
and rsp,0fffffff0h
我使用 VS2015 進行了測試,使用 LOCAL 會導致 MASM 自動生成該代碼。 使用 VS2015,使用 wmain 而不是 WndProc,在進入時,rsp == 8 的最后一個半字節,顯然預計進入代碼將使用 rbp 作為幀指針:
push rbp
mov rbp,rsp
將 rsp 放在 16 字節邊界上,或者在進入時,rbp 可能隨機位於 8 字節或 16 字節邊界上。 我不確定 API 規則。
如果在輸入時,rsp 的最后一個半字節始終是 8,那么您可以使用虛擬 LOCALS,以便所有 LOCALS 占用 8 + 16 字節的一些倍數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.