Complete analysis of x64 variable parameter principle

Problem attractor

stackoverflow There is this problem. The standard says that the variable parameters of makecontext must be of type int, and then find the variable parameters of makecontext Source code There is a note in the following paragraph.

 /* Handle arguments.
  
       The standard says the parameters must all be int values.  This is
       an historic accident and would be done differently today.  For
       x86-64 all integer values are passed as 64-bit values and
       therefore extending the API to copy 64-bit values instead of
       32-bit ints makes sense.  It does not break existing
       functionality and it does not violate the standard which says
       that passing non-int values means undefined behavior.  */

In the notes, it is mentioned that the shaping of variable parameters is treated as 64bit. Let's explore the implementation principle of x64 variable parameters, including the situation that floating-point numbers are contained in variable parameters.

Experimental code

The experimental code is as follows

#include <stdio.h>
#include <stdarg.h>
int f(int x, float y, short a, double b, ...)
{
    va_list ap;
    va_start(ap, b);

    char cc = va_arg(ap, int);
    float dd = va_arg(ap, double);
    int ee = va_arg(ap, int);
    double ff = va_arg(ap, double);
    int last = va_arg(ap, int);

    printf("%d  %f  %d  %lf  %d\n", (int)cc, dd, ee, ff, last);

    for(int i = 0; i < last; ++i)
    {
        int tmp = va_arg(ap, int);
        printf("%d\n", tmp);
    }

    va_end(ap);
}

int main()
{
    f(1, 2.2f, 3, 4.4,             5, 6.6f, 7, 8.8, 3, 10, 11, 12, 'x', 'y');
    return 0;
}

va_list structure

typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
} va_list[1];

g represents a general-purpose register and f represents a floating-point register.

reg_save_area points to a special location on the stack, which is in the middle of the stack. 6 + 8 positions are provided here (the first 6 positions are used to store the parameters passed by the general register, 8 bytes each; the last 8 positions are used to store the floating point parameters passed by the xmm register, 16 bytes each). All variable parameters passed through registers are eventually copied here. For example, the 5th and 6th parameters (variable parameters) are passed through the general register. Then the 5th and 6th positions will be occupied, and the first 4 positions will not be used. If no variable parameter is passed through the register (i.e. there are no less than 6 determined parameters before the variable parameter), these 6 positions still exist (occupy space) but are not used.

gp_offset indicates that the variable parameters stored in the six positions mentioned above are relative to overflow_ arg_ Offset of area. If it is a variable parameter from the third number, the offset is 2 * 8 = = 16, that is, 0x10 in the following figure

overflow_arg_area represents the starting address of the parameter passed through the stack. The first 6 parameters are passed through registers together with the determined parameters and variable parameters. The following are passed through the stack. The parameters passed in the last stack are put on the stack first, and the seventh parameter is put on the stack last, with the lowest address. So overflow_arg_area points to the seventh parameter.

Under x64, floating point numbers are passed through the xmm register. The variable parameters passed by the xmm floating-point register will eventually be copied to the stack. fp_offset indicates that the starting address of the variable floating-point number parameter copied to the stack deviates from reg_ save_ The size of area, because the parameters passed by 6 8-byte general-purpose registers are just separated in the middle. Therefore, if you start from the third floating point number (excluding non floating point numbers), it is 8 * 6 + 2 * 16 = 80, that is, 0x50 in the following figure

So the principle is through reg_save_area + gp_offset accesses the variable parameters passed through the general register. These variable parameters are copied to the middle of the stack through reg_save_area + fp_offset accesses the variable parameters passed by the floating-point register, and these variable parameters are also copied to the middle of the stack. The remaining parameters are passed to the caller's stack by the superior caller of this function through the stack and through overflow_arg_area to access.

On stack memory layout

 

Assembly code

The corresponding assembly code and comments are as follows

