I have a project that consists of a bunch of dynamically loaded modules. Originally, everything was always built with MSVC 2003, but lately I've been working on getting it to work with GCC. Everything has been going pretty smoothly, except for one problem. For 64-bit code, GCC and MSVC don't agree about what a va_list
is. For 32-bit, things seem to line up fine. The problem the 64-bit mismatch causes is when a module built with one compiler has a public function with a va_list
parameter and that function is called from a module built by the other compiler.
The spec says nothing about what a va_list
is, outside of Section 7.15 Variable arguments <stdarg.h>
, paragraph 3:
The type declared is
va_list
which is an object type suitable for holding information needed by the macros
va_start
,va_arg
,va_end
, andva_copy
.
That paragraph just means this is all compiler dependent stuff - so, is there a way to make these two compilers agree on the contents of a 64-bit va_list
? For least impact to my system, making GCC match the MSVC va_list
would be best, but I'll take any solution I can get.
Thanks for helping out!
Edit:
I did some 32-bit testing, and I have problems there too, which surprised me since there are supposedly no ABI differences between any 32-bit Intel platforms. The MSVC codebase I'm using defines all of the variadic function macros as:
typedef char *va_list;
#define intsizeof(n) ((sizeof(n) + sizeof(int) - 1) &~(sizeof(int) - 1))
#define va_start(ap, v) (ap = (va_list)&(v) + intsizeof(v))
#define va_arg(ap, t) (*(t *) ((ap += intsizeof(t)) - intsizeof(t)))
#define va_end(ap) (ap = (va_list)0)
I've simplified a bit from the real project, but this is the code I was using for my test. With GCC, this code definitely isn't correctly getting my arguments. Maybe it is just a bug, as Zack suggests below?
Edit again:
I get working results for the following 32-bit test application with -O0
, -O0
, and -O2
, but not -O3
, -Os
, and -Oz
:
typedef char *va_list;
#define intsizeof(n) ((sizeof(n) + sizeof(int) - 1) &~(sizeof(int) - 1))
#define va_start(ap, v) (ap = (va_list)&(v) + intsizeof(v))
#define va_arg(ap, t) (*(t *) ((ap += intsizeof(t)) - intsizeof(t)))
#define va_end(ap) (ap = (va_list)0)
int printf(const char *format, ...);
int f(int n, ...)
{
int r = 0;
va_list ap;
va_start(ap, n);
while (n--)
r = va_arg(ap, int);
va_end(ap);
return r;
}
int main(int argc, char **argv)
{
int r;
r = f(1, 1, 2, 3, 4, 5);
printf("%x\n", r);
r = f(2, 1, 2, 3, 4, 5);
printf("%x\n", r);
r = f(3, 1, 2, 3, 4, 5);
printf("%x\n", r);
r = f(4, 1, 2, 3, 4, 5);
printf("%x\n", r);
r = f(5, 1, 2, 3, 4, 5);
printf("%x\n", r);
return 0;
}
va_list is a complete object type suitable for holding the information needed by the macros va_start, va_copy, va_arg, and va_end. If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end.
These macros are defined in the header file stdarg. h . The type va_list is used for argument pointer variables.
The va_list array type is defined in stdarg.h. This type holds data required by the va_arg and va_end routines. The va_list type is defined as: typedef char *va_list; and is used as shown in the following example: #include <stdarg.h> int varfunc (char *buf, int id, ...)
Since MSVC defines the Win64 ABI, you have found a bug in GCC. Please report it in GCC bugzilla.
Since there doesn't seem to be an ABI for va_list
(or at least MSVC and GCC don't agree on an ABI), you'll probably need to marshal those parameters yourself.
The most straightforward way I can think of to work around this problem off the top of my head is to marshal your variable parameters in a dynamically allocated block of memory and pass a pointer to that block.
Of course, this has the drawback of completely changing the interface to the functions that are currently using va_args.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With