2

I am reading this answer What is the format of the x86_64 va_list structure?, where there is mention a member of va_list -> void *reg_save_area, which should be a address of start of the register save area. But as far as I know, there is no save area defined in ABI, since all arguments passed are not pushed but rather saved on register rdi,rsi,rdx,rcx,.., which mean the address does not make sense to me, since no argument is passed to memory. I have tried to see it in assembly, So I have compiled code from my previous question What is -fverbose-asm trying to say in assembly?:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void print_ints(int len, ...){
    va_list args;
    va_start(args, len);

    for(int i =0; i<len ; i++)
    {
        int val = va_arg(args,int);
        printf("i:%i\tval:%i\n",i,val);
    }

    va_end(args);
}

int main(){
    print_ints(2,1,2);
}

and got:

print_ints:
    pushq   %rbp    #
    movq    %rsp, %rbp  #,
    subq    $224, %rsp  #,
    movl    %edi, -212(%rbp)    # len, len
    movq    %rsi, -168(%rbp)    #,
    movq    %rdx, -160(%rbp)    #,
    movq    %rcx, -152(%rbp)    #,
    movq    %r8, -144(%rbp) #,
    movq    %r9, -136(%rbp) #,
    testb   %al, %al    #
    je  .L7 #,
    ...

    movl    $8, -208(%rbp)  #, MEM[(struct [1] *)&args].gp_offset
    movl    $48, -204(%rbp) #, MEM[(struct [1] *)&args].fp_offset
    leaq    16(%rbp), %rax  #, tmp100
    movq    %rax, -200(%rbp)    # tmp100, MEM[(struct [1] *)&args].overflow_arg_area
    ...
    
main:
    pushq   %rbp    #
    movq    %rsp, %rbp  #,
# a.c:19:   print_ints(2,1,2);
    movl    $2, %edx    #,
    movl    $1, %esi    #,
    movl    $2, %edi    #,
    movl    $0, %eax    #,
    call    print_ints  # THERE IS NO ARGUMENT PASSED TO STACK, 16(%rbp) does not make sense then to be address for reg_save_area

So now, the void *overflow_arg_area points to the 16(%rbp), but there is nothing, since all argument where saved to rdi,rsi,rdx(3 arguments). Can someone explain, why does *overflow_arg_area points to random 16(%rbp)?

milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • 1
    If there were extra arguments passed on the stack, they would be at `16(%rbp)`. `print_ints` doesn't know how many arguments it's going to receive, so there could be arguments there. – fuz Jun 20 '20 at 13:11
  • @fuz why `16(%rbp)`? I know `%rbp==ret`, `8(%rbp)==first_argument`, `16(%rbp)==second_argument`, ... But that is for x32, where args are passed to stack. On x64 there are 6 registers, `di,si,dx,cx,r8,r9`, after which it goes to the stack (not sure with that, never needed more then 6 args), but why `16(%rbp)`?? – milanHrabos Jun 20 '20 at 13:14
  • 1
    Not quite. After the standard `push %rbp`; mov `%rsp, %rbp` prologue, `rbp` points to the old `rsp`, `rbp+8` points to the return address, and `rbp+16` points to the first argument on the stack. – fuz Jun 20 '20 at 13:26
  • @fuz yes, forgot about that one. But anyway, that is for x32, not x64 where args go to regs. – milanHrabos Jun 20 '20 at 13:35
  • Even on the amd64 SysV ABI, if there are more than 6 integer arguments, further arguments are passed on the stack. – fuz Jun 20 '20 at 14:05
  • Note that the compiler still needs to "pretend" the arguments are passed on the stack if you do such things as taking the address of one the parameters. `va_list` is likely to be another such thing, you need sequential access to all the arguments but the first ones are in the registers and thus cannot be indexed; so the compiler may prefer to spill them in memory with the others. I haven't read the format of `va_list` or that answer. Unless it is specifically for Linux, another thing to check would be the Windows' homing/shadow register space and see if that has something to do with this. – Margaret Bloom Jun 20 '20 at 15:27
  • "But that is for x32, where args are passed to stack" That's wrong. [x32](https://en.wikipedia.org/wiki/X32_ABI) passes arguments in registers just like x64. The only real difference between x32 and x64 is the pointer size. – Joseph Sible-Reinstate Monica Jun 20 '20 at 16:38
  • "But as far as I know, there is no save area defined in ABI" Right, usually there isn't one, but when you call `va_start`, the compiler makes one. (And the details actually are in the ABI, along with `va_list`.) – Joseph Sible-Reinstate Monica Jun 20 '20 at 16:41
  • 1
    "Can someone explain, why does *overflow_arg_area points to random `16(%rbp)`?" If instead of `print_ints(2,1,2);`, you did `print_ints(6,1,2,3,4,5,6);`, where do you think that last `6` would live? – Joseph Sible-Reinstate Monica Jun 20 '20 at 16:43

0 Answers0