简体   繁体   English

带有 LINK.EXE 和 WinAPI 的 NASM 中的 Hello world

[英]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到目前为止我尝试过的一些事情是

  • Using the decorated names (like _GetStdHandle@4), which resulted in the linker complaining about unresolved references使用修饰名称(如 _GetStdHandle@4),导致链接器抱怨未解析的引用
  • Not trying to print anything and calling Sleep, which resulted in the process sleeping indefinitely不尝试打印任何内容并调用 Sleep,导致进程无限期休眠
  • Exiting with a different return code, which once again did nothing以不同的返回码退出,这再次什么也没做

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM