我正在尝试在内核模块中实现一个打印功能,以用于学习目的。我在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)
但是我收到__builtin_va_alist未声明的错误。我是否应该尝试找到__builtin_va_alist的定义并将其放入我的包含文件中,或者我不知道这里有什么内容?另外,如果我将__builtin_va_alist更改为__builtin_va_list(注意:a不存在),那么我收到一个名为implicit declaration of __builtin_varargs_start
的错误。请帮助。
由于
奇丹巴拉姆
答案 0 :(得分:3)
varargs如何在x86-64上运行实际上相当复杂。
如果我们以此为例:
#include <stdio.h>
int main()
{
double f=0.7;
printf("%d %f %p %d %f", 17, f, "hello", 42, 0.8);
return 0;
}
它生成的代码是:
.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
中的值传递SSE寄存器(在本例中为2)。常规参数在寄存器中传递,因此%edi
,%esi
,%edx
,%ecx
包含格式字符串,第一个整数参数,"hello"
的地址和第二个整数参数。这遵循x86_64的标准参数排序。
编译器通常会生成代码,然后将所有参数寄存器推送到堆栈中,并“剔除”va*
函数中的寄存器。
因此,如果我们采用上述源代码并将printf
替换为myprintf
,如下所示:
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
的开头,它确实:
...
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:
...
然后从堆栈中捕获东西的代码非常复杂。这是浮点方面:
.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
生成此代码没有任何问题。