Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matching va_list types between compilers

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, and va_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;
}
like image 581
Carl Norum Avatar asked Sep 29 '10 18:09

Carl Norum


People also ask

What is va_list type?

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.

Is va_ list a pointer?

These macros are defined in the header file stdarg. h . The type va_list is used for argument pointer variables.

Where is va_list defined?

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, ...)


2 Answers

Since MSVC defines the Win64 ABI, you have found a bug in GCC. Please report it in GCC bugzilla.

like image 96
zwol Avatar answered Sep 21 '22 10:09

zwol


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.

like image 34
Michael Burr Avatar answered Sep 21 '22 10:09

Michael Burr