带有 LINK.EXE 和 WinAPI 的 NASM 中的 Hello world [英] Hello world in NASM with LINK.EXE and WinAPI

查看:86
本文介绍了带有 LINK.EXE 和 WinAPI 的 NASM 中的 Hello world的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