GNU assembler on Windows: WriteFile returns ERROR_INVALID_HANDLE
The main issue with my code was that I violated Windows' ABI, which included
- not aligning the stack properly before a function call
- not adding a 32B padding (shadow space) onto the stack, meaning that the 5th parameter was incorrectly provided
Here's a working version of this code:
.intel_syntax noprefix
.extern GetStdHandle
.extern GetLastError
.extern WriteFile
.section .rodata
.Lchar: .ascii "F"
.section .data
.Lbytes_written: .long 0 # surprisingly just needs to be a dword, not qword in win64
.section .text
.global main
main:
sub rsp, 40 # allocate shadow space + re-align the stack to RSP%16 == 0
mov rcx, -11 # Magic number for stdout
call GetStdHandle
mov rcx, rax # hFile = return value
lea rdx, [rip + .Lchar] # lpBuffer
mov r8, 1 # 1 byte to write
lea r9, [rip + .Lbytes_written] # output arg pointer
mov QWORD PTR [rsp + 32], 0 # lpOverlapped=NULL in the stack space that was padding for the first call
call WriteFile
add rsp, 40
ret
Thank you to Peter Cordes, Joshua and RbMm for helping me on this one!
main:
sub rsp, 8
mov rcx, -11
call GetStdHandle
Memory trashed at this point. The top four slots (32 bytes) in the stack need to be free. The correct line seems to be
sub rsp, 40
Moving right along:
call WriteFile
call GetLastError
Mistake here. Check rax
for error code. If no error, GetLastError
returns the previous error whatever it was.
If something seems absolutely nuts, check code further along. As far as I can determine from trying to make this stuff work, Windows disassembles your code in some places and code that isn't reached can still crash if it would misalign the stack. I had a case where the crash was due to the difference between not commenting code and commenting code that was unconditionally jumped around.