1 Лекция № 4 Процедуры
2 План • Модификатор static • О программировании: ▫ Точки следования ▫ Типизация ▫ Контексты • Стековые фреймы • Соглашения вызова
3 static • Помечаем функции и переменные • Инициализируются один раз, находятся в сегменте данных • Доступ к ним только из текущего файла • Выпадают из таблицы экспорта (следствие). • В C все нестатические функции находятся в таблицах экспорта (а те, для которых указываются сигнатуры без тела, включаются в таблицу импорта текущего модуля).
4 static • Как у нас сохраняется значение: void func () { static int counter = 0; counter++; printf("%dn", counter); } int main (int argc, char** argv ) { func(); } Выведет: 1 2 3
5 Точки следования int a = 4; a = ++ a + a; printf(“%d”, a); Что выведется на экран?
6 Еще примеры • i = v[i++]; • void f(int, int); int g() {puts(“g”); } int h() {puts(“h”); } f(g(), h()) {} Выведет g h или h g ?
7 Точки следования Где абстрактный вычислитель в том же состоянии, что и конкретный. - Все побочные эффекты предыдущих выражений применены. - Все побочные эффекты следующих выражений еще не наступили.
8 Где они? Стандарт C: • at the end of the evaluation of a full expression (a full expression is an expression statement, or any other expression which is not a subexpression within any larger expression); • at the ||, &&, ? : , and comma operators; • at a function call (after the evaluation of all the arguments, and just before the actual call).
9 Где они? Стандарт C: • В конце вычисления полного выражения (которое не является ничьим подвыражением) • После вычисления первого из двух операндов для ||, &&, запятой и для тернарного ? : • После вычисления аргументов функции (непосредственно перед её вызовом)
10 Типизация • Строгая и нестрогая • Статическая и динамическая • Например, в C статическая и нестрогая.
11 Контексты • Статический: что нам точно доступно в данном месте программы исходя из её структуры. • Динамический: что нам доступно исходя из того, что «программа так выполняется»
12 int some_global = 4; func 2() { } func 3() { int_3 func 1(); } void func 1() { int_1; func 2(); func 3(); } //доступ только к int_1 и global void main ( int argc , char** argv ) { int_main; //отсюда есть доступ к global и int_main func 1(); }
13 Stack frame Состояние стека. func. A вызывает func. B, которая вызывает func. C
14 Вызов процедуры • High level way (stack frame, механизм стекового кадра) • Возвращаемое значение обычно в EAX
15 Stack frame 1. Поместить аргументы в стек (push) 2. Вызвать функцию с помощью команды call (в стеке сохранится адрес возврата) 3. Сохранить предыдущее значение EBP в стеке (начало предыдущего стекового фрейма) 4. ESP->EBP; EBP теперь обозначает «начало стекового кадра» и используется для адресации аргументов и локальных переменных. 5. Выделяем память в стеке для локальных переменных (уменьшить ESP на их суммарный размер)
16 Stack frame void myproc(int argument 1) { int a, b; /*код*/ } Вызываем функцию: myproc(42); 1. push argument 1 (42) 2. call myproc 3. push ebp 4. mov ebp, esp 5. sub esp, 8 Рост стека ESP 8 байт под локальные переменные EBP Начало предыдущего кадра Адрес возврата argument 1 = 42
17 Calling conventions • Где аргументы, где возвращаемое значение? • Порядок передачи параметров. • Кто возвращает указатель стека на исходную позицию • Какой командой вызывать подпрограмму и какой — возвращаться в основную программу. • Какие регистры восстанавливает
18 Calling conventions • Где аргументы, где возвращаемое значение? ▫ Стек ▫ Регистры ▫ Общая память (глобальные переменные)
19 Calling conventions • Порядок передачи параметров. ▫ Прямой ▫ Обратный
20 Calling conventions • Кто возвращает указатель стека на исходную позицию ▫ Сама процедура ▫ Вызывающая программа
21 Calling conventions • Какой командой вызывать подпрограмму и какой — возвращаться в основную программу. ▫ call near - retn == ret ▫ call far - retf ▫ pushf + call far - iret
22 Calling conventions • Соглашения вызова Используются следующие: ▫C ▫ Pascal ▫ stdcall ▫ fastcall Другие: ▫ safecall ▫ thiscall
23 Calling convention: C • Параметры помещаются в стек справа налево • Значение SP восстанавливается после выхода из процедуры
24 Calling convention: Pascal • Параметры помещаются в стек слева направо • Значение SP восстанавливается в самой процедуре с помощью аргумента ret
25 Calling convention: stdcall • Параметры помещаются в стек справа налево (как в С) • Значение SP восстанавливается в самой процедуре с помощью аргумента ret (как в Pascal) • Почти всегда используется для Windows API
26 Calling conventions • Используются модификаторы ▫ ▫ _stdcall _cdecl _pascal _fastcall (параметры передаются через регистры) • Например int _cdecl main(int argc, char** argv)
27 Простая программа int _cdecl main(int argc, char** argv) { int result = 0; char* str = "stringy!"; result++; return 0; }
28 TITLE f: ? ? ? ? ? Visual Studio 2008Projectssimplemain. c. 686 P include listing. inc. model flat INCLUDELIB OLDNAMES $SG-5 DB 'stringy!', 00 H PUBLIC _main _TEXT SEGMENT _str$ = -8 ; size = 4 _result$ = -4 ; size = 4 _argc$ = 8 ; size = 4 _argv$ = 12 ; size = 4 _main PROC ; COMDAT ; 1 : int _cdecl main(int argc, char** argv) { 00000 55 push ebp 00001 8 b ec mov ebp, esp 00003 83 ec 08 sub esp, 8
29 ; 2 : int result = 0; 00006 c 7 45 fc 00 00 mov DWORD PTR _result$[ebp], 0 ; 3 : char* str = "stringy!"; 0000 d c 7 45 f 8 00 00 mov DWORD PTR _str$[ebp], OFFSET $SG-5 ; 4 : result++; 00014 8 b 45 fc mov eax, DWORD PTR _result$[ebp] 00017 83 c 0 01 add eax, 1 0001 a 89 45 fc mov DWORD PTR _result$[ebp], eax ; 5 : return 0; 0001 d 33 c 0 xor eax, eax ; 6 : } 0001 f 8 b e 5 mov esp, ebp 00021 5 d pop ebp 00022 c 3 ret 0 _main ENDP _TEXT ENDS END
30 TITLE f: ? ? ? ? ? Visual Studio 2008Projectssimplemain. c. 686 P include listing. inc. model flat INCLUDELIB OLDNAMES $SG-5 DB 'stringy!', 00 H PUBLIC _main _TEXT SEGMENT _str$ = -8 ; size = 4 _result$ = -4 ; size = 4 _argc$ = 8 ; size = 4 _argv$ = 12 ; size = 4 _main PROC ; COMDAT
31 ; 1 : int _cdecl main(int argc, char** argv) { 00000 55 push ebp 00001 8 b ec mov ebp, esp 00003 83 ec 08 sub esp, 8 ; sizeof(int) + sizeof(char*) = 4 + 4 = 8 ; 2 : int result = 0; 00006 c 7 45 fc 00 00 mov DWORD PTR _result$[ebp], 0 ; 3 : char* str = "stringy!"; 0000 d c 7 45 f 8 00 00 mov DWORD PTR _str$[ebp], OFFSET $SG-5 ; 4 : result++; 00014 8 b 45 fc mov eax, DWORD PTR _result$[ebp] 00017 83 c 0 01 add eax, 1 0001 a 89 45 fc mov DWORD PTR _result$[ebp], eax ; 5 : return 0; 0001 d 33 c 0 xor eax, eax ; 6 : } 0001 f 8 b e 5 mov esp, ebp 00021 5 d pop ebp 00022 c 3 ret 0 _main ENDP _TEXT ENDS // затем END
32 Программа с printf и дополнительной функцией void printf(const char*, . . . ); int _cdecl foosize (char* param_str) { register int size = 0; while(param_str[size++]); return size; } int _cdecl main(int argc, char** argv) { int result = 0; char* str = "stringy!"; result = foosize(str); printf("%d", result); return 0; }
33 INCLUDELIB OLDNAMES EXTRN _printf: PROC $SG-5 DB 'stringy!', 00 H ORG $+3 $SG-6 DB '%d', 00 H ; Директива ORG – ассемблирование начнётся по новому адресу. $ - текущий адрес
34 Программа с printf и дополнительной функцией void printf(const char*, . . . ); int _cdecl foosize (char* param_str) { register int size = 0; while(param_str[size++]); return size; } int _cdecl main(int argc, char** argv) { int result = 0; char* str = "stringy!"; result = foosize(str); printf("%d", result); return 0; }
35 _TEXT SEGMENT _size$ = -4 _param_str$ = 8 _foosize PROC ; size = 4 ; COMDAT ; 3 : int _cdecl foosize (char* param_str) { 00000 55 push ebp 00001 8 b ec mov ebp, esp 00003 51 push ecx ; 4 : register int size = 0; 00004 c 7 45 fc 00 00 mov DWORD PTR _size$[ebp], 0 $LN 2@foosize: ; 5 : while(param_str[size++]); /* код для while*/ ; 6 : return size; 00023 8 b 45 fc mov eax, DWORD PTR _size$[ebp] ; 7 : } 00026 8 b e 5 mov esp, ebp 00028 5 d pop ebp 00029 c 3 ret 0 _foosize ENDP
36 Программа с printf и дополнительной функцией void printf(const char*, . . . ); int _cdecl foosize (char* param_str) { register int size = 0; while(param_str[size++]); return size; } int _cdecl main(int argc, char** argv) { int result = 0; char* str = "stringy!"; result = foosize(str); printf("%d", result); return 0; }
37 из функции main ; 13 : result = foosize(str); 00014 00017 00018 0001 d 00020 8 b 45 f 8 50 e 8 00 00 83 c 4 04 89 45 fc ; 14 : printf("%d", result); 00023 00026 00027 0002 c 00031 8 b 4 d fc 51 68 00 00 e 8 00 00 83 c 4 08 ; 15 : return 0; 00034 33 c 0 mov push call add mov eax, DWORD PTR _str$[ebp] eax _foosize esp, 4 DWORD PTR _result$[ebp], eax mov push call add ecx, DWORD PTR _result$[ebp] ecx OFFSET $SG-6 _printf esp, 8 xor eax, eax


