145 lines
7.4 KiB
C
145 lines
7.4 KiB
C
// stdarg.h - Макросы для работы с переменным числом аргументов функций
|
||
//
|
||
// Этот заголовочный файл реализует механизм передачи переменного числа
|
||
// аргументов (variadic functions) согласно ABI System V для x86-64.
|
||
//
|
||
// Структура __va_state отражает внутреннее представление области
|
||
// сохранения регистров, создаваемой компилятором для variadic функций.
|
||
//
|
||
// Поддерживаются как стандартные макросы (va_start, va_arg, va_end, va_copy),
|
||
// так и русские синонимы (арг_начало, арг_взять, арг_конец, арг_копия).
|
||
|
||
#ifndef _STDARG_H_RU
|
||
#define _STDARG_H_RU
|
||
|
||
// ============================================================
|
||
// Внутреннее состояние списка аргументов
|
||
// ============================================================
|
||
|
||
// Структура описывает состояние итератора по аргументам variadic функции.
|
||
// Она отражает ABI x86-64 System V:
|
||
// - gp_offset: смещение в области сохранения регистров общего назначения
|
||
// - fp_offset: смещение в области сохранения регистров с плавающей точкой
|
||
// - overflow_arg_area: указатель на аргументы, переданные через стек
|
||
// - reg_save_area: указатель на область сохранения регистров
|
||
typedef struct {
|
||
unsigned int gp_offset;
|
||
unsigned int fp_offset;
|
||
void *overflow_arg_area;
|
||
void *reg_save_area;
|
||
} __va_state;
|
||
|
||
// Список аргументов — массив из одного элемента __va_state.
|
||
// Такое представление позволяет передавать список по ссылке
|
||
// и модифицировать его внутри функций.
|
||
typedef __va_state va_list[1];
|
||
|
||
// ============================================================
|
||
// Инициализация и завершение работы с аргументами
|
||
// ============================================================
|
||
|
||
// Инициализация списка аргументов.
|
||
// Копирует состояние из специальной области __va_area__,
|
||
// которая создаётся компилятором для каждой variadic функции.
|
||
#define va_start(ap, last) \
|
||
do { *(ap) = *(__va_state *)__va_area__; } while (0)
|
||
|
||
// Завершение работы со списком аргументов.
|
||
// В данной реализации не требует никаких действий.
|
||
#define va_end(ap)
|
||
|
||
// ============================================================
|
||
// Внутренние функции извлечения аргументов
|
||
// ============================================================
|
||
|
||
// Извлечение аргумента из области переполнения (переданного через стек).
|
||
// Выравнивает указатель согласно требованиям типа и продвигает
|
||
// область переполнения вперёд с учётом размера и выравнивания.
|
||
static void *__va_read_from_stack(__va_state *ap, int sz, int align) {
|
||
void *p = ap->overflow_arg_area;
|
||
if (align > 8)
|
||
p = (p + 15) / 16 * 16;
|
||
ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8;
|
||
return p;
|
||
}
|
||
|
||
// Извлечение аргумента из области сохранения регистров общего назначения.
|
||
// Если регистры общего назначения исчерпаны (gp_offset >= 48),
|
||
// переключается на извлечение из стека.
|
||
static void *__va_read_from_gp(__va_state *ap, int sz, int align) {
|
||
if (ap->gp_offset >= 48)
|
||
return __va_read_from_stack(ap, sz, align);
|
||
|
||
void *r = ap->reg_save_area + ap->gp_offset;
|
||
ap->gp_offset += 8;
|
||
return r;
|
||
}
|
||
|
||
// Извлечение аргумента из области сохранения регистров с плавающей точкой.
|
||
// Если регистры с плавающей точкой исчерпаны (fp_offset >= 112),
|
||
// переключается на извлечение из стека.
|
||
static void *__va_read_from_fp(__va_state *ap, int sz, int align) {
|
||
if (ap->fp_offset >= 112)
|
||
return __va_read_from_stack(ap, sz, align);
|
||
|
||
void *r = ap->reg_save_area + ap->fp_offset;
|
||
ap->fp_offset += 8;
|
||
return r;
|
||
}
|
||
|
||
// ============================================================
|
||
// Извлечение следующего аргумента
|
||
// ============================================================
|
||
|
||
// Извлечение следующего аргумента заданного типа из списка.
|
||
// Использует встроенную функцию __builtin_reg_class для определения
|
||
// класса регистра (0 - общего назначения, 1 - плавающая точка, 2 - память)
|
||
// и вызывает соответствующую функцию извлечения.
|
||
#define va_arg(ap, ty) \
|
||
({ \
|
||
int klass = __builtin_reg_class(ty); \
|
||
*(ty *)(klass == 0 ? __va_read_from_gp(ap, sizeof(ty), _Alignof(ty)) : \
|
||
klass == 1 ? __va_read_from_fp(ap, sizeof(ty), _Alignof(ty)) : \
|
||
__va_read_from_stack(ap, sizeof(ty), _Alignof(ty))); \
|
||
})
|
||
|
||
// ============================================================
|
||
// Копирование списка аргументов
|
||
// ============================================================
|
||
|
||
// Копирование состояния списка аргументов.
|
||
// Позволяет итерироваться по аргументам независимо в разных местах.
|
||
#define va_copy(dest, src) ((dest)[0] = (src)[0])
|
||
|
||
// ============================================================
|
||
// Совместимость с GCC
|
||
// ============================================================
|
||
|
||
// Макрос, указывающий, что va_list совместим с GCC
|
||
#define __GNUC_VA_LIST 1
|
||
|
||
// Альтернативное имя типа для совместимости с GCC
|
||
typedef va_list __gnuc_va_list;
|
||
|
||
// ============================================================
|
||
// Русские синонимы для удобства
|
||
// ============================================================
|
||
|
||
// Тип списка аргументов
|
||
#define аргументы va_list
|
||
|
||
// Инициализация списка аргументов
|
||
// Пример: арг_начало(args, последний);
|
||
#define арг_начало va_start
|
||
|
||
// Завершение работы со списком аргументов
|
||
#define арг_конец va_end
|
||
|
||
// Извлечение следующего аргумента
|
||
// Пример: цел x = арг_взять(args, цел);
|
||
#define арг_взять va_arg
|
||
|
||
// Копирование списка аргументов
|
||
#define арг_копия va_copy
|
||
|
||
#endif // _STDARG_H_RU
|