Reputation: 29
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
Upvotes: 0
Views: 467
Reputation: 179819
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.
Upvotes: 1