c++ 的魔法,va_arg 不能在 64 位架构上正常工作
magic with c++, va_arg doesn't work on 64 bit architecture with double properly
我有一个非常有趣的问题,我的朋友和同事中没有人不能帮助我。
那么,让我们看看下一个c++代码:
#include <stdio.h>
#include <stdarg.h>
typedef void* VirtualMethodTable;
void funcM(void* __this, ...);
__interface Ix
{
void __cdecl qq(long a, long b, double x, long c);
};
struct tagInterface
{
tagInterface()
{
VirtualMethodTable* VMT = new VirtualMethodTable[1];
VMT[0] = (void*)&funcM; //here's funcM assignment
this->VMT = VMT;
}
~tagInterface(){ delete[] VMT; }
VirtualMethodTable* VMT;
};
void func1(long a, long b, double x, long c)
{
//some_logic
}
void funcM(void* __this, ...)
{
va_list marker;
va_start(marker, __this);
marker -= sizeof(__this); // line 1
tagInterface* inst = va_arg(marker, tagInterface*); //line 2
//we can comment line 1 and line 2 and it still will work as earlier (doesn't work)
long l1 = va_arg(marker, long);
long l2 = va_arg(marker, long);
double d = va_arg(marker, double);//d = 4.343564450161e-311#DEN, not 3.3
long l4 = va_arg(marker, long);
func1(l1, l2, d, l4);
va_end(marker);
}
long main()
{
tagInterface x;
Ix* ins = (Ix*)((void*)&x);
long p1 = 1;
long p2 = 2;
double p3 = 3.3;
long p4 = 4;
ins->qq(p1, p2, p3, p4); //it will call funcM
}
在 Win32 架构上运行良好(visual studio 2013,win7x64)
但是当我在 x64 上启动它时,函数 funcM 中的 "d" 变量具有“4.343564450161e-311#DEN”值
"l1"、"l2"、"l4"、"inst" 等其他变量正常启动。
虽然,我已经尝试使用 'float',但它也不起作用!
我搜索了所有 va_arg 个堆栈溢出问题,但没有找到答案!
那我哪里错了?
谢谢!
更新 1.:
是的,它不起作用,因为 "conceptual stack doesn't match the physical stack"
"d" 变量通过 xmm3 寄存器并 va_arg 尝试与 xmm0 一起工作。
希望 smbd 有时会发现它很有用!
更新 2. 问题的解决!
对于 64 位程序,调用约定不同:值并不总是通过堆栈传递:https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx。其中一些通过寄存器传递。因此,这里不能使用va_list(与堆栈一起使用,适用于可变参数函数)。
试试这个解决方法:
struct ClassIx
{
virtual void __cdecl qq(...);
};
ClassIx* ins = (ClassIx*)((void*)&x);
ins->qq(p1, p2, p3, p4); //it will call funcM
va_arg
函数必须这样声明和调用。您不调用 va_arg
函数,而是调用 void __cdecl Ix::qq(long a, long b, double x, long c);
.
部分问题可能是 "I just push parametres on the stack"。 堆栈 不存在。有一个概念性的 C++ 调用堆栈(所有已调用但尚未调用的函数的列表 return,按时间顺序,及其参数)和一个物理 x64 堆栈(RBP/RSP 寄存器)。
您的问题是概念堆栈与物理堆栈不匹配。并非所有函数参数都在物理堆栈上。但是,var_arg
机制必须动态找出函数参数的位置,这可能意味着可变函数的参数 是 在物理堆栈上。
我有一个非常有趣的问题,我的朋友和同事中没有人不能帮助我。 那么,让我们看看下一个c++代码:
#include <stdio.h>
#include <stdarg.h>
typedef void* VirtualMethodTable;
void funcM(void* __this, ...);
__interface Ix
{
void __cdecl qq(long a, long b, double x, long c);
};
struct tagInterface
{
tagInterface()
{
VirtualMethodTable* VMT = new VirtualMethodTable[1];
VMT[0] = (void*)&funcM; //here's funcM assignment
this->VMT = VMT;
}
~tagInterface(){ delete[] VMT; }
VirtualMethodTable* VMT;
};
void func1(long a, long b, double x, long c)
{
//some_logic
}
void funcM(void* __this, ...)
{
va_list marker;
va_start(marker, __this);
marker -= sizeof(__this); // line 1
tagInterface* inst = va_arg(marker, tagInterface*); //line 2
//we can comment line 1 and line 2 and it still will work as earlier (doesn't work)
long l1 = va_arg(marker, long);
long l2 = va_arg(marker, long);
double d = va_arg(marker, double);//d = 4.343564450161e-311#DEN, not 3.3
long l4 = va_arg(marker, long);
func1(l1, l2, d, l4);
va_end(marker);
}
long main()
{
tagInterface x;
Ix* ins = (Ix*)((void*)&x);
long p1 = 1;
long p2 = 2;
double p3 = 3.3;
long p4 = 4;
ins->qq(p1, p2, p3, p4); //it will call funcM
}
在 Win32 架构上运行良好(visual studio 2013,win7x64)
但是当我在 x64 上启动它时,函数 funcM 中的 "d" 变量具有“4.343564450161e-311#DEN”值
"l1"、"l2"、"l4"、"inst" 等其他变量正常启动。
虽然,我已经尝试使用 'float',但它也不起作用!
我搜索了所有 va_arg 个堆栈溢出问题,但没有找到答案!
那我哪里错了?
谢谢!
更新 1.:
是的,它不起作用,因为 "conceptual stack doesn't match the physical stack"
"d" 变量通过 xmm3 寄存器并 va_arg 尝试与 xmm0 一起工作。
希望 smbd 有时会发现它很有用!
更新 2. 问题的解决!
对于 64 位程序,调用约定不同:值并不总是通过堆栈传递:https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx。其中一些通过寄存器传递。因此,这里不能使用va_list(与堆栈一起使用,适用于可变参数函数)。
试试这个解决方法:
struct ClassIx
{
virtual void __cdecl qq(...);
};
ClassIx* ins = (ClassIx*)((void*)&x);
ins->qq(p1, p2, p3, p4); //it will call funcM
va_arg
函数必须这样声明和调用。您不调用 va_arg
函数,而是调用 void __cdecl Ix::qq(long a, long b, double x, long c);
.
部分问题可能是 "I just push parametres on the stack"。 堆栈 不存在。有一个概念性的 C++ 调用堆栈(所有已调用但尚未调用的函数的列表 return,按时间顺序,及其参数)和一个物理 x64 堆栈(RBP/RSP 寄存器)。
您的问题是概念堆栈与物理堆栈不匹配。并非所有函数参数都在物理堆栈上。但是,var_arg
机制必须动态找出函数参数的位置,这可能意味着可变函数的参数 是 在物理堆栈上。