在编译器之间匹配va_list类型

时间:2010-09-29 18:31:32

标签: c gcc visual-studio-2003 variadic-functions

我有一个由一堆动态加载的模块组成的项目。最初,一切都是用MSVC 2003构建的,但最近我一直在努力让它与GCC一起工作。除了一个问题,一切都进展顺利。对于64位代码,GCC和MSVC不同意va_list是什么。对于32位,事情看起来很好。 64位不匹配导致的问题是,使用一个编译器构建的模块具有带有va_list参数的公共函数,并且该函数是从其他编译器构建的模块调用的。

规范没有说明va_list是什么,在第7.15节变量<stdarg.h> 之外,第3段:

  

声明的类型是

     

va_list

     

这是一种对象类型,适用于保存宏 va_start va_arg va_end所需的信息 va_copy

该段落意味着这是所有与编译器相关的东西 - 那么,有没有办法让这两个编译器就64位va_list的内容达成一致?对我的系统影响最小,使GCC与MSVC va_list匹配最好,但我会采取任何我能得到的解决方案。

感谢您的帮助!

编辑:

我做了一些32位测试,我也有问题,这让我感到惊讶,因为任何32位Intel平台之间都没有ABI差异。我正在使用的MSVC代码库将所有可变参数函数宏定义为:

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)

我从实际项目中略微简化了一下,但这是我用于测试的代码。使用GCC,这段代码肯定没有正确得到我的论点。也许这只是一个bug,正如Zack在下面所说的那样?

再次编辑:

我使用-O0-O0-O2获得了以下32位测试应用的工作结果,但不是-O3-Os-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;
}

5 个答案:

答案 0 :(得分:5)

由于MSVC定义了Win64 ABI,因此您在GCC中发现了一个错误。请在GCC bugzilla报告。

答案 1 :(得分:2)

由于va_list似乎没有ABI(或者至少MSVC和GCC不同意ABI),您可能需要自己编组这些参数。

我能想到解决这个问题的最直接的方法是在动态分配的内存块中封送你的变量参数,并将指针传递给该块。

当然,这样做的缺点是完全将界面更改为当前使用va_args的函数。

答案 2 :(得分:0)

因为没有关于va_args如何被手动处理的标准,如果你需要这个功能在交叉编译平台中是一致的,你可能最好滚动你自己的版本。我们没有这样做,最近因为支持代码库的其他目标而多次被烧毁。如果其他人有更好的解决方案,我会喜欢错误的。)

答案 3 :(得分:0)

尝试通过程序集级调试器(例如ollydbg)运行它,因为你的问题可能不是va_args,而是编译器期望传递args的方式(gcc可能期望它们以linux格式, msvc在哪里使用win64 __fastcall),如果有的话,这会给情况带来更多的亮点。另一个相当hacky的方法是尝试摆弄64位args所使用的定义,例如将msvc宏导入gcc头文件(当然使用项目本地副本),看看是否可以解决任何问题。

答案 4 :(得分:-1)

您使用的是哪种类型?严格地说,WinXX仅为varargs(see documentation for wsprintf in user32.dll)定义字符,字符串,指针和整数的行为。因此,如果传递浮点值或结构,则结果在技术上未指定Windows平台。