首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >va_alist (在64位机器中使用变量列表)

va_alist (在64位机器中使用变量列表)
EN

Stack Overflow用户
提问于 2013-09-07 07:01:18
回答 1查看 483关注 0票数 1

出于学习的目的,我正在尝试在内核模块中实现一个打印函数。我正在QEMU上模拟它。

代码语言:javascript
复制
#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)

但是我得到的错误是__builtin_va_alist是未声明的。我是否也应该尝试找到__builtin_va_alist的定义并将其放入我的包含文件中,或者我在这里没有意识到什么?此外,如果我将__builtin_va_alist更改为__builtin_va_list (注意:a不在那里),那么我会得到一个名为implicit declaration of __builtin_varargs_start的错误。请帮帮忙。

谢谢

奇丹巴拉姆

EN

回答 1

Stack Overflow用户

发布于 2013-09-07 08:23:52

varargs在x86-64上的工作原理实际上相当复杂。

如果我们以此为例:

代码语言:javascript
复制
#include <stdio.h>

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

    return 0;
}

它生成的代码是:

代码语言:javascript
复制
    .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

正如您所看到的,浮点值保存在%xmm0%xmm1中,printf函数(就像其他任何varargs函数一样)通过%eax中的值(在本例中为2)“告知”SSE寄存器中传递了多少参数。常规参数在寄存器中传递,因此%edi%esi%edx%ecx包含格式字符串、第一个整数参数、"hello"的地址和第二个整数参数。这遵循x86_64的标准参数顺序。

编译器通常会生成代码,然后将堆栈上的所有参数寄存器压入堆栈,并“钓出”va*函数中的寄存器。

因此,如果我们使用上面的源代码并将printf替换为myprintf,如下所示:

代码语言:javascript
复制
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");
}

myprintf的开头,它是这样的:

代码语言:javascript
复制
    ... 
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:
    ... 

然后,从堆栈中提取内容的代码非常复杂。这是浮点端:

代码语言:javascript
复制
.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

现在,我不知道您正在使用什么编译器标志,因为我的编译器用gcc -O2 -nostdlib -fno-builtin -ffreestanding生成这段代码没有任何问题。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18667830

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档