va_list
有人对 x86_64 ABI(Linux 上使用的那个)的表示有参考吗?我正在尝试调试一些堆栈或参数似乎已损坏的代码,这真的有助于理解我应该看到的内容......
3 回答
x86-64 System V ABi 文档可能会有所帮助。这是一个参考,虽然很轻。
变量参数列表参考从第 54 页开始,然后继续,第 56-57 页文档va_list
:
va_list
类型_该
va_list
类型是一个数组,其中包含一个结构的单个元素,其中包含实现va_arg
宏所需的信息。va_list
图 3.34 给出了类型的 C 定义。图 3.34:
va_list
类型声明typedef struct { unsigned int gp_offset; unsigned int fp_offset; void *overflow_arg_area; void *reg_save_area; } va_list[1];
va_start
宏观_
va_start
宏初始化结构如下:
reg_save_area
该元素指向寄存器保存区域的开始。
overflow_arg_area
此指针用于获取堆栈上传递的参数。它使用堆栈上传递的第一个参数的地址进行初始化,如果有的话,然后总是更新以指向堆栈上下一个参数的开始。
gp_offset
该元素保存从reg_save_area
到保存下一个可用通用参数寄存器的位置的偏移量(以字节为单位)。如果所有参数寄存器都已用尽,则将其设置为值 48 (6 * 8)。
fp_offset
该元素保存从reg_save_area
到保存下一个可用浮点参数寄存器的位置的偏移量(以字节为单位)。如果所有参数寄存器都已用尽,则将其设置为值 304 (6 * 8 + 16 * 16)。
事实证明,问题在于 gcc 制作va_list
了一个数组类型。我的功能是签名:
void foo(va_list ap);
我想传递一个指向ap
另一个函数的指针,所以我做了:
void foo(va_list ap)
{
bar(&ap);
}
不幸的是,数组类型衰减为函数参数列表中的指针类型,因此我没有将指针传递给原始结构,而是将指针传递给指针。
为了解决这个问题,我将代码更改为:
void foo(va_list ap)
{
va_list ap2;
va_copy(ap2, ap);
bar(&ap2);
va_end(ap2);
}
这是我能想出的唯一可移植解决方案,它既可以解释va_list
为数组类型的可能性,也可以解释不是数组类型的可能性。
在 i386 架构中,va_list 是指针类型。但是,在 AMD64 架构中,它是一个数组类型。有什么不同?实际上,如果你对一个指针类型应用 & 操作,你会得到这个指针变量的地址。但是无论你对一个数组类型应用&操作多少次,其值都是一样的,并且等于这个数组的地址。
那么,在 AMD64 中应该怎么做呢?在函数中传递 va_list 变量的最简单方法是不带 * 或 & 运算符传递它。
例如:
void foo(const char *fmt, ...) {
va_list ap;
int cnt;
va_start(ap, fmt);
bar(fmt, ap);
va_end(ap);
return cnt;
}
void bar(const char *fmt, va_list ap) {
va_arg(ap, int);
//do something
test(ap);
}
void test(va_list ap) {
va_arg(ap, int);
//do something
}
它只是工作!而且您不必担心您有多少参数。