Каков формат структуры x86_64 va_list? - PullRequest
19 голосов
/ 10 февраля 2011

У кого-нибудь есть ссылка на представление va_list в x86_64 ABI (тот, который используется в Linux)?Я пытаюсь отладить некоторый код, в котором стек или аргументы кажутся поврежденными, и это действительно поможет понять, что я должен видеть ...

Ответы [ 3 ]

28 голосов
/ 10 февраля 2011

Я сделал свой комментарий в ответ.

Это может помочь . Это ссылка, хотя и небольшая ( РЕДАКТИРОВАТЬ : исходная ссылка не работает; заменена на сохраненную вэйкбэком ссылку).

Ссылка на список аргументов переменной начинается со стр. 50, а затем продолжается, стр. 52-53, документы va_list:

Тип va_list

Тип va_list представляет собой массив содержащий один элемент из одного структура, содержащая необходимые информация для реализации va_arg макро. Определение C для va_list тип приведен на рисунке 3.34

// Figure 3.34
typedef struct {
   unsigned int gp_offset;
   unsigned int fp_offset;
   void *overflow_arg_area;
   void *reg_save_area;
} va_list[1];

Макрос va_start

Макрос va_start инициализирует Структура выглядит следующим образом:

reg_save_area Элемент указывает на начало области сохранения регистра.

overflow_arg_area Этот указатель используется чтобы получить аргументы, переданные на стек. Инициализируется с адрес первого переданного аргумента стек, если есть, и всегда обновлен, чтобы указать на начало следующий аргумент в стеке.

gp_offset Элемент содержит смещение в байтах от reg_save_area до место, где следующий доступный генерал регистр аргумента цели сохранен. В В этом случае все регистры аргументов были исчерпан, он установлен на значение 48 (6 * 8).

fp_offset Элемент содержит смещение в байтах от reg_save_area до место, где следующее плавание доступно регистр аргумента точки сохранен. В В этом случае все регистры аргументов были исчерпан, установлено значение 304 (6 * 8 + 16 * 16).

14 голосов
/ 10 февраля 2011

Оказывается, проблема в том, что gcc сделал va_list типом массива.Моя функция была сигнатуры:

void foo(va_list ap);

, и я хотел передать указатель на ap другой функции, поэтому я сделал:

void foo(va_list ap)
{
    bar(&ap);
}

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

Чтобы обойти эту проблему, я изменил код на:

void foo(va_list ap)
{
    va_list ap2;
    va_copy(ap2, ap);
    bar(&ap2);
    va_end(ap2);
}

Это единственное портативное решение, которое я мог придумать, которое учитывает как возможность того, что va_list является типом массива, так и возможность того, что это не так.

0 голосов
/ 16 сентября 2018

В архитектуре i386 va_list является типом указателя. Однако в архитектуре AMD64 это тип массива. В чем разница? На самом деле, если вы примените операцию & к типу указателя, вы получите адрес этой переменной указателя. Но независимо от того, сколько раз вы применили & операцию к типу массива, значение будет одинаковым и будет равно адресу этого массива.

Итак, что вы должны делать в AMD64? Самый простой способ передать переменную va_list в функцию - просто передать ее без оператора * или &.

Например:

void foo(const char *fmt, ...) {
    va_list ap;
    int cnt;
    va_start(ap, fmt);
    bar(fmt, ap);
    va_end(ap);
    return cnt;
}
void bar(const char *fmt, va_list ap) {
    va_arg(ap, int);
    //do something
    test(ap);
}
void test(va_list ap) {
    va_arg(ap, int);
    //do something
}

Это просто работает! И вам не нужно беспокоиться о том, сколько у вас аргументов.

...