带有 LINK.EXE 和 WinAPI 的 NASM 中的 Hello world [英] Hello world in NASM with LINK.EXE and WinAPI
问题描述
我正在尝试在 NASM 中运行一个简单的 Hello world 程序.我想在不使用 C-Libraries 的情况下打印到控制台,直接与 WinAPI 交互.
I'm trying to get a simple Hello world program in NASM to run. I want to print to the console without using C-Libraries, interfacing directly with WinAPI.
我使用 Visual Studio 提供的 LINK.EXE 进行链接.
I am using the Visual Studio provided LINK.EXE for linking.
这是我目前的代码:
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
我像这样组装和链接:
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"
但是,当我运行生成的 .exe 文件时,我什么也没得到.
However when I run the resulting .exe file, I get nothing.
到目前为止我尝试过的一些事情是
Some things I tried so far are
- 使用修饰名称(如 _GetStdHandle@4),导致链接器抱怨未解析的引用
- 未尝试打印任何内容并调用 Sleep,导致进程无限期休眠
- 以不同的返回码退出,这又一次什么都没做
我做错了什么?
固定调用约定
推荐答案
您修改后的代码存在三个问题.第一个是:
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
您将 messageLen
定义为一个字节,其中包含消息的长度并将该值存储在 messageLen
的地址中.然后你这样做:
You defined messageLen
to be a byte containing the length of the message and storing that value at the address of messageLen
. You then do this:
mov r8, messageLen
这会将标签 messageLen
的地址移动到 r8.您真正应该做的是将 messageLen
定义为汇编时间常数,如下所示:
That would move the address of label messageLen
to r8. What you really should have done is define messageLen
as an assembly time constant like this:
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
这没有任何问题,但要打印出来,您需要使用函数 WriteConsole
的 Ansi 版本,即 WriteConsoleA
.使用 WriteConsoleW
将字符串打印为 Unicode(Windows 2000 和更高版本上的 UTF-16,NT4 和更早版本的 Windows 上的 UTS-2).
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
. Using WriteConsoleW
printed the string as Unicode (UTF-16 on Windows 2000 and later, UTS-2 on NT4 and earlier versions of Windows).
第三个问题是关于在基于堆栈的参数在进行函数调用之前放置在堆栈上之前的强制性 32 字节阴影空间.您还需要确保堆栈 (RSP) 在进行函数调用时是 16 字节对齐的值.这些要求可以在 Microsoft64 位调用约定.
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. You also need to make sure the stack (RSP) is a 16-byte aligned value at the point of making a function call. These requirement can be found in the Microsoft 64-bit calling convention.
考虑到这一点的代码如下所示:
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
这篇关于带有 LINK.EXE 和 WinAPI 的 NASM 中的 Hello world的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!