gef➤  disassemble /m main
Dump of assembler code for function main:
26      {
=> 0x0000000000400854 <+0>:     push   %rbp
   0x0000000000400855 <+1>:     mov    %rsp,%rbp
​
27          f(1, 2.2f, 3, 4.4,             5, 6.6f, 7, 8.8, 3, 10, 11, 12);
   0x0000000000400858 <+4>:     movsd  0x118(%rip),%xmm2        # 0x400978
   0x0000000000400860 <+12>:    movsd  0x118(%rip),%xmm1        # 0x400980
   0x0000000000400868 <+20>:    movsd  0x118(%rip),%xmm0        # 0x400988
   0x0000000000400870 <+28>:    pushq  $0xc
   0x0000000000400872 <+30>:    pushq  $0xb
   0x0000000000400874 <+32>:    mov    $0xa,%r9d  #Pass the 6th non floating point parameter to the general register
   0x000000000040087a <+38>:    mov    $0x3,%r8d  #Pass the 5th non floating point parameter to the general register
   0x0000000000400880 <+44>:    movapd %xmm2,%xmm3
   0x0000000000400884 <+48>:    mov    $0x7,%ecx   #Pass the 4th non floating point parameter to the general register
   0x0000000000400889 <+53>:    movapd %xmm1,%xmm2
   0x000000000040088d <+57>:    mov    $0x5,%edx   #Pass the third non floating point parameter to the general register
   0x0000000000400892 <+62>:    movapd %xmm0,%xmm1    #The XMM register is used to pass floating-point parameters. Before the call instruction, pass the floating-point number parameters from left to right starting from xmm0 in order
   0x0000000000400896 <+66>:    mov    $0x3,%esi  #Pass the second non floating point parameter to the general register
   0x000000000040089b <+71>:    movss  0xed(%rip),%xmm0        # 0x400990
   0x00000000004008a3 <+79>:    mov    $0x1,%edi  #Pass the first non floating point parameter to the general register
   0x00000000004008a8 <+84>:    mov    $0x4,%eax   #Eax passes the number of floating point number parameters. Among callers, the value in eax is used to determine whether the floating-point register needs to be copied to the stack.
   0x00000000004008ad <+89>:    callq  0x400596 <f>  #Of course, if there is no floating-point number parameter, MOV $0 is directly used,% eax so there is no need to copy
   0x00000000004008b2 <+94>:    add    $0x10,%rsp
​
28          return 0;
   0x00000000004008b6 <+98>:    mov    $0x0,%eax
​
29      }
   0x00000000004008bb <+103>:   leaveq
   0x00000000004008bc <+104>:   retq
