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

41. Functions in Windows

Jo Van Hoey1 
(1)
Hamme, Belgium
 

Passing argument to functions is simple when you have four or fewer non-floating-point arguments. You use rcx, rdx, r8, and r9 and provide shadow space on the stack before calling the function. After the call, you re-adjust the stack for the shadow space, and everything is fine. If you have more than four arguments, things are more complicated.

Using More Than Four Arguments

Let’s first see why things get complicated with more than four non-floating-point arguments, as shown in Listing 41-1.
; arguments1.asm
extern printf
section .data
      first      db    "A",0
      second     db    "B",0
      third      db    "C",0
      fourth     db    "D",0
      fifth      db    "E",0
      sixth      db    "F",0
      seventh    db    "G",0
      eighth     db    "H",0
      ninth      db    "I",0
      tenth      db    "J",0
      fmt   db   "The string is: %s%s%s%s%s%s%s%s%s%s",10,0
section .bss
section .text
      global main
main:
push  rbp
mov   rbp,rsp
      sub   rsp,8
      mov   rcx, fmt
      mov   rdx, first
      mov   r8, second
      mov   r9, third
      push  tenth         ; now start pushing in
      push  ninth         ; reverse order
      push  eighth
      push  seventh
      push  sixth
      push  fifth
      push  fourth
      sub   rsp,32        ; shadow space
      call  printf
      add   rsp,32+8      ; restore stack
leave
ret
Listing 41-1

arguments1.asm

Look at the instruction sub rsp,8; it is there because when we call printf, the stack needs to be 16-byte aligned. Why not just use one instruction, such as sub rsp,40 just before the call? Well, the stack would be 16-byte aligned, but printf is likely to fail. If we decrease the stack by 40 instead of 32 just before the call, the arguments on the stack are not where printf expects them to be, just above the shadow space. So, we need to align the stack before we start pushing the arguments. Note that we need to push the arguments in reverse order. After the call, we restore the stack for the alignment and for the shadow space.

Figure 41-1 shows the output.
../images/483996_1_En_41_Chapter/483996_1_En_41_Fig1_HTML.jpg
Figure 41-1

arguments1.asm output

You can also build the stack in another way. Listing 41-2 shows how it works.
;arguments2.asm
extern printf
section .data
      first         db    "A",0
      second        db    "B",0
      third         db    "C",0
      fourth        db    "D",0
      fifth         db    "E",0
      sixth         db    "F",0
      seventh       db    "G",0
      eighth        db    "H",0
      ninth         db    "I",0
      tenth         db    "J",0
      fmt     db    "The string is: %s%s%s%s%s%s%s%s%s%s",10,0
section .bss
section .text
      global main
main:
push  rbp
mov   rbp,rsp
      sub   rsp,32+56+8    ;shadow space + 7 arguments on stack + alignment
      mov   rcx, fmt
      mov   rdx, first
      mov   r8, second
      mov   r9, third
      mov   qword[rsp+32],fourth
      mov   qword[rsp+40],fifth
      mov   qword[rsp+48],sixth
      mov   qword[rsp+56],seventh
      mov   qword[rsp+64],eighth
      mov   qword[rsp+72],ninth
      mov   qword[rsp+80],tenth
      call  printf
      add   rsp, 32+56+8         ;not needed before leave
leave
ret
Listing 41-2

arguments2.asm

First use sub rsp,32+56+8 to adjust the stack.
  • 32 bytes for shadow space

  • 7 arguments to be pushed times 8 bytes, for a total of 56 bytes

Then you start building the stack, and when you see that you have to align the stack, another 8 bytes have to be subtracted from the stack pointer.

Now at the bottom of the stack, you have 32 bytes for the shadow space, and just above that you have the fourth argument, above the fifth, and so on. The stack that you build here looks the same as the one in the previous program. It is up to you to decide what you prefer.

Figure 41-2 shows the output.
../images/483996_1_En_41_Chapter/483996_1_En_41_Fig2_HTML.jpg
Figure 41-2

arguments2.asm output

How does this work in the called function? Listing 41-3 shows some example code that uses the function lfunc to build a string buffer to be printed by printf.
; stack.asm
extern printf
section .data
      first        db    "A"
      second       db    "B"
      third        db    "C"
      fourth       db    "D"
      fifth        db    "E"
      sixth        db    "F"
      seventh      db    "G"
      eighth       db    "H"
      ninth        db    "I"
      tenth        db    "J"
       fmt         db    "The string is: %s",10,0
section .bss
      flist  resb  14          ;length of string plus end 0
section .text
      global main
main:
push rbp
mov rbp,rsp
      sub rsp, 8
      mov rcx, flist
      mov rdx, first
      mov r8, second
      mov r9, third
      push tenth            ; now start pushing in
      push ninth            ; reverse order
      push eighth
      push seventh
      push sixth
      push fifth
      push fourth
      sub rsp,32      ; shadow
      call lfunc
      add rsp,32+8
; print the result
      mov rcx, fmt
      mov rdx, flist
      sub rsp,32+8
      call printf
      add rsp,32+8
