Каков формат структуры x86_64 va_list?
У любого есть ссылка для представления va_list
в x86_64 ABI (тот, который используется в Linux)? Я пытаюсь отладить некоторый код, где стек или аргументы кажутся поврежденными, и это действительно поможет понять, что я должен видеть...
3 ответа
Я сделал свой комментарий в ответ.
Это может помочь. Это ссылка, хотя и легковесная (РЕДАКТИРОВАТЬ: исходная ссылка не работает; заменена Wayback Machine-preserved link).
Ссылка на список аргументов переменных начинается на стр. 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).
Оказывается, проблема была в создании 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
это тип массива и вероятность того, что это не так.
В архитектуре 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
}
Это просто работает! И вам не нужно беспокоиться о том, сколько у вас аргументов.