I am in the process of writing 64-bit assembly code for the Pentium. I am using Microsoft Visual C/C++ and MASM. In assembly code, I wrote the following code fragment to call printf
:
push rcx
push rdx
push rdi
push rsi
lea rcx,string1
call printf
pop rsi
pop rdi
pop rdx
pop rcx
where string1 is defined as: string1 db "start of func", 10, 0
.
I expected the first four instructions to save four registers on the stack. That is, the values of the saved registers would be restored by the four pop
instructions. However, the register values were not being properly restored. I found this video:
This video says that before calling printf
you need to allocate 32 bytes on the stack for these values. Hence, I wrote this code:
push rcx
push rdx
push rdi
push rsi
sub rsp,20
lea rcx,string1
call printf
add rsp,20h
pop rsi
pop rdi
pop rdx
pop rcx
This code works and makes some sense to me. When I look at the code generated by the C compiler it does not allocate any additional space on the stack and it works. Therefore, I do not understand why I need to allocate additional space on the stack. Please tell me what I am missing.
From the fact that you are passing a value in rcx
, it appears you are using the windows calling conventions, rather than the "standard" calling conventions used by everyone except microsoft.
For the Windows x64 calling convetions, when calling a varargs function (which printf
is) you must allocate 32 bytes of "shadow" space on the stack immediately above1 the return address. There doesn't need to be anything in this space, but you can't (reliably) store anything there, as the called function may clobber it -- that is in fact what is happening to your saved register values in your first fragment.
1"above" since the stack grows downward means allocated before the call