A variadicfunction is a function that takes a variable number of arguments. A good example is printf. Remember, in Linux assembly, when we use printf with xmm registers, the convention is that rax contains the number of xmm registers that printf has to use. This number can also be retrieved from the printf format instruction, so often you can get away without using rax. For example, the following format indicates that we want to print four floating-point values, each with nine decimals:
fmt db "%.f",9,"%.f",9, "%.f",9,"%.f",10,0
Even if we do not comply with the convention to specify the number of floating-point values in rax, printf would print the four values anyway.
Variadic Functions in Windows
In Windows, the process is different. If you have xmm registers in the first four arguments, you have to copy them in the respective argument register. Listing 42-1 shows an example.
; variadic1.asm
extern printf
section .data
one dq 1.0
two dq 2.0
three dq 3.0
fmt dq "The values are: %.1f %.1f %.1f",10,0
section .bss
section .text
global main
main:
push rbp
mov rbp,rsp
sub rsp,32 ;shadow space
mov rcx, fmt
movq xmm0, [one]
movq rdx,xmm0
movq xmm1, [two]
movq r8,xmm1
movq xmm2, [three]
movq r9,xmm2
call printf
add rsp, 32 ;not needed before leave
leave
ret
Listing 42-1
variadic1.asm
When you create shadow space before calling a function, it is a good habit to delete the shadow space after you execute the function. In our example, add rsp,32 is not necessary because it immediately precedes the leave instruction, which will restore the stack pointer anyway. In this case, we called just one function (printf), but if you call several functions in your program, be sure to create the needed shadow space and do not forget to delete the shadows space every time you continue after a function.
Here you can see that we copy the floating-point values to xmm registers and to an argument general-purpose register. This a Windows requirement. The explanation is beyond the scope of this book, but it is a requirement when using unprototyped or variadic C functions. If you commented out the copy of the general-purpose registers, printf would not print the correct values.
As you can see, it is just a matter of respecting the order of the arguments, copying the xmm registers to general-purpose registers when needed, and pushing the remaining arguments in reverse order to the stack.