Search code examples
windowsassemblywinapix86-64nasm

Why does the Windows API WriteFile require rbx to be set to [rsp]?


I'm trying to try out writing to the console in assembly. I ended up using Windows API WriteFile function. It works well, but I have encountered a weird quirk. If I don't have the rbx register set to [rsp], the WriteFile doesn't return and gives the error code 0xC0000005, which is an access violation error. It still writes correctly to the console, which I find weird. It isn't a problem and I can include one line, no problem. But it bothers me, because I cant figure out why this behaviour exists.

If it does help, I'm using NASM and GCC to compile my program.

Here is the entire code for context:

bits 64
default rel

extern GetStdHandle
extern WriteFile
extern ExitProcess

section .data
buffer db 0

section .text

print:
    push    rbp
    mov     rbp, rsp
    add     rsp, 8

    mov     rcx, -11
    call    GetStdHandle
    mov     rcx, rax

    mov     rax, 0x61616161
    mov     [buffer], rax
    mov     rdx, buffer
    mov     r8, 4

    mov     rbx, [rsp]
    call    WriteFile

    mov     rsp, rbp
    pop     rbp

    ret


global main
main:
    call    print

    mov     rcx, 0
    call    ExitProcess

I tried moving the content of the function to replace call print and in that case it works even without mov rbx, [rsp]. I also tried setting the rbx register to other values, but [rsp] seems to be the only one that works.


Solution

  • The Windows calling convention requires the caller to provide 32 bytes of free space on the stack above the return address for the called function to use. Because this space is not provided, the WriteFile function overwrites parts of the caller's stack. In particular, it is overwriting the return address with the value in rbx. Loading rbx with the return address masks this bug.

    To fix it, change add rsp, 8 to sub rsp, 32. (And of course remove the instruction that changes rbx. The function must not change rbx without preserving its value.)