지역변수 - 해당되는 글 1건
__declspec이라는 키워드는 Microsoft의 C++ 컴파일러가 알아먹는 속성들을 지정해줍니다. __declspec(attribute) 이런 식으로 쓸 수가 있는데, 대표적인 attribute로 dllimport, dllexport가 있습니다.

가끔씩 naked라는 attribute가 유용한 경우가 있습니다. 변수를 특별히 사용하지 않으면서 순수하게 코드만 따로 떼어내고 싶은 경우가 그렇습니다. 또, stack이 정해져 있지 않은 경우도 있을 수 있겠지요.

naked attribute를 사용한 함수와 아닌 함수의 코드가 어떻게 생성되는 지 비교해보겠습니다.

__declspec ( naked ) void NakedFunc(void)
{
    int i;
    __asm { mov eax, offset CodeBegin }
00413010  mov         eax,offset CodeBegin (413020h)
    __asm { mov ebx, offset CodeEnd }
00413015  mov         ebx,offset CodeEnd (41302Ah)
    __asm { mov ecx, __LOCAL_SIZE }
0041301A  mov         ecx,4      
CodeBegin:
    i = 1;
00413020  mov         dword ptr [i],1
    __asm { mov eax, dword ptr [esp] };
00413027  mov         eax,dword ptr [esp]
CodeEnd:
    printing();
0041302A  call        printing (41111Dh)
0041302F  int         3   
}

파란 색이 제가 작성한 코드이고, 까만 부분이 disassembly된 코드입니다. 위의 코드를 보시면 웃기게도 블럭 닫기 기호인 '}'에 해당하는 코드가 없습니다. 리턴하는 코드 조차 없다는 말입니다. 당연히 위와 같이 프로그래밍 했다면, 프로그램은 죽어버릴 것입니다. 리턴하지도 않거니와, 맨 마지막에 'int 3'이라는 명령 때문입니다. interrupt 발생시키는 명령인데, 3번은 디버거를 위한 것입니다.
물론, 당연하게도, 범용 레지스터를 저장한다던가, 이전 함수의 스택 포인터를 저장한다던가 하는 명령어들도 없습니다. 이런 것들을 함수의 prolog와 epilog라고 하는데, __declspec(naked)는 바로 prolog와 epilog를 컴파일러가 만들어주지 않습니다.

참고로, 'int 3' 명령은 모든 함수 밑에 있습니다. 디버깅 모드로 빌드해서 그런 지는 모르겠습니다만, 아무튼, 혹시나 프로그램 제어가 함수 밖으로 뛰쳐나갈 경우를 대비한 것인 듯 합니다.

void UnNakedFunc(void)
{
00413030  push        ebp 
00413031  mov         ebp,esp
00413033  push        ecx 
00413034  push        ebx 
00413035  mov         dword ptr [ebp-4],0CCCCCCCCh
    int i;
    __asm { mov eax, offset CodeBegin }
0041303C  mov         eax,offset CodeBegin (41304Ch)
    __asm { mov ebx, offset CodeEnd }
00413041  mov         ebx,offset CodeEnd (413056h)
    __asm { mov ecx, 0x00 }
00413046  mov         ecx,0      
CodeBegin:
    i = 1;
0041304C  mov         dword ptr [i],1
    __asm { mov eax, dword ptr [esp] };
00413053  mov         eax,dword ptr [esp]
CodeEnd:
    printing();
00413056  call        printing (41111Dh)
}
0041305B  pop         ebx 
0041305C  add         esp,4
0041305F  cmp         ebp,esp
00413061  call        @ILT+320(__RTC_CheckEsp) (411145h)
00413066  mov       esp,ebp
00413068  pop       ebp
00413069  ret

아.. 모양이 많이 다릅니다. 이 소스코드에서 __declspec(naked)와 또 다른 점은 __LOCAL_SIZE가 없다는 것인데, __LOCAL_SIZE는 컴파일러가 알아먹는 심볼로, 컴파일할 때, __declspec(naked)로 정의된 함수의 스택 크기를 집어넣어줍니다. 따라서 naked가 아닌 함수에는 사용할 수 없습니다. __LOCAL_SIZE의 용도는 위의 코드에서 진하게 표시한 부분을 직접 어셈블리로 작성해야 할 때 유용하게 사용하기 위한 것입니다. 함수의 지역 변수의 전체 크기를 사람이 직접 측정하는 것은 오류를 발생시킬 가능성이 크므로, 컴파일러가 대신 하여 넣어주는 것입니다.

아무튼! 일반적인 함수와는 달리, naked 함수는 좀 특별한 용도로 사용될 것 같죠? 보통 함수는 뭔가를 넣으면 안에서 처리해 그 결과를 돌려준다는 개념인데, naked 함수는 단지 코드의 집합 정도로 보는 것이 좋을 것 같습니다.

그런데!!!

놀라운 것은 위의 첫 번째 예제에서 __LOCAL_SIZE가 4가 아닐 수도 있다는 것입니다. 지역 변수가 정수형 하나이기 때문에 4라고 생각했는데, 192 + 12 이었습니다. 지역 변수가 없으면 192, 변수 하나에 8이 더 붙은 것입니다.

컴파일러 옵션으로 /ZI가 주어지면 이렇게 됩니다. 컴파일러 옵션에 'Debug Information Format' 항목에서 'Program Database for Edit & Continue' 옵션을 선택하는 것으로 설정할 수 있습니다. 이렇게 하면, 링킹할 때 /Gy 옵션이 따라 붙게 되는데, 함수를 어찌어찌 한다고 합니다.

설명서를 읽어도 잘 모르겠네요 ㅠㅠ 혹시 아시는 분 가르쳐주세요!

참, /ZI 옵션, __declspec(naked)는 모두 x86 코드에만 해당된다고 합니다.



Trackbacks  0 | Comments 
permalink 해보리
2009.01.12 09:31 댓글에 댓글수정/삭제
좋은 정보 감사합니다. 담아가도 되나요?
링크만 달아 갈께요.




풀리비’s Blog is powered by Daum & Tattertools.com