CHID
CHID

Reputation: 1643

va_alist ( using variable list in 64 bit machine )

I am trying to implement a print function in the kernel module for my learning purposes. I am emulating it on QEMU.

#define                va_alist                __builtin_va_alist
#define                va_dcl                  __builtin_va_list_t __builtin_va_list; ...
#define                va_start(ap)         __builtin_varargs_start(ap)
#define                va_arg(ap, type)        __builtin_va_arg((ap), type)
#define                va_end(ap)              __builtin_va_end(ap)

But I am getting the error that __builtin_va_alist is undeclared. Should I try to find the definition of __builtin_va_alist also and put it in my include file or am I not aware of something here? Also, If i change __builtin_va_alist to __builtin_va_list ( note: a is not there ), then I am getting an error called implicit declaration of __builtin_varargs_start . Kindly help.

Thanks

Chidambaram

Upvotes: 1

Views: 687

Answers (1)

Mats Petersson
Mats Petersson

Reputation: 129484

How varargs works on x86-64 is actually fairly complicated.

If we take this as an example:

#include <stdio.h>

int main()
{
    double f=0.7;
    printf("%d %f %p %d %f", 17, f, "hello", 42, 0.8);

    return 0;
}

The code it generates is:

    .file   "printf.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC1:
    .string "hello"
.LC3:
    .string "%d %f %p %d %f"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB11:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $42, %ecx
    movl    $.LC1, %edx
    movsd   .LC0(%rip), %xmm1
    movl    $17, %esi
    movsd   .LC2(%rip), %xmm0
    movl    $.LC3, %edi
    movl    $2, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE11:
    .size   main, .-main
    .section    .rodata.cst8,"aM",@progbits,8
    .align 8
.LC0:
    .long   2576980378
    .long   1072273817
    .align 8
.LC2:
    .long   1717986918
    .long   1072064102
    .ident  "GCC: (GNU) 4.6.3 20120306 (Red Hat 4.6.3-2)"
    .section    .note.GNU-stack,"",@progbits

As you can see, the floating point values are held in %xmm0 and %xmm1, and the printf function (like any other varargs function) is "told" how many arguments are passed in SSE registers by the value in %eax (2 in this case). Regular arguments are passed in registers, so %edi, %esi, %edx, %ecx contain the format string, the first integer argument, the address of "hello" and the second integer argument. This follows the standard argument ordering of x86_64.

The compiler normally generates code to then push all the argument registers on the stack, and "fish out" the registers in the va* functions.

So if we take the above source code and replace the printf with a myprintf, which looks like this:

void myprintf(const char *fmt, ...)
{
    va_list va;
    int i;

    va_start(va, fmt);
    for(i = 0; i < 5; i++)
    {
    switch(i)
    {
    case 1:
    case 4:
    {
        double d = va_arg(va, double);
        printf("double %f:", d);
    }
    break;
    default:
    {
        long l = va_arg(va, long);
        printf("long %ld:", l);
    }
    }
    }
    printf("\n");
}

at the beginning of myprintf it does:

    ... 
movq    %rsi, 40(%rsp)
movq    %rdx, 48(%rsp)
movq    %rcx, 56(%rsp)
movq    %r8, 64(%rsp)
movq    %r9, 72(%rsp)
je  .L2
movaps  %xmm0, 80(%rsp)
movaps  %xmm1, 96(%rsp)
movaps  %xmm2, 112(%rsp)
movaps  %xmm3, 128(%rsp)
movaps  %xmm4, 144(%rsp)
movaps  %xmm5, 160(%rsp)
movaps  %xmm6, 176(%rsp)
movaps  %xmm7, 192(%rsp)
.L2:
    ... 

The code to then fish things out of the stack is quite complicated. This is the floating point side:

.L4:
    .cfi_restore_state
    movl    12(%rsp), %edx
    cmpl    $176, %edx
    jae .L5
    movl    %edx, %eax
    addq    24(%rsp), %rax
    addl    $16, %edx
    movl    %edx, 12(%rsp)
.L6:
    movsd   (%rax), %xmm0
    movl    $.LC0, %edi
    movl    $1, %eax
    call    printf
    jmp .L7
    .p2align 4,,10
    .p2align 3
.L8:
    movq    16(%rsp), %rax
    leaq    8(%rax), %rdx
    movq    %rdx, 16(%rsp)
    jmp .L9
    .p2align 4,,10
    .p2align 3
.L5:
    movq    16(%rsp), %rax
    leaq    8(%rax), %rdx
    movq    %rdx, 16(%rsp)
    jmp .L6

Now, I don't know what compiler flags you are using, because my compiler generates this code with gcc -O2 -nostdlib -fno-builtin -ffreestanding without any problem.

Upvotes: 4

Related Questions