leave
ret
;-------------------------------------------------------------------------
lfunc:
push  rbp
mov   rbp,rsp
      xor rax,rax             ;clear rax (especially higher bits)
      ;arguments in registers
      mov al,byte[rdx]           ; move content argument to al
      mov [rcx], al              ; store al to memory
      mov al, byte[r8]
      mov [rcx+1], al
      mov al, byte[r9]
      mov [rcx+2], al
      ;arguments on stack
      xor rbx,rbx
      mov rax, qword [rbp+8+8+32] ; rsp + rbp + return address + shadow
      mov bl,[rax]
      mov [rcx+3], bl
      mov rax, qword [rbp+48+8]
      mov bl,[rax]
      mov [rcx+4], bl
      mov rax, qword [rbp+48+16]
      mov bl,[rax]
      mov [rcx+5], bl
      mov rax, qword [rbp+48+24]
      mov bl,[rax]
      mov [rcx+6], bl
      mov rax, qword [rbp+48+32]
      mov bl,[rax]
      mov [rcx+7], bl
      mov rax, qword [rbp+48+40]
      mov bl,[rax]
      mov [rcx+8], bl
      mov rax, qword [rbp+48+48]
      mov bl,[rax]
      mov [rcx+9], bl
      mov bl,0                ; terminating zero
      mov [rcx+10], bl
leave
ret
Listing 41-3

stack.asm

The main function is the same as in arguments1.asm; however, the function called is lfunc instead of printf, which is called later in the code.

In lfunc, look at the instruction mov rax, qword [rbp+8+8+32], which loads the fourth argument from the stack into rax. The register rbp contains a copy of the stack pointer. The first 8-byte value on the stack is the rbp we pushed in the prologue of lfunc. The 8-byte value higher up is the return address to main, which was automatically pushed on the stack when lfunc was called. Then we have shadow space with 32 bytes. Finally, we arrive at the pushed arguments. Hence, the fourth and other arguments can be found at rbp+48 and higher.

When we return to main, the stack is aligned again, and printf is called.

Figure 41-3 shows the output, which is of course the same as before.
../images/483996_1_En_41_Chapter/483996_1_En_41_Fig3_HTML.jpg
Figure 41-3

stack.asm output

Working with Floating Points

Floating points are another story. Listing 41-4 shows some example code.
; stack_float.asm
extern printf
section .data
      zero   dq    0.0        ;0x0000000000000000
      one    dq    1.0        ;0x3FF0000000000000
      two    dq    2.0        ;0x4000000000000000
      three  dq    3.0        ;0x4008000000000000
      four   dq    4.0        ;0x4010000000000000
      five   dq    5.0        ;0x4014000000000000
      six    dq    6.0        ;0x4018000000000000
      seven  dq    7.0        ;0x401C000000000000
      eight  dq    8.0        ;0x4020000000000000
      nine   dq    9.0        ;0x4022000000000000
section .bss
section .text
      global main
main:
push rbp
mov rbp,rsp
      movq  xmm0, [zero]
      movq  xmm1, [one]
      movq  xmm2, [two]
      movq  xmm3, [three]
      movq  xmm4, [nine]
      sub   rsp, 8
      movq  [rsp], xmm4
      movq  xmm4, [eight]
      sub   rsp, 8
      movq  [rsp], xmm4
      movq  xmm4, [seven]
      sub   rsp, 8
      movq  [rsp], xmm4
      movq  xmm4, [six]
      sub   rsp, 8
      movq  [rsp], xmm4
      movq  xmm4, [five]
      sub   rsp, 8
      movq  [rsp], xmm4
      movq  xmm4, [four]
      sub   rsp, 8
      movq  [rsp], xmm4
      sub   rsp,32      ; shadow
      call  lfunc
      add   rsp,32
leave
ret
;------------------------------------------------
lfunc:
push  rbp
mov   rbp,rsp
      movsd xmm4,[rbp+8+8+32]
      movsd xmm5,[rbp+8+8+32+8]
      movsd xmm6,[rbp+8+8+32+16]
      movsd xmm7,[rbp+8+8+32+24]
      movsd xmm8,[rbp+8+8+32+32]
      movsd xmm9,[rbp+8+8+32+40]
leave
ret
Listing 41-4

stack_float.asm

There is no output for this little program because there is an oddity that we will explain in the next chapter. You will have to use a debugger to look at the xmm registers. For your convenience, we have provided the floating-point values in hexadecimal in the comments. The first four values are passed to the function in the xmm0 to xmm3 registers. The remaining arguments will be stored on the stack. Remember that the xmm registers can contain one scalar double-precision value, two packed double-precision values, or four packed single-precision values. In this case, we use one scalar double-precision value, and for the sake of the demonstration we stored the values on the stack without using a push instruction. This would be the way to store packed values on the stack, adjusting rsp every time with the appropriate amount. A more efficient way would be to push the scalar value directly from memory to the stack, as shown here:
      push  qword[nine]

In the function, we have to copy the values from the stack into the xmm registers, where we can process them further.

Summary

In this chapter, you learned about the following:
  • How to pass arguments to functions in registers and the stack

  • How to use shadow space on the stack

  • How to access arguments on the stack

  • How to store floating-point values on the stack

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

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