2

I have compiled this function:

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);
}

into this assembly:

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 #,
    movaps  %xmm0, -128(%rbp)   #,
    ...
.L7:
# a.c:7:    va_start(args, len);
    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
    leaq    -176(%rbp), %rax    #, tmp101
    movq    %rax, -192(%rbp)    # tmp101, MEM[(struct [1] *)&args].reg_save_area
# a.c:9:    for(int i =0; i<len ; i++)
    movl    $0, -180(%rbp)  #, i
# a.c:9:    for(int i =0; i<len ; i++)
    jmp .L3 #
.L6:
# a.c:11:       int val = va_arg(args,int);
    movl    -208(%rbp), %eax    # MEM[(struct  *)&args].gp_offset, D.2882
    cmpl    $47, %eax   #, D.2882
    ja  .L4 #,
# a.c:11:       int val = va_arg(args,int);
    ...

I know it looks like mess, but there are some part of the output I am interested in. Specially lines like this:

movl    $8, -208(%rbp)  #, MEM[(struct [1] *)&args].gp_offset

I would try to analyse it:

  1. MEM[...] -> could represent a whole memory as an array (an addresses as a index)
  2. MEM[(struct[1]*)...] -> could represent a type, which this type itself is a member of some unknown struct struct[1]* gives then pointer to a type of the second member of that struct
  3. MEM[(struct[1]*)&args] -> once the data type (length) is specified (struct[1]*), it reside at address of variable args, so know we know, where to start
  4. MEM[(struct[1]*)&args].gp_offset -> So we have a type, a start address which together gives us a index to global memory table, which obtain a "some value" upon which we want to access to get gp_offset ?

Is my surmises correct? What members or functions (or what it is) are the members gained ->

gp_offset, fp_offset, overflow_arg_area, reg_save_area, ...

Can I find an implementation of them to see what they does? or are they documented under compiler internals?

milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • Note that x86-64 System V has register args (General-Purpose integer (GP) and float) and (potentially) stack args to deal with separately for variadic functions. Unlike Windows x64, where the calling convention includes "home space" that variadic functions can dump their register args into to form one array of args contiguous with the stack args. (That's why Windows x64 uses so few arg-passing regs, and makes XMM compete with integer for slots). So expect all of that complexity in your asm; x86-64 SysV makes variadic functions pay the price for an efficient calling convention. – Peter Cordes Jun 19 '20 at 21:28
  • Also, did you check how the `va_start` macro expands? It might involve some of those names. Otherwise they're invented by compiler internals for va_arg handling. – Peter Cordes Jun 19 '20 at 22:52
  • 1
    Are you asking how to interpret the output of `-fverbose-asm` in general, or do you just want to know how `va_list`s work? If the latter, then see [What is the format of the x86_64 va_list structure?](https://stackoverflow.com/q/4958384/7509065). – Joseph Sible-Reinstate Monica Jun 20 '20 at 01:09

0 Answers0