写32位汇编时,JWASM基本与MASM相同,写64位汇编时,JWASM与MASM区别较大,因JWASM支持高级语法,如.if,.while,invoke等等,且支持自动控制堆栈(和写32位汇编一样)。
我们依旧以经典的HelloWorld来做为例子
注:关于Win64调用约定详细参考Win64调用约定
简单说一下Win64的调用约定吧,即函数的前4个参数通过寄存器传递,分别为rcx, rdx, r8, r9,其余参数通过堆栈(即push)传递。但64位下堆栈由Caller(调用者)分配,所以调用函数前都要先预留堆栈空间,至少要【 8 * 参数个数】个byte,MSDN堆栈分配
Win64与Win32数据类型区别:int与long依旧为32 bits(Linux64下long类型为64 bits),Pointer为64 bits,用64 bits类型用int64或longlong,MSDN说这是为了节约内存。详细见MSDN标量类型
先看一下Jwasm_HelloWorld.asm
option win64:1
option frame:auto
option casemap:none
.nolist
.nocref
.NOLISTMACRO
include ..\macros.inc ;;此文件为MASM SDK中的macros.inc,里面的UCCSTR宏,定义Unicode字符串
;include windows.inc ;;这些inc文件全来自MASM SDK,不过里面的一些定义被我手动修改了
;;如HINSTANCE定义,改为qword了,为了代码可直接使用,
;;我将用到的定义写出来了
;include kernel32.inc
;include user32.inc
;includelib kernel32.lib
;includelib kernel32.lib
.list
.cref
MB_OK equ 0
NULL equ 0
TRUE equ 1
HINSTANCE typedef QWORD
LPSTR typedef QWORD
GetModuleHandleW proto :qword
GetCommandLineW proto
MessageBoxW proto :qword, :qword, :qword, :qword
ExitProcess proto :qword
GetModuleHandle equ <GetModuleHandleW>
GetCommandLine equ <GetCommandLineW>
MessageBox equ <MessageBoxW>
.data
UCCSTR szMsgTitle,"x64",0
UCCSTR szMsgContent, "Hello World",0
.code
_start proc frame
local hInstance:HINSTANCE, lpCommandLine:LPSTR ;;为了测试语法,所以将这2个变量保存在堆栈上了
invoke GetModuleHandle, NULL
mov hInstance, rax
invoke GetCommandLine
mov lpCommandLine, rax
invoke MessageBox, NULL, addr szMsgContent, addr szMsgTitle, MB_OK
invoke ExitProcess, NULL
ret
_start endp
@Test proc frame numb1:qword, numb2:qword, numb3:qword ;;此函数为JWASM语法示范
mov rax, TRUE
ret
@Test endp
end _start
下面是用MASM x64(即ML64)写Masm_HelloWorld.asm
option casemap:none
.nolist
.nocref
.NOLISTMACRO
include macros.inc
.list
.cref
MB_OK equ 0
NULL equ 0
HINSTANCE typedef QWORD
LPSTR typedef QWORD
GetModuleHandleW proto :qword
GetCommandLineW proto
MessageBoxW proto :qword, :qword, :qword, :qword
ExitProcess proto :qword
GetModuleHandle equ <GetModuleHandleW>
GetCommandLine equ <GetCommandLineW>
MessageBox equ <MessageBoxW>
.data
UCCSTR szMsgTitle,"x64",0
UCCSTR szMsgContent, "Hello World",0
.code
_start proc FRAME
local hInstance:HINSTANCE, lpCommandLine:LPSTR
push rbp
.pushreg rbp
mov rbp, rsp
.setframe rbp, 0
sub rsp, 16 ;分配堆栈,2个64 bits变量,即hInstance, lpCommandLine
.allocstack 16
.endprolog
sub rsp, 1 * 8 ;分配堆栈,1个参数
mov rcx, NULL
call GetModuleHandle ;invoke GetModuleHandle, NULL
add rsp, 1 * 8 ;收回堆栈空间
mov hInstance, rax
sub rsp, 0 ;无参数,这个指令只是为说明堆栈分配,实际中可以省略
call GetCommandLine ;invoke GetCommandLine
add rsp, 0 ;收回堆栈空间
mov lpCommandLine, rax
sub rsp, 4 * 8 ;分配堆栈,MessageBox函数有4个参数
mov rcx, NULL
lea rdx, szMsgContent
lea r8, szMsgTitle
mov r9, MB_OK
call MessageBox ;invoke MessageBox, NULL, addr lpMsgContent, addr lpMsgTitle, MB_OK
add rsp, 5 * 8 ;收回堆栈
sub rsp, 1 * 8 ;分配堆栈
mov rcx, NULL
call ExitProcess ;invoke ExitProcess, NULL
add rsp, 1 * 8 ;收回堆栈
add rsp, 16
pop rbp
ret
_start endp
_start_Op proc FRAME ;;此函数为_start函数优化后的样子,
local hInstance:HINSTANCE, lpCommandLine:LPSTR
push rbp
.pushreg rbp
mov rbp, rsp
.setframe rbp, 0
sub rsp, 16
.allocstack 16
.endprolog
sub rsp, 4 * 8 ;;只在开始处为【call函数】时分配一次堆栈空间
mov rcx, NULL
call GetModuleHandle ;invoke GetModuleHandle, NULL
mov hInstance, rax
call GetCommandLine ;invoke GetCommandLine
mov lpCommandLine, rax
mov rcx, NULL
lea rdx, szMsgContent
lea r8, szMsgTitle
mov r9, MB_OK
call MessageBox ;invoke MessageBoxW, NULL, addr lpMsgContent, addr lpMsgTitle, MB_OK
mov rcx, NULL
call ExitProcess ;invoke ExitProcess, NULL
add rsp, 4 * 8 ;;收回为【call函数】时分配的空间
add rsp, 16
pop rbp
ret
_start_Op endp
end
Masm_HelloWorld.asm可用ML64或JWASM编译,而Jwasm_HelloWorld.asm只能用JWASM编译,因里面用到高级语法与JWASM特有的指令(option win64, option frame)
Ml64的编译命令:
ml64.exe /c /WX /Zi /Fl"Masm_HelloWorld.lst"/Zd "Masm_HelloWorld.asm"
JWASM编译命令:
jwasm.exe -Fl"Masm_HelloWorld.lst" -c -win64 -Zi -Zd "Masm_HelloWorld.asm"
jwasm.exe -Fl"Jwasm_HelloWorld.lst" -c -win64 -Zi -Zd "Jwasm_HelloWorld.asm"
JWASM的-c必须存在,因JWASM不支持调用Link程序,-c只是为兼容MASM-Fl同MASM,可以看出,编译命令几乎都没变,只是将“/”转为“-”
以下是链接命令,一种是用GoLink链接,一种是微软的Link链接
GoLink.exe /entry _Start /debug dbg /mix /fo HelloWorld.exe HelloWorld.obj kernel32.dll user32.dll
link.exe HelloWorld.obj /entry:_Start /debug /machine:x64 /out:HelloWorld.exe /subsystem:windows /defaultlib:kernel32.lib user32.lib
编译完成后,查看List清单文件,Jwasm_HelloWorld.list中的_start函数几乎与Masm_HelloWorld.list的相同,唯一不同在于sub rsp,Number中的Number,因Masm_HelloWorld.asm中的堆栈分配为人工,为了示范Win64堆栈分配我只是分配了刚好的空间,没有富余。而Jwasm_HelloWorld.asm中,因使用了option win64 指令,所以Jwasm自动帮我们分配了4*8 byte堆栈空间。
下图为Jwasm_HelloWorld.list中_start函数内容
下图为Jwasm_HelloWorld.list中@Test函数内容
Masm_HelloWorld.list就不再贴图了,其指令与Masm_HelloWorld.asm相同
option win64:number 用为允许Jwasm自动分配堆栈的方法和是否自动保存寄存器数据(即函数的前4个参数)至堆栈上。
当number的第1 bit为0时(option win64:2),在函数中,Jwasm不会自动保存寄存器中的参数至堆栈上
当number的第1 bit为1时(option win64:1),JWASM会自动将寄存器中的参数保存至堆栈上,如Jwasm_HelloWorld.list中@Test函数
当number的第2 bit为0时(option win64:1),遇到invoke时,每次都会自动分配堆栈,且最少分配4 * 8 byte(即rcx, rdx, r8, r9),如Jwasm_HelloWorld.list中_start函数中每次遇到invoke指令都会分配4 * 8byte的空间,即sub rsp, 32
当number的第2 bit为1时(option win64:3),只在函数入口处分配一次堆栈(8 * 此函数内invoke参数的最大个数),如MASM_HellowWorld.asm中_start_Op函数一样
OPTION FRAME:<AUTO | NOAUTO> 默认为noauto。此指令影响64位汇编,
当此指令设置为auto且函数声明中带有frame指令时(_start proc frame),JWASM自动生成ML64的Prolog指令(.pushreg,.setframe,.endprolog等等),
JWASM中新增的指令还有很多,如option FieldAlign,option elg,option mz,option DLLImport,option codeview等等,但在HelloWold中没有用到,所以有时间了再翻译下JWASM的帮助文件吧