[英]Hello world in NASM with LINK.EXE and WinAPI
I'm trying to get a simple Hello world program in NASM to run.我正在尝试在 NASM 中运行一个简单的 Hello world 程序。 I want to print to the console without using C-Libraries, interfacing directly with WinAPI.我想在不使用 C-Libraries 的情况下打印到控制台,直接与 WinAPI 接口。
I am using the Visual Studio provided LINK.EXE for linking.我正在使用 Visual Studio 提供的 LINK.EXE 进行链接。
Here's my code so far:到目前为止,这是我的代码:
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen: db $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleW
extern ExitProcess
section .text
_start:
; DWORD bytes;
mov rbp, rsp
sub rsp, byte 8
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-4]
push 0
call WriteConsoleW
; ExitProcess(0)
mov rcx, 0
call ExitProcess
ret
Which I assemble and link like this:我像这样组装和链接:
nasm -f win64 .\ASM.ASM
link /entry:_start /nodefaultlib /subsystem:console .\ASM.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\kernel32.lib" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
However when I run the resulting .exe file, I get nothing.但是,当我运行生成的 .exe 文件时,我什么也没得到。
Some things I tried so far are到目前为止我尝试过的一些事情是
What am I doing wrong?我究竟做错了什么?
EDIT: Fixed calling convention编辑:固定调用约定
There are three problems with your revised code.您修改后的代码存在三个问题。 The first is:第一个是:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen: db $-message ; Length of the 'Hello world!' string
You defined messageLen
to be a byte containing the length of the message and storing that value at the address of messageLen
.您将messageLen
定义为一个字节,其中包含消息的长度并将该值存储在messageLen
的地址处。 You then do this:然后你这样做:
mov r8, messageLen
That would move the address of label messageLen
to r8.这会将标签messageLen
的地址移动到 r8。 What you really should have done is define messageLen
as an assembly time constant like this:您真正应该做的是将messageLen
定义为汇编时间常数,如下所示:
messageLen equ $-message ; Length of the 'Hello world!' string
The second problem is that you define the the string as a sequence of single byte characters:第二个问题是您将字符串定义为单字节字符序列:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
There is nothing wrong with this, but to print them out you need to use the Ansi version of the function WriteConsole
which is WriteConsoleA
.这没有任何问题,但是要打印它们,您需要使用函数WriteConsole
的 Ansi 版本,即WriteConsoleA
。 Using WriteConsoleW
printed the string as Unicode (UTF-16 on Windows 2000 and later, UTS-2 on NT4 and earlier versions of Windows).使用WriteConsoleW
将字符串打印为 Unicode(Windows 2000 和更高版本上的 UTF-16,NT4 和更早版本 Windows 上的 UTS-2)。
The third problem is with regards to a mandatory 32 bytes of shadow space before the stack based parameter(s) are placed on the stack before making a function call.第三个问题是关于在基于堆栈的参数在进行函数调用之前放置在堆栈上之前的强制性 32 字节阴影空间。 You also need to make sure the stack (RSP) is a 16-byte aligned value at the point of making a function call.您还需要确保堆栈 (RSP) 在进行函数调用时是 16 字节对齐的值。 These requirement can be found in the Microsoft 64-bit calling convention .这些要求可以在Microsoft 64 位调用约定中找到。
Code that would take this into account would look like this:考虑到这一点的代码如下所示:
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen equ $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
section .text
_start:
; At _start the stack is 8 bytes misaligned because there is a return
; address to the MSVCRT runtime library on the stack.
; 8 bytes of temporary storage for `bytes`.
; allocate 32 bytes of stack for shadow space.
; 8 bytes for the 5th parameter of WriteConsole.
; An additional 8 bytes for padding to make RSP 16 byte aligned.
sub rsp, 8+8+8+32
; At this point RSP is aligned on a 16 byte boundary and all necessary
; space has been allocated.
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-16] ; Address for `bytes`
; RSP-17 through RSP-48 are the 32 bytes of shadow space
mov qword [rsp-56], 0 ; First stack parameter of WriteConsoleA function
call WriteConsoleA
; ExitProcess(0)
; mov rcx, 0
; call ExitProcess
; alternatively you can exit by setting RAX to 0
; and doing a ret
add rsp, 8+8+32+8 ; Restore the stack pointer.
xor eax, eax ; RAX = return value = 0
ret
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.