PE View를 만드는 과정을 블로그에 업데이트 할 예정이다.
#include <stdio.h>
int main() { int a; scanf("%d", &a); switch(a) { case 0: printf("0000"); break; case 1: printf("1111"); break; case 2: printf("2222"); break; case 3: printf("3333"); break; case 4: printf("4444"); break; case 5: printf("5555"); break; } }
8: switch(a)
000A1039 8B 45 F8 mov eax,dword ptr [a]
000A103C 89 85 30 FF FF FF mov dword ptr [ebp-0D0h],eax
000A1042 83 BD 30 FF FF FF 05 cmp dword ptr [ebp-0D0h],5
000A1049 0F 87 A1 00 00 00 ja $LN1+17h (0A10F0h)
000A104F 8B 8D 30 FF FF FF mov ecx,dword ptr [ebp-0D0h]
000A1055 FF 24 8D 30 11 0A 00 jmp dword ptr (0A1130h)[ecx*4]
9: {
10: case 0:
11: printf("0000");
000A105C 8B F4 mov esi,esp
000A105E 68 54 31 0A 00 push offset string "0000" (0A3154h)
000A1063 FF 15 E0 30 0A 00 call dword ptr [__imp__printf (0A30E0h)]
000A1069 83 C4 04 add esp,4
000A106C 3B F4 cmp esi,esp
000A106E E8 ED 00 00 00 call _RTC_CheckEsp (0A1160h)
12: break;
000A1073 EB 7B jmp $LN1+17h (0A10F0h)
13: case 1:
14: printf("1111");
000A1075 8B F4 mov esi,esp
000A1077 68 4C 31 0A 00 push offset string "1111" (0A314Ch)
000A107C FF 15 E0 30 0A 00 call dword ptr [__imp__printf (0A30E0h)]
000A1082 83 C4 04 add esp,4
000A1085 3B F4 cmp esi,esp
000A1087 E8 D4 00 00 00 call _RTC_CheckEsp (0A1160h)
15: break;
000A108C EB 62 jmp $LN1+17h (0A10F0h)
16: case 2:
17: printf("2222");
000A108E 8B F4 mov esi,esp
000A1090 68 44 31 0A 00 push offset string "2222" (0A3144h)
000A1095 FF 15 E0 30 0A 00 call dword ptr [__imp__printf (0A30E0h)]
000A109B 83 C4 04 add esp,4
000A109E 3B F4 cmp esi,esp
000A10A0 E8 BB 00 00 00 call _RTC_CheckEsp (0A1160h)
18: break;
.........
27: break;
28: }
29:
30:
31: }
000A1039 8B 45 F8 mov eax,dword ptr [a] : 변수 a 값을 eax에 대입한다.
000A103C 89 85 30 FF FF FF mov dword ptr [ebp-0D0h],eax : eax를 ebp-0D0h에 기록한다.
000A1042 83 BD 30 FF FF FF 05 cmp dword ptr [ebp-0D0h],5 : case 범위를 넘기는가 비교
000A1049 0F 87 A1 00 00 00 ja $LN1+17h (0A10F0h) : ja(jump above) 5 보다 크면 switch case를 통과한다.
mov dword ptr [ebp-0D0h], eax : 조건변수 값(eax)을 ebp-0D0h 번지에 기록함을 알 수 있다.
// 여기서는 001AF934~001AF936번지를 쓰고 있음을 알 수 있다.
001AF934 02 00 add al,byte ptr [eax]
001AF936 00 00 add byte ptr [eax],al
000A1055 FF 24 8D 30 11 0A 00 jmp : dword ptr (0A1130h)[ecx*4] 명령을 통해서
000A1130 + 8h 에 있는 값으로 jmp를 한다.( dword ptr (0A1130h)[ecx*4] )
000A1138h에는 000A108E 주소값이 들어 있으며, 이 주소는 case 2의 시작하는 주소와 같다.
<case 2>
000A1138 8E db 8eh
000A1139 10 db 10h
000A113A 0A db 0ah
000A113B 00 db 00h
<switch table>
000A1130 5C db 5ch
000A1131 10 db 10h
000A1132 0A db 0ah
000A1133 00 db 00h
000A1134 75 db 75h
000A1135 10 db 10h
000A1136 0A db 0ah
000A1137 00 db 00h
000A1138 8E db 8eh
000A1139 10 db 10h
000A113A 0A db 0ah
000A113B 00 db 00h
000A113C A7 db a7h
000A113D 10 db 10h
000A113E 0A db 0ah
000A113F 00 db 00h
000A1140 C0 db c0h
000A1141 10 db 10h
000A1142 0A db 0ah
000A1143 00 db 00h
000A1144 D9 db d9h
000A1145 10 db 10h
000A1146 0A db 0ah
000A1147 00 db 00h
16: case 2:
17: printf("2222");
000A108E 8B F4 mov esi,esp
000A1090 68 44 31 0A 00 push offset string "2222" (0A3144h)
000A1095 FF 15 E0 30 0A 00 call dword ptr [__imp__printf (0A30E0h)]
000A109B 83 C4 04 add esp,4
000A109E 3B F4 cmp esi,esp
'C++' 카테고리의 다른 글
[C++0x] Move Constructor (0) | 2012.11.15 |
---|---|
[C++] C++ name decoration 혹은 name mangling (0) | 2012.11.13 |
[C++] 디폴트 생성자, 복사 생성자, 소멸자는 존재하는가? (0) | 2012.11.07 |
[C++] 어셈으로 보는 함수 호출(1) (0) | 2012.11.06 |
[C++] 헤더는 선언이지 정의가 아니다. (0) | 2012.10.10 |
C++ 관련 책을 보면 아래와 같은 코드에서 C++ 컴파일러가 클래스 내에서 생성자가 정의되어 있지 않을 경우, 기본 생성자(default constructor)를 삽입한다고 알려져 있다.
하지만 실제로는 컴파일러 구현체에 따라서 달라지겠지만, 내가 현재 주로 쓰고 있는 VC++에서는 그러한 뻘짓(?)을 하지 않는다.
class Ref { public: int a; int b; }; int main() { Ref a; a.a = 12; a.b = 13; Ref b = a; }
실제 어셈블리로 변환된 코드는 아래와 같다.
Ref a;
a.a = 12;
002A101E mov dword ptr [a],0Ch
a.b = 13;
002A1025 mov dword ptr [ebp-8],0Dh
Ref b = a;
002A102C mov eax,dword ptr [a]
002A102F mov dword ptr [b],eax
002A1032 mov ecx,dword ptr [ebp-8]
002A1035 mov dword ptr [ebp-18h],ecx
놀랍지 않은가? 보시다시피 기본 생성자를 부를꺼라고 예상했지만, 실제로는 그러한 코드는 없다.
또한 디폴트 복사 생성자도 존재하지 않음을 알 수 있다.
만약 생성자가 존재하는 상태에서 기본 복사 생성자도 존재할까?
아래의 코드를 디스어셈블 해보았다.
class Ref { public: int a; int b; Ref(int a, int b) { cout << "1 "; } }; int main() { Ref a(1, 2); Ref b = a; }
그 결과 아래와 같은 코드로 변환이 되었음을 알 수 있었다.
Ref a(1, 2);
00EC101E push 2
00EC1020 push 1
00EC1022 lea ecx,[a]
00EC1025 call Ref::Ref (0EC1090h) // 생성자 호출
Ref b = a;
00EC102A mov eax,dword ptr [a]
00EC102D mov dword ptr [b],eax
00EC1030 mov ecx,dword ptr [ebp-8]
00EC1033 mov dword ptr [ebp-18h],ecx
즉 사용자가 정의한 생성자는 호출되었지만, 복사 생성자는 역시 존재하지 않음을 알 수 있다.
객체의 동적 할당인 경우는 어떻게 될까?
아래의 코드를 보자
class Ref { public: // Ref(){ cout << "constructor call" << endl; } // ~Ref() { cout << "destructor call" << endl; } }; int main() { Ref* p = new Ref; delete p; }
Ref* p = new Ref;
00EA101E push 1
00EA1020 call operator new (0EA106Ch)
00EA1025 add esp,4
00EA1028 mov dword ptr [ebp-0E0h],eax
00EA102E mov eax,dword ptr [ebp-0E0h]
00EA1034 mov dword ptr [p],eax
delete p;
00EA1037 mov eax,dword ptr [p]
00EA103A mov dword ptr [ebp-0D4h],eax
00EA1040 mov ecx,dword ptr [ebp-0D4h]
00EA1046 push ecx
00EA1047 call operator delete (0EA1066h)
00EA104C add esp,4
즉 여전히 기본 생성자는 찾아 볼 수가 없다. 메모리 할당, 해제와 관련된 함수 호출이 있을 뿐이다. 여기서 재미있는 것은 소멸자 또한 없다는 사실이다.
비교를 하기 위해서 아래는 명시적으로 생성자를 지정한 경우의 동적할당 어셈코드이다.
class Ref { public: Ref(){ cout << "constructor call" << endl; } ~Ref() { cout << "destructor call" << endl; } }; int main() { Ref* p = new Ref; delete p; }
Ref* p = new Ref;
0080103D push 1
0080103F call operator new (801B7Ah)
00801044 add esp,4
00801047 mov dword ptr [ebp-0F8h],eax
0080104D mov dword ptr [ebp-4],0
00801054 cmp dword ptr [ebp-0F8h],0
0080105B je main+70h (801070h)
0080105D mov ecx,dword ptr [ebp-0F8h]
00801063 call Ref::Ref (801100h)
00801068 mov dword ptr [ebp-10Ch],eax
0080106E jmp main+7Ah (80107Ah)
00801070 mov dword ptr [ebp-10Ch],0
0080107A mov eax,dword ptr [ebp-10Ch]
00801080 mov dword ptr [ebp-104h],eax
00801086 mov dword ptr [ebp-4],0FFFFFFFFh
0080108D mov ecx,dword ptr [ebp-104h]
00801093 mov dword ptr [ebp-14h],ecx
delete p;
00801096 mov eax,dword ptr [ebp-14h]
00801099 mov dword ptr [ebp-0E0h],eax
0080109F mov ecx,dword ptr [ebp-0E0h]
008010A5 mov dword ptr [ebp-0ECh],ecx
008010AB cmp dword ptr [ebp-0ECh],0
008010B2 je main+0C9h (8010C9h)
008010B4 push 1
008010B6 mov ecx,dword ptr [ebp-0ECh]
008010BC call Ref::`scalar deleting destructor' (801170h)
008010C1 mov dword ptr [ebp-10Ch],eax
008010C7 jmp main+0D3h (8010D3h)
008010C9 mov dword ptr [ebp-10Ch],0
'C++' 카테고리의 다른 글
[C++] C++ name decoration 혹은 name mangling (0) | 2012.11.13 |
---|---|
[C/C++] switch의 jump table (0) | 2012.11.07 |
[C++] 어셈으로 보는 함수 호출(1) (0) | 2012.11.06 |
[C++] 헤더는 선언이지 정의가 아니다. (0) | 2012.10.10 |
[C++] const 객체는 기본적으로 파일에 지역적이다. (0) | 2012.10.10 |