I've a very interesting question, nobody of my friends and colleagues can't help me. So, let's have a look to the next c++ code:
#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
}
It works fine on the Win32 architecture (visual studio 2013, win7x64)
But when I start it on x64 the "d" variable in the function funcM have "4.343564450161e-311#DEN" value
Other variables like "l1","l2","l4", "inst" initiate normally.
Although, I've tried to work with 'float', it doesn't work too!
I've searched through all va_arg stack overflow questions and didn't find an answer!
So where have I wrong?
Thank you!
UPDATE 1.:
yeah, its not work because of "conceptual stack doesn't match the physical stack"
The "d" variable goes through xmm3 register and va_arg trying to work with xmm0.
Hope smbd sometime will find it's useful!
UPDATE 2. Solution of problem!
In case of 64-bit programs, the calling convention is different: the values are not always passed via stack: https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx . Some of them are passed via registers. Therefore, va_list (which works with stack and is suitable for variable-argument functions) cannot be used here.
Try this workaround:
struct ClassIx
{
virtual void __cdecl qq(...);
};
ClassIx* ins = (ClassIx*)((void*)&x);
ins->qq(p1, p2, p3, p4); //it will call funcM
by Viorel_ from MSDN:
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
va_arg
functions must be declared and called as such. You don't call a va_arg
function, you call void __cdecl Ix::qq(long a, long b, double x, long c);
.
Part of the problem muight be "I just push parametres on the stack". The stack doesn't exist. There's a conceptual C++ call stack (the list of all functions that have been called, but did not yet return, in chronological order, with their arguments) and a physical x64 stack (RBP/RSP registers).
Your problem is that the conceptual stack doesn't match the physical stack. Not all function parameters are on the physical stack. The var_arg
mechanism has to dynamically figure out where the function parameters are, though, and that may mean that parameters to variadic functions are on the physical stack.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.