Соответствие типов va_list между компиляторами - PullRequest
8 голосов
/ 29 сентября 2010

У меня есть проект, который состоит из группы динамически загружаемых модулей.Первоначально все было построено с 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-битное тестирование, и у меня там тоже есть проблемы, что меня удивило, так как якобы нет различий ABI между любыми 32-битными платформами Intel.Кодовая база 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 этот код определенно не получает мои аргументы.Может быть, это просто ошибка, как советует Зак ниже?

Редактировать еще раз:

Я получаю результаты работы для следующего 32-разрядного тестового приложения с -O0, -O0 и -O2, но не -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 ]

5 голосов
/ 29 сентября 2010

Поскольку MSVC определяет Win64 ABI, вы обнаружили ошибку в GCC.Пожалуйста, сообщите об этом в GCC bugzilla .

2 голосов
/ 29 сентября 2010

Поскольку для va_list не существует ABI (или, по крайней мере, MSVC и GCC не согласны с ABI), вам, вероятно, придется маршалировать эти параметры самостоятельно.

Самый простой способ обойти эту проблему - подумать о том, как распределить параметры вашей переменной в динамически выделенном блоке памяти и передать указатель на этот блок.

Конечно, этоНедостатком является полное изменение интерфейса для функций, которые в данный момент используют va_args.

0 голосов
/ 29 сентября 2010

Какие типы вы используете?Строго говоря, WinXX определяет поведение только для символов, строк, указателей и целых чисел для varargs ( см. Документацию по wsprintf в user32.dll ).Таким образом, если вы передадите значения или структуры с плавающей запятой, результаты технически не определены для платформы Windows.

0 голосов
/ 29 сентября 2010

Попробуйте запустить его через отладчик уровня сборки, такой как ollydbg, поскольку ваша проблема может быть не в va_args, а в том, что компилятор ожидает передачи аргументов (gcc может ожидать их в формате linux, где в качестве msvc используется win64 __fastcall), если что-нибудь, это даст немного больше света ситуации. Другой, довольно хакерский подход, это попытаться поиграться с определениями, используемыми для 64-битных аргументов, такими как импорт макросов msvc в заголовок gcc (конечно, используйте локальную копию проекта), посмотрите, исправляет ли это что-нибудь.

0 голосов
/ 29 сентября 2010

Поскольку не существует стандарта для обработки va_args, если вам нужна эта функциональность для обеспечения совместимости в кросс-скомпилированной платформе, вам, вероятно, будет лучше развернуть собственную версию. Мы не делали этого и недавно были сожжены несколько раз в результате при поддержке дополнительных целей для нашей кодовой базы. Хотелось бы ошибаться, если бы у других было лучшее решение :)

...