Printf() с несколькими агрументами

From Wiki.conus.info

Jump to: navigation, search

Попробуем теперь немного расширить пример 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.

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

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox