使用c ++的魔法,va_arg在64位架构上无法正常工作

时间:2016-03-10 12:04:57

标签: c++ visual-c++ 64-bit variadic-functions

我有一个非常有趣的问题,我的朋友和同事都没有人帮助我。 那么,让我们来看看下一个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”正常启动。
虽然,我试图使用'浮动',但它也不起作用!
我搜索了所有va_arg堆栈溢出问题,但没有找到答案!
那我哪里错了?
谢谢!
更新1。:
是的,因为“概念堆栈与物理堆栈不匹配”而无法正常工作
“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


来自MSDN的Viorel_:
https://social.msdn.microsoft.com/Forums/en-US/7a2d9bb5-2b83-4bc5-a018-d7b460fa5550/magic-with-c-vaarg-doesnt-work-on-64-bit-architecture-with-double-properly?forum=vcgeneral

1 个答案:

答案 0 :(得分:1)

必须声明

va_arg函数并调用它们。您没有拨打va_arg功能,请拨打void __cdecl Ix::qq(long a, long b, double x, long c);

问题的一部分可能是&#34;我只是在堆栈上推送参数#34;。 堆栈不存在。这是一个概念性的C ++调用堆栈(所有已调用但尚未按时间顺序返回的函数列表及其参数)和物理x64堆栈(RBP / RSP寄存器)。

您的问题是概念堆栈与物理堆栈不匹配。并非所有功能参数都在物理堆栈上。 var_arg机制必须动态地确定函数参数的位置,这可能意味着物理堆栈上的可变函数的参数是