Передача параметров через стек
From Wiki.conus.info
Как мы уже успели заметить, вызывающая функция передает аргументы для вызываемой через стек. А как вызываемая функция имеет к ним доступ?
#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
Практически то же самое, если не считать мелких отличий описанных раннее.