Передача параметров через стек

From Wiki.conus.info

Jump to: navigation, search

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

#include <stdio.h>
 
int f (int a, int b, int c)
{
	return a*b+c;
};
 
int main() 
{
	printf ("%d\n", f(1, 2, 3));
	return 0;
};

Имеем в итоге (MSVC 2010 Express):

_TEXT	SEGMENT
_a$ = 8							; size = 4
_b$ = 12						; size = 4
_c$ = 16						; size = 4
_f	PROC
; File c:\...\1.c
; Line 4
	push	ebp
	mov	ebp, esp
; Line 5
	mov	eax, DWORD PTR _a$[ebp]
	imul	eax, DWORD PTR _b$[ebp]
	add	eax, DWORD PTR _c$[ebp]
; Line 6
	pop	ebp
	ret	0
_f	ENDP
 
_main	PROC
; Line 9
	push	ebp
	mov	ebp, esp
; Line 10
	push	3
	push	2
	push	1
	call	_f
	add	esp, 12					; 0000000cH
	push	eax
	push	OFFSET $SG2463 ; '%d', 0aH, 00H
	call	_printf
	add	esp, 8
; Line 11
	xor	eax, eax
; Line 12
	pop	ebp
	ret	0
_main	ENDP

Итак, здесь видно: в функции _main заталкиваются три числа в стек и вызывается функиця f(int,int,int).

Внутри f(), доступ к аргументам, также как и к локальным переменным, происходит через макросы: _a$ = 8, но разница в том, что эти смещения со знаком "плюс", таким образом если прибавить макрос _a$ к указателю на EBP, то адресуется "внешняя" часть стека относительно EBP. Далее все более-менее просто: значение a кладется в EAX. Далее EAX умножается при помощи инструкции IMUL на то что лежит в _b. Далее к регистру EAX прибавляется то что лежит в _c. Значение из EAX никуда не нужно перекладывать, оно уже лежит где надо. Возвращаем управление вызываемой функции, которая возьмет значение из EAX и отправит его в printf().

Скомпилируем то же в GCC 4.4.1 и посмотрим результат в IDA:

                public f
f               proc near               ; CODE XREF: main+20�p
 
arg_0           = dword ptr  8
arg_4           = dword ptr  0Ch
arg_8           = dword ptr  10h
 
                push    ebp
                mov     ebp, esp
                mov     eax, [ebp+arg_0]
                imul    eax, [ebp+arg_4]
                add     eax, [ebp+arg_8]
                pop     ebp
                retn
f               endp
 
                public main
main            proc near               ; DATA XREF: _start+17�o
 
var_10          = dword ptr -10h
var_C           = dword ptr -0Ch
var_8           = dword ptr -8
 
                push    ebp
                mov     ebp, esp
                and     esp, 0FFFFFFF0h
                sub     esp, 10h        ; char *
                mov     [esp+10h+var_8], 3
                mov     [esp+10h+var_C], 2
                mov     [esp+10h+var_10], 1
                call    f
                mov     edx, offset aD  ; "%d\n"
                mov     [esp+10h+var_C], eax
                mov     [esp+10h+var_10], edx
                call    _printf
                mov     eax, 0
                leave
                retn
main            endp

Практически то же самое, если не считать мелких отличий описанных раннее.

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox