Printf() с несколькими агрументами
From Wiki.conus.info
Попробуем теперь немного расширить пример Hello, world!, написав в теле функции _main:
printf("a=%d; b=%d; c=%d", 1, 2, 3);
Компилируем при помощи MSVC 2010 Express, и в итоге получим:
$SG3830 DB 'a=%d; b=%d; c=%d', 00H ... push 3 push 2 push 1 push OFFSET $SG3830 call _printf add esp, 16 ; 00000010H
Все почти то же, за исключением того, что теперь видно, что аргументы для printf() заталкиваются в стек в обратном порядке: самый первый аргумент заталкивается последним. Всего аргумента 4. 4*4 = 16 - именно 16 байт занимают в стеке указатель на строку плюс еще 3 числа типа int. Переменные типа int в 32-битной системе, как известно, имеет ширину тоже 32 бита.
Когда при помощи инструкции "ADD ESP, X" корректируется указатель стека ESP после вызова какой-либо функции, зачастую можно сделать вывод о том, сколько аргументов у вызываемой функции было, разделив X на 4.
Конечно, это относится только к cdecl-методу передачи аргументов через стек.
См.также: x86 calling conventions
Иногда бывает так, что подряд идут несколько вызовов разных функций, но стек корректируется только один раз, после последней:
push a1 push a2 call ... ... push a1 call ... ... push a1 push a2 push a3 call ... add esp, 24
Скомпилируем то же самое в Linux при помощи GCC 4.4.1 и посмотрим в IDA что вышло:
main proc near var_10 = dword ptr -10h var_C = dword ptr -0Ch var_8 = dword ptr -8 var_4 = dword ptr -4 push ebp mov ebp, esp and esp, 0FFFFFFF0h sub esp, 10h mov eax, offset aADBDCD ; "a=%d; b=%d; c=%d" mov [esp+10h+var_4], 3 mov [esp+10h+var_8], 2 mov [esp+10h+var_C], 1 mov [esp+10h+var_10], eax call _printf mov eax, 0 leave retn main endp
Можно сказать, что этот короткий код созданный GCC отличается от кода MSVC только способом помещения значений в стек. Здесь GCC снова работает со стеком напрямую без PUSH/POP.
Кстати, эта разница неплохо иллюстрирует тот важный момент, что процессору, в общем, все равно как будут передаваться параметры функций. Можно создать гипотетический компилятор, который будет передавать их при помощи указателя на структуру с параметрами, не пользуясь стеком вообще.