Тонкости работы с vararg - C (СИ)
Формулировка задачи:
Думается, многим будет полезно попробовать разобраться с одной проблемой. С одной стороны не наступить на эти грабли в будущем, с другой стороны попробовать докопаться до сути ошибки, которая с виду возникает на ровном месте
При работе на sparc'е этот код работает одинаково правильно как в режиме 32, так и в режиме 64. При работе на intel'е этот код работает правильно в режиме 32, но ошибочно в режиме 64
Собственно, вопрос простой - почему так происходит? Или, другими словами, где ошибка в коде?
Традиционная просьба прятать ответы под спойлер
#include <stdio.h> #include <stdarg.h> void foo (va_list ap) { int r; r = va_arg (ap, int); printf ("param1 = 0x%x\n", r); r = va_arg (ap, int); printf ("param2 = 0x%x\n", r); r = va_arg (ap, int); printf ("param3 = 0x%x\n", r); } void bar (int x, ...) { va_list ap; va_start (ap, x); foo (ap); foo (ap); va_end (ap); } int main (void) { bar (0x11111111, 0x22222222, 0x33333333, 0x44444444); return 0; }
$ gcc -m32 t.c $ ./a.out param1 = 0x22222222 param2 = 0x33333333 param3 = 0x44444444 param1 = 0x22222222 param2 = 0x33333333 param3 = 0x44444444 $ gcc -m64 t.c $ ./a.out param1 = 0x22222222 param2 = 0x33333333 param3 = 0x44444444 param1 = 0x59407c60 param2 = 0x5941ade0 param3 = 0x0
Решение задачи: «Тонкости работы с vararg»
textual
Листинг программы
.LC0: .string "param1 = 0x%x\n" .LC1: .string "param2 = 0x%x\n" .LC2: .string "param3 = 0x%x\n" foo(__va_list_tag*): push rbp mov rbp, rsp sub rsp, 32 mov QWORD PTR [rbp-24], rdi mov rax, QWORD PTR [rbp-24] mov eax, DWORD PTR [rax] cmp eax, 47 ja .L2 mov rax, QWORD PTR [rbp-24] mov rdx, QWORD PTR [rax+16] mov rax, QWORD PTR [rbp-24] mov eax, DWORD PTR [rax] mov eax, eax add rax, rdx mov rdx, QWORD PTR [rbp-24] mov edx, DWORD PTR [rdx] lea ecx, [rdx+8] mov rdx, QWORD PTR [rbp-24] mov DWORD PTR [rdx], ecx jmp .L3 .L2: mov rax, QWORD PTR [rbp-24] mov rax, QWORD PTR [rax+8] lea rcx, [rax+8] mov rdx, QWORD PTR [rbp-24] mov QWORD PTR [rdx+8], rcx .L3: mov eax, DWORD PTR [rax] mov DWORD PTR [rbp-4], eax mov eax, DWORD PTR [rbp-4] mov esi, eax mov edi, OFFSET FLAT:.LC0 mov eax, 0 call printf mov rax, QWORD PTR [rbp-24] mov eax, DWORD PTR [rax] cmp eax, 47 ja .L4 mov rax, QWORD PTR [rbp-24] mov rdx, QWORD PTR [rax+16] mov rax, QWORD PTR [rbp-24] mov eax, DWORD PTR [rax] mov eax, eax add rax, rdx mov rdx, QWORD PTR [rbp-24] mov edx, DWORD PTR [rdx] lea ecx, [rdx+8] mov rdx, QWORD PTR [rbp-24] mov DWORD PTR [rdx], ecx jmp .L5 .L4: mov rax, QWORD PTR [rbp-24] mov rax, QWORD PTR [rax+8] lea rcx, [rax+8] mov rdx, QWORD PTR [rbp-24] mov QWORD PTR [rdx+8], rcx .L5: mov eax, DWORD PTR [rax] mov DWORD PTR [rbp-4], eax mov eax, DWORD PTR [rbp-4] mov esi, eax mov edi, OFFSET FLAT:.LC1 mov eax, 0 call printf mov rax, QWORD PTR [rbp-24] mov eax, DWORD PTR [rax] cmp eax, 47 ja .L6 mov rax, QWORD PTR [rbp-24] mov rdx, QWORD PTR [rax+16] mov rax, QWORD PTR [rbp-24] mov eax, DWORD PTR [rax] mov eax, eax add rax, rdx mov rdx, QWORD PTR [rbp-24] mov edx, DWORD PTR [rdx] lea ecx, [rdx+8] mov rdx, QWORD PTR [rbp-24] mov DWORD PTR [rdx], ecx jmp .L7 .L6: mov rax, QWORD PTR [rbp-24] mov rax, QWORD PTR [rax+8] lea rcx, [rax+8] mov rdx, QWORD PTR [rbp-24] mov QWORD PTR [rdx+8], rcx .L7: mov eax, DWORD PTR [rax] mov DWORD PTR [rbp-4], eax mov eax, DWORD PTR [rbp-4] mov esi, eax mov edi, OFFSET FLAT:.LC2 mov eax, 0 call printf nop leave ret bar(int, ...): push rbp mov rbp, rsp sub rsp, 224 mov DWORD PTR [rbp-212], edi mov QWORD PTR [rbp-168], rsi mov QWORD PTR [rbp-160], rdx mov QWORD PTR [rbp-152], rcx mov QWORD PTR [rbp-144], r8 mov QWORD PTR [rbp-136], r9 test al, al je .L10 movaps XMMWORD PTR [rbp-128], xmm0 movaps XMMWORD PTR [rbp-112], xmm1 movaps XMMWORD PTR [rbp-96], xmm2 movaps XMMWORD PTR [rbp-80], xmm3 movaps XMMWORD PTR [rbp-64], xmm4 movaps XMMWORD PTR [rbp-48], xmm5 movaps XMMWORD PTR [rbp-32], xmm6 movaps XMMWORD PTR [rbp-16], xmm7 .L10: mov DWORD PTR [rbp-200], 8 mov DWORD PTR [rbp-196], 48 lea rax, [rbp+16] mov QWORD PTR [rbp-192], rax lea rax, [rbp-176] mov QWORD PTR [rbp-184], rax lea rax, [rbp-200] mov rdi, rax call foo(__va_list_tag*) lea rax, [rbp-200] mov rdi, rax call foo(__va_list_tag*) nop leave ret main: push rbp mov rbp, rsp mov ecx, 1145324612 mov edx, 858993459 mov esi, 572662306 mov edi, 286331153 mov eax, 0 call bar(int, ...) mov eax, 0 pop rbp ret
Объяснение кода листинга программы
- foo(__va_list_tag*):
- Проверяет, является ли первый аргумент 47. Если это так, то аргументы 4 и 5 выводятся как 0x%x и 0x%x.
- Если первый аргумент не равен 47, то второй аргумент выводится как 0x%x.
- Затем происходит обход стека вызовов и вывод каждого аргумента в формате 0x%x.
- В конце функция foo() вызывается с аргументами 4 и 5.
- bar(int, ...):
- Проверяет, является ли первый аргумент 47. Если это так, то аргументы 4 и 5 выводятся как 0x%x и 0x%x.
- Если первый аргумент не равен 47, то второй аргумент выводится как 0x%x.
- Затем происходит обход стека вызовов и вывод каждого аргумента в формате 0x%x.
- В конце функция bar() вызывается с аргументами 4 и 5.
- main():
- Инициализирует стек вызовов с аргументами 1145324612, 858993459, 572662306 и 286331153.
- Вызывает функцию bar() с этими аргументами.
- Возвращает 0.