380 likes | 572 Views
Лекция № 4. Процедуры. План. Модификатор static О программировании: Точки следования Типизация Контексты Стековые фреймы Соглашения вызова. static. Помечаем функции и переменные Инициализируются один раз, находятся в сегменте данных Доступ к ним только из текущего файла
E N D
Лекция № 4 Процедуры
План • Модификатор static • О программировании: • Точки следования • Типизация • Контексты • Стековые фреймы • Соглашения вызова
static • Помечаем функции и переменные • Инициализируются один раз, находятся в сегменте данных • Доступ к ним только из текущего файла • Выпадают из таблицы экспорта (следствие). • В C все нестатические функции находятся в таблицах экспорта (а те, для которых указываются сигнатуры без тела, включаются в таблицу импорта текущего модуля).
static • Как у нас сохраняется значение: void func () { static int counter = 0; counter++; printf("%d\n", counter); } int main (int argc, char** argv ) { func(); func(); func(); } Выведет: 1 2 3
Точки следования int a = 4; a = ++ a + a; printf(“%d”, a); Что выведется на экран?
Еще примеры • i = v[i++]; • void f(int, int);int g(){puts(“g”);}int h() {puts(“h”);}f(g(), h()) {} Выведет g h или h g ?
Точки следования Где абстрактный вычислитель в том же состоянии, что и конкретный. • Все побочные эффекты предыдущих выражений применены. • Все побочные эффекты следующих выражений еще не наступили.
Где они? Стандарт 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).
Где они? Стандарт C: • В конце вычисления полного выражения (которое не является ничьим подвыражением) • После вычисления первого из двух операндов для ||, &&, запятой и для тернарного ?: • После вычисления аргументов функции (непосредственно перед её вызовом)
Типизация • Строгая и нестрогая • Статическая и динамическая • Например, в C статическая и нестрогая.
Контексты • Статический: что нам точно доступно в данном месте программы исходя из её структуры. • Динамический: что нам доступно исходя из того, что «программа так выполняется»
int some_global = 4; func2() {} func3() { int int_3 func1(); } void func1() { int int_1; //доступ только к int_1 и global func2(); func3(); } void main ( int argc , char** argv ) { int int_main; //отсюда есть доступ к global и int_main func1(); }
Stack frame Состояние стека. funcAвызывает funcB, которая вызывает funcC
Вызов процедуры • High level way (stack frame, механизм стекового кадра) • Возвращаемое значение обычно в EAX
Stack frame • Поместить аргументы в стек (push) • Вызвать функцию с помощью команды call (в стеке сохранится адрес возврата) • Сохранить предыдущее значение EBP в стеке (начало предыдущего стекового фрейма) • ESP->EBP; EBP теперь обозначает «начало стекового кадра» и используется для адресации аргументов и локальных переменных. • Выделяем память в стеке для локальных переменных (уменьшить ESP на их суммарный размер)
Stack frame Рост стека void myproc(int argument1) { int a, b; /*код*/ } Вызываем функцию: myproc(42); • push argument1 (42) • call myproc • push ebp • mov ebp, esp • sub esp, 8 ESP 8 байт под локальные переменные Начало предыдущего кадра EBP Адрес возврата argument1 = 42
Calling conventions • Где аргументы, где возвращаемое значение? • Порядок передачи параметров. • Кто возвращает указатель стека на исходную позицию • Какой командой вызывать подпрограмму и какой — возвращаться в основную программу. • Какие регистры восстанавливает
Calling conventions • Где аргументы, где возвращаемое значение? • Стек • Регистры • Общая память (глобальные переменные)
Calling conventions • Порядок передачи параметров. • Прямой • Обратный
Calling conventions • Кто возвращает указатель стека на исходную позицию • Сама процедура • Вызывающая программа
Calling conventions • Какой командой вызывать подпрограмму и какой — возвращаться в основную программу. • call near - retn == ret • call far - retf • pushf + call far - iret
Calling conventions • Соглашения вызова Используются следующие: • C • Pascal • stdcall • fastcall Другие: • safecall • thiscall
Calling convention: C • Параметры помещаются в стек справа налево • Значение SP восстанавливается после выхода из процедуры
Calling convention: Pascal • Параметры помещаются в стек слева направо • Значение SP восстанавливается в самой процедуре с помощью аргумента ret
Calling convention: stdcall • Параметры помещаются в стек справа налево (как в С) • Значение SP восстанавливается в самой процедуре с помощью аргумента ret (как в Pascal) • Почти всегда используется для Windows API
Calling conventions • Используются модификаторы • _stdcall • _cdecl • _pascal • _fastcall(параметры передаются через регистры) • Например int _cdecl main(int argc, char** argv)
Простая программа int _cdecl main(int argc, char** argv) { int result = 0; char* str = "stringy!"; result++; return 0; }
TITLE f:\??????\?????????\Visual Studio 2008\Projects\simple\simple\main.c .686P include listing.inc .model flat INCLUDELIB OLDNAMES $SG-5 DB 'stringy!', 00H 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 8b ec mov ebp, esp 00003 83 ec 08 sub esp, 8
; 2 : int result = 0; 00006 c7 45 fc 00 00 00 00 mov DWORD PTR _result$[ebp], 0 ; 3 : char* str = "stringy!"; 0000d c7 45 f8 00 00 00 00 mov DWORD PTR _str$[ebp], OFFSET $SG-5 ; 4 : result++; 00014 8b 45 fc mov eax, DWORD PTR _result$[ebp] 00017 83 c0 01 add eax, 1 0001a 89 45 fc mov DWORD PTR _result$[ebp], eax ; 5 : return 0; 0001d 33 c0 xor eax, eax ; 6 : } 0001f 8b e5 mov esp, ebp 00021 5d pop ebp 00022 c3 ret 0 _main ENDP _TEXT ENDS END
TITLE f:\??????\?????????\Visual Studio 2008\Projects\simple\simple\main.c .686P include listing.inc .model flat INCLUDELIB OLDNAMES $SG-5 DB 'stringy!', 00H 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 8b ec mov ebp, esp 00003 83 ec 08 sub esp, 8;sizeof(int) + sizeof(char*) = 4 + 4 = 8 ; 2 : int result = 0; 00006 c7 45 fc 00 00 00 00 mov DWORD PTR _result$[ebp], 0 ; 3 : char* str = "stringy!"; 0000d c7 45 f8 00 00 00 00 mov DWORD PTR _str$[ebp], OFFSET $SG-5 ; 4 : result++; 00014 8b 45 fc mov eax, DWORD PTR _result$[ebp] 00017 83 c0 01 add eax, 1 0001a 89 45 fc mov DWORD PTR _result$[ebp], eax ; 5 : return 0; 0001d 33 c0 xor eax, eax ; 6 : } 0001f 8b e5 mov esp, ebp 00021 5d pop ebp 00022 c3 ret 0 _main ENDP _TEXT ENDS// затем END
Программа с 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; }
INCLUDELIB OLDNAMES EXTRN _printf:PROC $SG-5 DB 'stringy!', 00H ORG $+3;Директива ORG – ассемблирование начнётся по новому адресу. $ - текущий адрес $SG-6 DB '%d', 00H
Программа с 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; }
_TEXT SEGMENT _size$ = -4 ; size = 4 _param_str$ = 8 ; size = 4 _foosize PROC ; COMDAT ; 3 : int _cdecl foosize (char* param_str) { 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 51 push ecx ; 4 : register int size = 0; 00004 c7 45 fc 00 00 00 00 mov DWORD PTR _size$[ebp], 0 $LN2@foosize: ; 5 : while(param_str[size++]); /* код для while*/ ; 6 : return size; 00023 8b 45 fc mov eax, DWORD PTR _size$[ebp] ; 7 : } 00026 8b e5 mov esp, ebp 00028 5d pop ebp 00029 c3 ret 0 _foosize ENDP
Программа с 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; }
из функции main ; 13 : result = foosize(str); 00014 8b 45 f8 mov eax, DWORD PTR _str$[ebp] 00017 50 push eax 00018 e8 00 00 00 00 call _foosize 0001d 83 c4 04 add esp, 4 00020 89 45 fc mov DWORD PTR _result$[ebp], eax ; 14 : printf("%d",result); 00023 8b 4d fc mov ecx, DWORD PTR _result$[ebp] 00026 51 push ecx 00027 68 00 00 00 00 push OFFSET $SG-6 0002c e8 00 00 00 00 call _printf 00031 83 c4 08 add esp, 8 ; 15 : return 0; 00034 33 c0 xor eax, eax