x86_64 va_list结构的格式是什么?

R..*_*R.. 20 c linux x86-64 abi variadic-functions

任何人都有参考va_listx86_64 ABI(在Linux上使用的那个)的表示?我正在尝试调试一些代码,其中堆栈或参数似乎已损坏,这将真正有助于理解我应该看到的内容......

Sku*_*del 29

我把我的评论写进了一个答案.

这可能有所帮助.这是一个参考,虽然轻量级(编辑:原始链接死亡;替换Wayback机器保留链接).

变量参数列表参考从第50页开始,然后继续,第52-53页文档va_list:

va_list类型

va_list类型是一个数组,其中包含一个结构的单个元素,其中包含实现va_arg宏的必要信息.va_list类型的定义在图3.34中给出

// Figure 3.34
typedef struct {
   unsigned int gp_offset;
   unsigned int fp_offset;
   void *overflow_arg_area;
   void *reg_save_area;
} va_list[1];
Run Code Online (Sandbox Code Playgroud)

va_start宏

va_start宏按如下方式初始化结构:

reg_save_area 该元素指向寄存器保存区域的开始.

over?ow_arg_area该指针用于获取在堆栈上传递的参数.它使用堆栈上传递的第一个参数的地址进行初始化(如果有),然后始终更新以指向堆栈上下一个参数的开始.

gp_offset该元素保存从reg_save_area到保存下一个可用通用参数寄存器的位置的偏移量(以字节为单位).如果所有参数寄存器都已用尽,则将其设置为值48(6*8).

fp_offset该元素保存从reg_save_area到保存下一个可用浮点参数寄存器的位置的偏移量(以字节为单位).如果所有参数寄存器都已用尽,则将其设置为值304(6*8 + 16*16).

  • 保存区的大小为176字节,即(6 * 8 + 8 * 16)而不是(6 * 8 + 16 * 16)。是的,处理器有 16 个 SSE 寄存器,但只有前 8 个寄存器用于传递参数。除了感谢您提供的信息之外,它确实很有帮助。 (2认同)

R..*_*R.. 15

事实证明问题是gcc正在制作va_list数组类型.我的功能是签名:

void foo(va_list ap);
Run Code Online (Sandbox Code Playgroud)

我想将指针传递ap给另一个函数,所以我做了:

void foo(va_list ap)
{
    bar(&ap);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,数组类型衰减到函数参数列表中的指针类型,因此我不是将指针传递给原始结构,而是将指针传递给指针.

要解决此问题,我将代码更改为:

void foo(va_list ap)
{
    va_list ap2;
    va_copy(ap2, ap);
    bar(&ap2);
    va_end(ap2);
}
Run Code Online (Sandbox Code Playgroud)

这是我能想到的唯一可移植解决方案,它既考虑va_list了阵列类型的可能性,又考虑了它不是的可能性.

  • @bdonlan:`bar`被`foo`多次调用,每次调用`bar`都必须看到前一个的效果.如果你传递`va_list`,这显然是UB(按照ISO C); 为了这个用法,你需要传递一个指向`va_list`的指针. (3认同)