© Jo Van Hoey 2019
J. Van HoeyBeginning x64 Assembly Programminghttps://doi.org/10.1007/978-1-4842-5076-1_42

42. Variadic Functions

Jo Van Hoey1 
(1)
Hamme, Belgium
 
A variadic function 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.

Figure 42-1 shows the output.
../images/483996_1_En_42_Chapter/483996_1_En_42_Fig1_HTML.jpg
Figure 42-1

variadic1.asm output

Figure 42-2 shows the output without using the general-purpose registers.
../images/483996_1_En_42_Chapter/483996_1_En_42_Fig2_HTML.jpg
Figure 42-2

variadiac1.asm erroneous output

Mixing Values

Listing 42-2 shows an example with a mix of floating-point and other values.
; variadic2.asm
extern printf
section .data
      fmt    db    "%.1f %s %.1f %s %.1f %s %.1f %s %.1f %s",10,0
      one    dq    1.0
      two    dq    2.0
      three  dq    3.0
      four   dq    4.0
      five   dq    5.0
      A      db    "A",0
      B      db    "B",0
      C      db    "C",0
      D      db    "D",0
      E      db    "E",0
section .bss
section .text
    global main
main:
push rbp
mov rbp,rsp
      sub   rsp,8               ;align the stack first
      mov   rcx,fmt             ;first argument
      movq  xmm0,[one]          ;second argument
      movq  rdx,xmm0
      mov   r8,A                ;third argument
      movq  xmm1,[two]          ;fourth argument
      movq  r9,xmm1
; now push to the stack in reverse
      push  E                   ;11th argument
      push  qword[five]         ;10th argument
      push  D                   ;9th argument
      push  qword[four]         ;8th argument
      push  C                   ;7th argument
      push  qword[three]        ;6th argument
      push  B                   ;5th argument
; print
      sub rsp,32
      call printf
      add rsp,32
leave
ret
Listing 42-2

variadic1.asm

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.

Figure 42-3 shows the output.
../images/483996_1_En_42_Chapter/483996_1_En_42_Fig3_HTML.jpg
Figure 42-3

variadiac2.asm output

Summary

In this chapter, you learned the following:
  • Floating-point values in xmm registers in the first four arguments need to be copied to the corresponding general-purpose registers.

  • If there are more than four floating-point or other arguments, they have to be stored on the stack in reverse order.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.235.176