​
​
​
​
gef➤  disassemble /m f
Dump of assembler code for function f:
4       {
   0x0000000000400596 <+0>:     push   %rbp
   0x0000000000400597 <+1>:     mov    %rsp,%rbp
   0x000000000040059a <+4>:     sub    $0x110,%rsp
   0x00000000004005a1 <+11>:    mov    %edi,-0xf4(%rbp)  #The first two parameters passed by the general register are named parameters, which are copied directly to the top of the stack
   0x00000000004005a7 <+17>:    movss  %xmm0,-0xf8(%rbp)
   0x00000000004005af <+25>:    movsd  %xmm1,-0x108(%rbp) #The first two floating-point parameters passed by xmm are also named parameters and are copied directly to the top of the stack
   0x00000000004005b7 <+33>:    mov    %rdx,-0xa0(%rbp)
   0x00000000004005be <+40>:    mov    %rcx,-0x98(%rbp)
   0x00000000004005c5 <+47>:    mov    %r8,-0x90(%rbp)
   0x00000000004005cc <+54>:    mov    %r9,-0x88(%rbp)   #The last four parameters transferred by the general register are variable parameters. The variable parameters are copied to a separate specific area in its own stack to store the parameters transferred by the general register
   0x00000000004005d3 <+61>:    test   %al,%al #The caller is responsible for setting the value of eax, indicating the number of all floating-point number parameters passed
   0x00000000004005d5 <+63>:    je     0x4005ef <f+89>
   0x00000000004005d7 <+65>:    movaps %xmm2,-0x60(%rbp)
   0x00000000004005db <+69>:    movaps %xmm3,-0x50(%rbp)  #The remaining floating-point parameters passed by xmm are variable parameters, and the variable parameters are also copied to a specific area on its own stack that holds a separate floating-point register
   0x00000000004005df <+73>:    movaps %xmm4,-0x40(%rbp)  
   0x00000000004005e3 <+77>:    movaps %xmm5,-0x30(%rbp)
   0x00000000004005e7 <+81>:    movaps %xmm6,-0x20(%rbp)  #
   0x00000000004005eb <+85>:    movaps %xmm7,-0x10(%rbp)
   0x00000000004005ef <+89>:    mov    %esi,%eax           #Both edi and esi pass named parameters. So copy it directly to the top of the stack.
   0x00000000004005f1 <+91>:    mov    %ax,-0xfc(%rbp)  #The parameters passed by general registers and floating-point numbers are stored adjacent to each other on the stack
​
5           va_list ap;
6           va_start(ap, b);
   0x00000000004005f8 <+98>:    movl   $0x10,-0xe8(%rbp) #va_list.gp_offset the offset of the variable parameter passed by the first general-purpose register from the saved register area
   0x0000000000400602 <+108>:   movl   $0x50,-0xe4(%rbp) #va_list.fp_offset the offset of the variable parameter passed by the first floating-point register from the saved register area
   0x000000000040060c <+118>:   lea    0x10(%rbp),%rax  #Here is rbp + indicating the caller's stack. 0x10 just skips the saved caller's rbp (the current rbp points to the old rbp) and return address
   0x0000000000400610 <+122>:   mov    %rax,-0xe0(%rbp)  #va_list.overflow_arg_area pointing
   0x0000000000400617 <+129>:   lea    -0xb0(%rbp),%rax
   0x000000000040061e <+136>:   mov    %rax,-0xd8(%rbp) #va_list.reg_save_area points to the register area saved on the previous stack frame (saving the parameters passed by the general-purpose register and floating-point register)
​
7
8           char cc = va_arg(ap, int);
   0x0000000000400625 <+143>:   mov    -0xe8(%rbp),%eax #eax = va_list.gp_offset
   0x000000000040062b <+149>:   cmp    $0x2f,%eax  #Since the general register has only 6 positions and 48 bytes, whether the offset exceeds 47 is calculated here, which indicates that the remaining variable parameters are not in this special area in this stack frame
   0x000000000040062e <+152>:   ja     0x400653 <f+189>
   0x0000000000400630 <+154>:   mov    -0xd8(%rbp),%rax #va_list.reg_save_area
   0x0000000000400637 <+161>:   mov    -0xe8(%rbp),%edx
   0x000000000040063d <+167>:   mov    %edx,%edx
   0x000000000040063f <+169>:   add    %rdx,%rax #eax = va_list.gp_offset + va_list.reg_save_area, which just points to the parameters passed by the register saved in a specific area
   0x0000000000400642 <+172>:   mov    -0xe8(%rbp),%edx
   0x0000000000400648 <+178>:   add    $0x8,%edx
   0x000000000040064b <+181>:   mov    %edx,-0xe8(%rbp) #va_list.gp_offset = va_list.gp_offset + 0x8, offset + 8 points to the next saved register parameter
   0x0000000000400651 <+187>:   jmp    0x400665 <f+207>
   0x0000000000400653 <+189>:   mov    -0xe0(%rbp),%rax
   0x000000000040065a <+196>:   lea    0x8(%rax),%rdx
   0x000000000040065e <+200>:   mov    %rdx,-0xe0(%rbp)
   0x0000000000400665 <+207>:   mov    (%rax),%eax  #eax points to the saved register parameter, and the value is
   0x0000000000400667 <+209>:   mov    %al,-0xb5(%rbp) # Copy to the local variable cc on the stack
​
9           float dd = va_arg(ap, double);
   0x000000000040066d <+215>:   mov    -0xe4(%rbp),%eax #eax = va_list.fp_offset
   0x0000000000400673 <+221>:   cmp    $0xaf,%eax #General purpose register 6 * 8 floating point number register 8 * 16 is 48 + 128 = 128 bytes in total. If it is greater than 0xaf(127), it means that the remaining floating point number parameters are on the previous stack
   0x0000000000400678 <+226>:   ja     0x40069d <f+263>
   0x000000000040067a <+228>:   mov    -0xd8(%rbp),%rax #va_list.reg_save_area
   0x0000000000400681 <+235>:   mov    -0xe4(%rbp),%edx
   0x0000000000400687 <+241>:   mov    %edx,%edx
   0x0000000000400689 <+243>:   add    %rdx,%rax #rax = va_list.reg_save_area + va_list.fp_offset just points to the stored floating-point register in a special area
   0x000000000040068c <+246>:   mov    -0xe4(%rbp),%edx
   0x0000000000400692 <+252>:   add    $0x10,%edx 
   0x0000000000400695 <+255>:   mov    %edx,-0xe4(%rbp) #Offset + 16, pointing to the next va_list.fp_offset = va_list.fp_offset + 16
   0x000000000040069b <+261>:   jmp    0x4006af <f+281>
   0x000000000040069d <+263>:   mov    -0xe0(%rbp),%rax
   0x00000000004006a4 <+270>:   lea    0x8(%rax),%rdx
   0x00000000004006a8 <+274>:   mov    %rdx,-0xe0(%rbp)
   0x00000000004006af <+281>:   movsd  (%rax),%xmm0      #These three instructions are copied to xmm0 with double precision first
   0x00000000004006b3 <+285>:   cvtsd2ss %xmm0,%xmm2    #Then convert double precision to single precision and copy it to xmm2 for transfer
   0x00000000004006b7 <+289>:   movss  %xmm2,-0xbc(%rbp)  #Finally, it is assigned to the local variable dd
​
10          int ee = va_arg(ap, int);
   0x00000000004006bf <+297>:   mov    -0xe8(%rbp),%eax
   0x00000000004006c5 <+303>:   cmp    $0x2f,%eax
   0x00000000004006c8 <+306>:   ja     0x4006ed <f+343>
   0x00000000004006ca <+308>:   mov    -0xd8(%rbp),%rax
   0x00000000004006d1 <+315>:   mov    -0xe8(%rbp),%edx
   0x00000000004006d7 <+321>:   mov    %edx,%edx
   0x00000000004006d9 <+323>:   add    %rdx,%rax
   0x00000000004006dc <+326>:   mov    -0xe8(%rbp),%edx
   0x00000000004006e2 <+332>:   add    $0x8,%edx
   0x00000000004006e5 <+335>:   mov    %edx,-0xe8(%rbp)
   0x00000000004006eb <+341>:   jmp    0x4006ff <f+361>
   0x00000000004006ed <+343>:   mov    -0xe0(%rbp),%rax
   0x00000000004006f4 <+350>:   lea    0x8(%rax),%rdx
   0x00000000004006f8 <+354>:   mov    %rdx,-0xe0(%rbp)
   0x00000000004006ff <+361>:   mov    (%rax),%eax
   0x0000000000400701 <+363>:   mov    %eax,-0xc0(%rbp)
​
11          double ff = va_arg(ap, double);
   0x0000000000400707 <+369>:   mov    -0xe4(%rbp),%eax
   0x000000000040070d <+375>:   cmp    $0xaf,%eax
   0x0000000000400712 <+380>:   ja     0x400737 <f+417>
   0x0000000000400714 <+382>:   mov    -0xd8(%rbp),%rax
   0x000000000040071b <+389>:   mov    -0xe4(%rbp),%edx
   0x0000000000400721 <+395>:   mov    %edx,%edx
   0x0000000000400723 <+397>:   add    %rdx,%rax
   0x0000000000400726 <+400>:   mov    -0xe4(%rbp),%edx
   0x000000000040072c <+406>:   add    $0x10,%edx
   0x000000000040072f <+409>:   mov    %edx,-0xe4(%rbp)
   0x0000000000400735 <+415>:   jmp    0x400749 <f+435>
   0x0000000000400737 <+417>:   mov    -0xe0(%rbp),%rax
   0x000000000040073e <+424>:   lea    0x8(%rax),%rdx
   0x0000000000400742 <+428>:   mov    %rdx,-0xe0(%rbp)
   0x0000000000400749 <+435>:   movsd  (%rax),%xmm0
   0x000000000040074d <+439>:   movsd  %xmm0,-0xc8(%rbp)
​
12          int last = va_arg(ap, int);
   0x0000000000400755 <+447>:   mov    -0xe8(%rbp),%eax
   0x000000000040075b <+453>:   cmp    $0x2f,%eax
   0x000000000040075e <+456>:   ja     0x400783 <f+493>
   0x0000000000400760 <+458>:   mov    -0xd8(%rbp),%rax
   0x0000000000400767 <+465>:   mov    -0xe8(%rbp),%edx
   0x000000000040076d <+471>:   mov    %edx,%edx
   0x000000000040076f <+473>:   add    %rdx,%rax
   0x0000000000400772 <+476>:   mov    -0xe8(%rbp),%edx
   0x0000000000400778 <+482>:   add    $0x8,%edx
   0x000000000040077b <+485>:   mov    %edx,-0xe8(%rbp)
   0x0000000000400781 <+491>:   jmp    0x400795 <f+511>
   0x0000000000400783 <+493>:   mov    -0xe0(%rbp),%rax
   0x000000000040078a <+500>:   lea    0x8(%rax),%rdx
   0x000000000040078e <+504>:   mov    %rdx,-0xe0(%rbp)
   0x0000000000400795 <+511>:   mov    (%rax),%eax
   0x0000000000400797 <+513>:   mov    %eax,-0xcc(%rbp)
​
13
14          printf("%d  %f  %d  %lf  %d\n", (int)cc, dd, ee, ff, last);
   0x000000000040079d <+519>:   cvtss2sd -0xbc(%rbp),%xmm0
   0x00000000004007a5 <+527>:   movsbl -0xb5(%rbp),%eax
   0x00000000004007ac <+534>:   mov    -0xcc(%rbp),%ecx
   0x00000000004007b2 <+540>:   movsd  -0xc8(%rbp),%xmm1
   0x00000000004007ba <+548>:   mov    -0xc0(%rbp),%edx
   0x00000000004007c0 <+554>:   mov    %eax,%esi
   0x00000000004007c2 <+556>:   mov    $0x400958,%edi
   0x00000000004007c7 <+561>:   mov    $0x2,%eax
   0x00000000004007cc <+566>:   callq  0x4004a0 <printf@plt>
​
15
16          for(int i = 0; i < last; ++i)
   0x00000000004007d1 <+571>:   movl   $0x0,-0xb4(%rbp)
   0x00000000004007db <+581>:   jmp    0x400843 <f+685>
   0x000000000040083c <+678>:   addl   $0x1,-0xb4(%rbp)
   0x0000000000400843 <+685>:   mov    -0xb4(%rbp),%eax
   0x0000000000400849 <+691>:   cmp    -0xcc(%rbp),%eax
   0x000000000040084f <+697>:   jl     0x4007dd <f+583>
​
17          {
18              int tmp = va_arg(ap, int);
   0x00000000004007dd <+583>:   mov    -0xe8(%rbp),%eax   #eax = va_list.gp_offset
   0x00000000004007e3 <+589>:   cmp    $0x2f,%eax     #Is the stored on the stack exhausted
   0x00000000004007e6 <+592>:   ja     0x40080b <f+629>
   0x00000000004007e8 <+594>:   mov    -0xd8(%rbp),%rax
   0x00000000004007ef <+601>:   mov    -0xe8(%rbp),%edx
   0x00000000004007f5 <+607>:   mov    %edx,%edx
   0x00000000004007f7 <+609>:   add    %rdx,%rax
   0x00000000004007fa <+612>:   mov    -0xe8(%rbp),%edx
   0x0000000000400800 <+618>:   add    $0x8,%edx
   0x0000000000400803 <+621>:   mov    %edx,-0xe8(%rbp)
   0x0000000000400809 <+627>:   jmp    0x40081d <f+647>
   0x000000000040080b <+629>:   mov    -0xe0(%rbp),%rax      #If you run out, jump here, eax = va_list.overflow_arg_area points to the previous stack frame
   0x0000000000400812 <+636>:   lea    0x8(%rax),%rdx        #
   0x0000000000400816 <+640>:   mov    %rdx,-0xe0(%rbp) #va_list.overflow_arg_area = va_list.overflow_arg_area + 8
   0x000000000040081d <+647>:   mov    (%rax),%eax   #Value
   0x000000000040081f <+649>:   mov    %eax,-0xd0(%rbp)
​
19              printf("%d\n", tmp);
   0x0000000000400825 <+655>:   mov    -0xd0(%rbp),%eax
   0x000000000040082b <+661>:   mov    %eax,%esi
   0x000000000040082d <+663>:   mov    $0x40096d,%edi
   0x0000000000400832 <+668>:   mov    $0x0,%eax
   0x0000000000400837 <+673>:   callq  0x4004a0 <printf@plt>
​
20          }
21
22          va_end(ap);
23      }
   0x0000000000400851 <+699>:   nop
   0x0000000000400852 <+700>:   leaveq
   0x0000000000400853 <+701>:   retq
