일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- getopts
- TiDB
- go
- 커널
- Windows via c/c++
- DBMS
- TiKV
- kernel
- SQLite
- newSQL
- Preprocessor
- DBMS 개발
- 포인터변수
- 긴옵션
- 함수포인터
- 전처리기
- 약어
- Pointer
- 한빛미디어
- 컴퓨터 강좌
- Symbol
- Programming
- bash
- UNIX Internals
- 구조와 원리
- FreeBSD
- 포인터
- Golang
- UNIX
- OS 커널
- Today
- Total
sonumb
Calling Convention 본문
작성자 : sonumb (http://sonumb.tistory.com)
E-mail:
작성일 : 2008년 2월 19일
Contents
0. 서론 [contents]
목적: 함수 호출 시 메모리 변화(구성되는 Stack Frame)와 메모리 해제에 대한 설명.
용어 :
Stack Frame : 함수 호출시 구성되는 스택의 형태 , 보통 실인수(argument value), 리턴 주소, 지역변수로 구성됩니다.
1. 컴파일러 세팅 [contents]
Release 모드로 맞춰놓고 C/C++의 Optimization 기능을 Disabled.
C/C++의 Output Files의 Assembler output을 Assembly With Source Code (/FAs)로 맞춤.
2. 소스파일 [contents]
1: #include <stdio.h>
2: void __cdecl f1( int a, int b );
3: void __stdcall f2( int a, int b );
4: void __fastcall f3( int a, int b );
5: /*
6: * __declspec(naked) f4( void ); // Proto type Declaration 과 Definition 부의 분리 불가!
7: * // error C2488: 'f4' : 'naked' can only be applied to non-member function definitions
8: */
9: __declspec(naked) /*int */ f4( int c, int d )
10: {
11: int a; //naked 함수내에서 지역변수의 초기화는 당연히 안된다! 이유는 17번째줄코드
12: int b;
13:
14: __asm{
15: push ebp
16: mov ebp, esp
17: sub esp, __LOCAL_SIZE ; 실질적인 로컬변수 공간할당
18: ; '__LOCAL_SIZE'는 'naked' 특성이 있는 함수에만 사용가능.
19: ; 모든 로컬 변수와 컴파일러가 생성한 임시 변수들의 총 바이트
20: }
21: a=2; // 실질적인 초기화
22: b=3;
23: __asm{
24: mov a, __LOCAL_SIZE
25: mov eax, a ; 'return value' 저장.
26: mov esp, ebp
27: pop ebp
28: ret
29: }
30: }
31: int main( void )
32: {
33: volatile int a=3, b=2;
34: f1(a,b);
35: f2(a,b);
36: f3(a,b);
37: a = f4(a,b);
38:
39:
40: printf("main - a : %d\n" , a);
41:
42: return a;
43: }
44: void __cdecl f1( int a, int b )
45: {
46: volatile int c = 0, d=0 , e=0;
47: c = a + b;
48: }
49: void __stdcall f2( int a, int b )
50: {
51: volatile int c = 0;
52: c = a +b;
53: }
54: void __fastcall f3( int a, int b )
55: {
56: volatile int c = 0;
57: a = a + b;
58: }
일단 naked 외에 3가지 함수는 별 차이점 없어 보입니다..
naked
naked를 일단 설명하고자 합니다.
1
naked의 뜻은 `벗겨진`, `발가벗은`이죠..일본 AV를 먼저 떠올리지 마시고 -_-;;;
특징:
calling convention을 프로그래머가 직접 만든다.
인자의 전달 및 인자 해제는 caller가 책임진다.
함수의 프로토타임 `Declaration`, `definition` 분리가 불가. `정의`만 있어야 한다.
return type의 선언해도 되고 안해도 되고,,
자 위의 코드를 봅시다.
9-30 line을 통해 `정의`를 하고 있습니다. 함수리턴타입을 주석으로 처리 해놨는데요. main함수부에서 호출하여 인자로 받고 있는데도, 위의 코드는 아무 탈 없이 잘 돌아갑니다.
또 보시면 내부의 __LOCAL_SIZE란 심벌을 쓰고 있는데요, 컴파일러가 정의한 상수 입니다. 보통 모든 Local Variable 변수의 크기라고 생각하시면 됩니다.
3. 빌드 결과[contents]
1: ; Listing generated by Microsoft (R) Optimizing Compiler Version 14.00.50727.762
2:
3: TITLE e:\src\test_call_convetion\test_call_convetion\test.c
4: .686P
5: .XMM
6: include listing.inc
7: .model flat
8:
9: INCLUDELIB OLDNAMES
10:
11: EXTRN @__security_check_cookie@4:PROC
12: EXTRN __imp__printf:PROC
13: $SG-5 DB 'main - a : %d', 0aH, 00H
14: PUBLIC @f3@8
15: ; Function compile flags: /Odtp
16: ; File e:\src\test_call_convetion\test_call_convetion\test.c
17: _TEXT SEGMENT
18: _b$ = -12 ; size = 4
19: _a$ = -8 ; size = 4
20: _c$ = -4 ; size = 4
21: @f3@8 PROC
22: ; _a$ = ecx
23: ; _b$ = edx
24:
25: ; 55 : {
26:
27: push ebp
28: mov ebp, esp
29: sub esp, 12 ; 0000000cH
30: mov DWORD PTR _b$[ebp], edx
31: mov DWORD PTR _a$[ebp], ecx
32:
33: ; 56 : volatile int c = 0;
34:
35: mov DWORD PTR _c$[ebp], 0
36:
37: ; 57 : a = a + b;
38:
39: mov eax, DWORD PTR _a$[ebp]
40: add eax, DWORD PTR _b$[ebp]
41: mov DWORD PTR _a$[ebp], eax
42:
43: ; 58 : }
44:
45: mov esp, ebp
46: pop ebp
47: ret 0
48: @f3@8 ENDP
49: _TEXT ENDS
50: PUBLIC _f2@8
51: ; Function compile flags: /Odtp
52: _TEXT SEGMENT
53: _c$ = -4 ; size = 4
54: _a$ = 8 ; size = 4
55: _b$ = 12 ; size = 4
56: _f2@8 PROC
57:
58: ; 50 : {
59:
60: push ebp
61: mov ebp, esp
62: push ecx
63:
64: ; 51 : volatile int c = 0;
65:
66: mov DWORD PTR _c$[ebp], 0
67:
68: ; 52 : c = a +b;
69:
70: mov eax, DWORD PTR _a$[ebp]
71: add eax, DWORD PTR _b$[ebp]
72: mov DWORD PTR _c$[ebp], eax
73:
74: ; 53 : }
75:
76: mov esp, ebp
77: pop ebp
78: ret 8
79: _f2@8 ENDP
80: _TEXT ENDS
81: PUBLIC _f1
82: ; Function compile flags: /Odtp
83: _TEXT SEGMENT
84: _e$ = -12 ; size = 4
85: _c$ = -8 ; size = 4
86: _d$ = -4 ; size = 4
87: _a$ = 8 ; size = 4
88: _b$ = 12 ; size = 4
89: _f1 PROC
90:
91: ; 45 : {
92:
93: push ebp
94: mov ebp, esp
95: sub esp, 12 ; 0000000cH
96:
97: ; 46 : volatile int c = 0, d=0 , e=0;
98:
99: mov DWORD PTR _c$[ebp], 0
100: mov DWORD PTR _d$[ebp], 0
101: mov DWORD PTR _e$[ebp], 0
102:
103: ; 47 : c = a + b;
104:
105: mov eax, DWORD PTR _a$[ebp]
106: add eax, DWORD PTR _b$[ebp]
107: mov DWORD PTR _c$[ebp], eax
108:
109: ; 48 : }
110:
111: mov esp, ebp
112: pop ebp
113: ret 0
114: _f1 ENDP
115: _TEXT ENDS
116: PUBLIC _f4
117: ; Function compile flags: /Odtp
118: _TEXT SEGMENT
119: _b$ = -8 ; size = 4
120: _a$ = -4 ; size = 4
121: _c$ = 8 ; size = 4
122: _d$ = 12 ; size = 4
123: _f4 PROC
124:
125: ; 11 : int a; //naked 함수내에서 지역변수의 초기화는 당연히 안된다!
126: ; 12 : int b;
127: ; 13 :
128: ; 14 : __asm{
129: ; 15 : push ebp
130:
131: push ebp
132:
133: ; 16 : mov ebp, esp
134:
135: mov ebp, esp
136:
137: ; 17 : sub esp, __LOCAL_SIZE ; 실질적인 로컬변수 공간할당
138:
139: sub esp, 8
140:
141: ; 18 : ; '__LOCAL_SIZE'는 'naked' 특성이 있는 함수에만 사용가능.
142: ; 19 : ; 모든 로컬 변수와 컴파일러가 생성한 임시 변수들의 총 바이트
143: ; 20 : }
144: ; 21 : a=2; // 실질적인 초기화
145:
146: mov DWORD PTR _a$[ebp], 2
147:
148: ; 22 : b=3;
149:
150: mov DWORD PTR _b$[ebp], 3
151:
152: ; 23 : __asm{
153: ; 24 : mov a, __LOCAL_SIZE
154:
155: mov DWORD PTR _a$[ebp], 8
156:
157: ; 25 : mov eax, a ; 'return value' 저장.
158:
159: mov eax, DWORD PTR _a$[ebp]
160:
161: ; 26 : mov esp, ebp
162:
163: mov esp, ebp
164:
165: ; 27 : pop ebp
166:
167: pop ebp
168:
169: ; 28 : ret
170:
171: ret 0
172: _f4 ENDP
173: _TEXT ENDS
174: PUBLIC _main
175: ; Function compile flags: /Odtp
176: _TEXT SEGMENT
177: _b$ = -8 ; size = 4
178: _a$ = -4 ; size = 4
179: _main PROC
180:
181: ; 32 : {
182:
183: push ebp
184: mov ebp, esp
185: sub esp, 8
186:
187: ; 33 : volatile int a=3, b=2;
188:
189: mov DWORD PTR _a$[ebp], 3
190: mov DWORD PTR _b$[ebp], 2
191:
192: ; 34 : f1(a,b);
193:
194: mov eax, DWORD PTR _b$[ebp]
195: push eax
196: mov ecx, DWORD PTR _a$[ebp]
197: push ecx
198: call _f1
199: add esp, 8
200:
201: ; 35 : f2(a,b);
202:
203: mov edx, DWORD PTR _b$[ebp]
204: push edx
205: mov eax, DWORD PTR _a$[ebp]
206: push eax
207: call _f2@8
208:
209: ; 36 : f3(a,b);
210:
211: mov edx, DWORD PTR _b$[ebp]
212: mov ecx, DWORD PTR _a$[ebp]
213: call @f3@8
214:
215: ; 37 : a = f4(a,b);
216:
217: mov ecx, DWORD PTR _b$[ebp]
218: push ecx
219: mov edx, DWORD PTR _a$[ebp]
220: push edx
221: call _f4
222: add esp, 8
223: mov DWORD PTR _a$[ebp], eax
224:
225: ; 38 :
226: ; 39 :
227: ; 40 : printf("main - a : %d\n" , a);
228:
229: mov eax, DWORD PTR _a$[ebp]
230: push eax
231: push OFFSET $SG-5
232: call DWORD PTR __imp__printf
233: add esp, 8
234:
235: ; 41 :
236: ; 42 : return a;
237:
238: mov eax, DWORD PTR _a$[ebp]
239:
240: ; 43 : }
241:
242: mov esp, ebp
243: pop ebp
244: ret 0
245: _main ENDP
246: _TEXT ENDS
247: END
199: add esp, 8
78: ret 8
// add esp, 8// ret// 과 같은 코드 입니다.
4. 결론 [contents]
__cdecl: 일반적인 C함수에서 쓰인다. 호출한쪽(caller)에서 전달한한 인자에 대한 처리(unwired)를 합니다.__stdcall : callback 함수나 handler, Thread에서 쓰인다. 호출 당한쪽(callee)이 전달 받은 인자를 처리합니다.
__fastcall: ecx, edx 를 각각 첫번째 인자, 두번째 인자로 쓴다. 그러면 전달 받은 인자 처리에 대해 전혀 신경쓰지 않아도 됩니다.
naked: 보통 __declspec(naked) 이렇게 씁니다. 함수 스택프레임의 구성과 해제를 프로그래머가 직접해주는 규약