C++ 不带<;标准h>;x64以下

C++ 不带<;标准h>;x64以下,c++,abi,C++,Abi,变量数参数可以使用stdarg.h中的var_list完成,但如何在64位环境中不使用stdarg.h实现这一点 在32位环境中,函数参数使用堆栈从右向左传递,并且是4字节对齐的。所以可以使用地址访问变量参数 void func(int arg1, ...) { int arg2 = *(&arg1 + 1); int arg3 = *(&arg1 + 2); } 然而,在64位环境(SystemV ABI)中,参数是使用寄存器传递的。如果寄存器不能容纳所有寄存

变量数参数可以使用
stdarg.h
中的
var_list
完成,但如何在64位环境中不使用
stdarg.h
实现这一点

在32位环境中,函数参数使用堆栈从右向左传递,并且是4字节对齐的。所以可以使用地址访问变量参数

void func(int arg1, ...) {
    int arg2 = *(&arg1 + 1);
    int arg3 = *(&arg1 + 2);
}
然而,在64位环境(SystemV 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)
...

您可以使用足够的参数定义函数,以便在堆栈上至少传递一个参数:

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];
}
void func(int arg1、int arg2、int arg3、int arg4、int arg5、int arg6、int arg7…){
int64_t*stack=reinterpret_cast(&arg7);
int arg8=(int)堆栈[1];
int arg9=(int)堆栈[2];
}

请注意,堆栈上的地址以8的步长递增(用于访问堆栈的“自然”类型的sizeof等于8)。

我最近在实现一个玩具版的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");
...

为什么不研究一下您的
stdarg.h
实现,看看它能做什么?你到底为什么要这么做?(我还建议您在问题的前面提到您的特定ABI,因为阅读的人会认为您的断言“参数是使用堆栈传递的…”是错误的。如果您只对编译时变量参数感兴趣,您也可能会感兴趣。只需使用stdarg.h。这就是它的用途。我正在编写我的自定义内核,所以我不能使用标准C库。为什么“编译器特定”是一个问题?是否希望代码与多个不同的编译器兼容?如果是,哪些?