可以使用var_list
中的stdarg.h
来完成变量数参数,但如何在64位环境中使用stdarg.h
来实现这一点。
在32位环境中,函数参数使用堆栈从右到左传递,并且是4字节对齐的。因此可以使用地址访问变量参数。
void func(int arg1, ...) {
int arg2 = *(&arg1 + 1);
int arg3 = *(&arg1 + 2);
}
但是,在64位环境(System V ABI)中,使用寄存器传递参数。如果寄存器不能保存所有寄存器,则额外的使用堆栈传递。
所以我想知道如何在64位环境下访问没有stdarg.h
的所有参数。感谢。
修改
我正在编写自定义内核,因此无法使用标准C库。
stdarg.h
将va_start
,va_end
和va_arg
定义为内置类型,它们似乎是特定于编译器的功能或扩展:
...
#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
...
答案 0 :(得分:0)
您可以使用足够的参数定义函数,因此至少有一个在堆栈上传递:
void func(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7 ...) {
int64_t* stack = reinterpret_cast<int64_t*>(&arg7);
int arg8 = (int)stack[1];
int arg9 = (int)stack[2];
}
请注意,堆栈上的地址以8为步长递增(&#34;自然&#34;用于访问堆栈的类型sizeof
等于8)。
答案 1 :(得分:0)
在实现libc syscall
函数的玩具版本时,我最近遇到了这个问题。如果可移植性不是问题,您总是可以使用程序集来执行此操作。以下是我的表现方式:
ssize_t syscall(size_t sysno, ...) {
// The x86_64 calling convention uses rdi, rsi, rdx, rcx, r8 and r9 as args
// The kernel interface uses rdi, rsi, rdx, r10, r8 and r9 for arg and rax for sysno
asm ("mov %%rdi,%%rax;" // System call number (sysno)
"mov %%rsi,%%rdi;" // first syscall arg and va_arg
"mov %%rdx,%%rsi;" // second syscall arg and va_arg
"mov %%rcx,%%rdx;" // third syscall arg and va_arg
"mov %%r8,%%r10;" // fourth syscall arg and va_arg
"mov %%r9,%%r8;" // fifth syscall arg and va_arg
"syscall;"
:
:
:
register ssize_t ret asm("rax");
return ret;
}
就我而言,我只是在汇编中编写了整个函数,但你可以做一些不那么极端的事情,例如:
register long arg1 asm("rdi");
register long arg2 asm("rsi");
...