​

x64 variable parameters and general types are saved according to 8 bytes, Therefore, there is no need to be integers.

summary

1. Via va_arg to get the parameter value, generalized shaping (char, short...) The specified type length cannot be less than int, and the floating-point type cannot be float. Otherwise, there will be a warning, the type will be promoted, and there is a problem with the generated assembly code.

warning: 'char' is promoted to 'int' when passed through '...'

warning: 'float' is promoted to 'double' when passed through '...'

2. Only the variable parameters passed through the register will be assigned to this special area on the stack. After all, the named parameters are copied to the top of the stack. Redundant parameters (except 6 shaping parameters and 8 floating-point parameters) are put on the stack before the call instruction, that is, the function, as the callee, is on the caller's stack, and these shaping passed directly through the stack occupy 8 bytes. Floating point type, whether single precision or double precision, also occupy 8 bytes.

If it is called in the following way, because there are already 8 floating-point parameters in the front, 14.14f will be passed through the stack.

f(1, 2.2f, 3, 4.4,             5, 6.6f, 7, 8.8, 3, 10, 11, 12, 'x', 'y', 9.9, 10.1, 11.11, 12.12, 13.3f, 14.4f);
 The corresponding part is disassembled as follows
 0x00000000004008e4 <+0>:     push   %rbp
   0x00000000004008e5 <+1>:     mov    %rsp,%rbp
   0x00000000004008e8 <+4>:     movsd  0x158(%rip),%xmm7        # 0x400a48
   0x00000000004008f0 <+12>:    movsd  0x158(%rip),%xmm6        # 0x400a50
   0x00000000004008f8 <+20>:    movsd  0x158(%rip),%xmm5        # 0x400a58
   0x0000000000400900 <+28>:    movsd  0x158(%rip),%xmm4        # 0x400a60
   0x0000000000400908 <+36>:    movsd  0x158(%rip),%xmm3        # 0x400a68
   0x0000000000400910 <+44>:    movsd  0x158(%rip),%xmm2        # 0x400a70
   0x0000000000400918 <+52>:    movsd  0x158(%rip),%xmm1        # 0x400a78
   0x0000000000400920 <+60>:    movsd  0x158(%rip),%xmm0        # 0x400a80
   0x0000000000400928 <+68>:    lea    -0x8(%rsp),%rsp
   0x000000000040092d <+73>:    movsd  %xmm0,(%rsp)                         #13.3f put on the stack
   0x0000000000400932 <+78>:    movsd  0x14e(%rip),%xmm0        # 0x400a88
   0x000000000040093a <+86>:    lea    -0x8(%rsp),%rsp
   0x000000000040093f <+91>:    movsd  %xmm0,(%rsp)  # 0x400a88             #14.4f put on the stack
   0x0000000000400944 <+96>:    pushq  $0x79 #Although char directly occupies 8 bytes
   0x0000000000400946 <+98>:    pushq  $0x78
   0x0000000000400948 <+100>:   pushq  $0xc
   0x000000000040094a <+102>:   pushq  $0xb #Directly occupy 8 bytes
   0x000000000040094c <+104>:   mov    $0xa,%r9d
   0x0000000000400952 <+110>:   mov    $0x3,%r8d
   0x0000000000400958 <+116>:   mov    $0x7,%ecx
   0x000000000040095d <+121>:   mov    $0x5,%edx
   0x0000000000400962 <+126>:   mov    $0x3,%esi
   0x0000000000400967 <+131>:   movss  0x121(%rip),%xmm0        # 0x400a90 #The first floating-point parameter 2.2f is passed to the register
   0x000000000040096f <+139>:   mov    $0x1,%edi
   0x0000000000400974 <+144>:   mov    $0x8,%eax
   0x0000000000400979 <+149>:   callq  0x400596 <f>
   0x000000000040097e <+154>:   add    $0x30,%rsp
   0x0000000000400982 <+158>:   mov    $0x0,%eax
   0x0000000000400987 <+163>:   leaveq
   0x0000000000400988 <+164>:   retq

Previously via VA_ When Arg gets the parameter, if it is on the caller's stack, whether it is integer or floating-point, va_list.overflow_arg_area is increased by 8 bytes, which also shows this point.

lea    0x8(%rax),%rdx
mov    %rdx,-0xe0(%rbp) #va_list.overflow_arg_area = va_list.overflow_arg_area + 8

Under x64, the integer and floating-point parameters passed through the stack occupy 8 bytes. After all, the pushq instruction is used. However, the named parameters passed through the register are copied to the stack, because the stack space is opened first, and then mov copied into the stack. They can be merged to save memory.

The variable parameters passed through the register are eventually copied to a specific area on the stack. The integer is 8 bytes, and the floating-point type is 16 bytes because it is an xmm register.

Keywords: Linux Assembly

Added by ewillms on Wed, 05 Jan 2022 04:17:55 +0200