OLD_posting

Calling Convention

슈개 2010. 11. 15. 22:35
반응형

▣ Calling Convention ?

함수 호출 방식이란
호출자(caller, 피호출자 함수를 호출하는 함수)
피호출자(callee, 호출자로부터 호출되는 함수) 간에 미리 정해둔
파라미터의 전달 순서와 사용이 끝난 후의 스택 정리에 대한 규약
WIN32 환경에서는 기본적으로 세 가지의 호출 방식이 존재한다.
  • Standard Call -- 파라미터들은 코드 상의 오른쪽 파라미터부터 먼저 푸쉬되고(첫번째 파라미터가 스택의 맨 위로 오게 된다는 말이다.), 피호출자(callee)가 스택을 정리한다.
  • CDECL or C Calling Convention -- 파라미터들은 코드 상의 오른쪽 파라미터부터 먼저 푸쉬되고(첫번째 파라미터가 스택의 맨 위로 오게 된다는 말이다.), 호출자(caller)가 스택을 정리한다.
  • Fast Call -- C++의 내부 구조에 익숙하다면, 멤버 함수를 호출하기 위해서는 this 포인터가 반드시 필요하다는 것을 알 것이다. 일반적으로 이 this 포인터가 스택에 최초로 푸쉬되는 파라미터다. 하지만 이 방식에서는 this 포인터를 스택에다가 푸쉬하는 게 아니라, 레지스터(ECX)에 저장한다. 파라미터들은 코드 상의 오른쪽 파라미터부터 먼저 푸쉬되고(첫번째 파라미터가 스택의 맨 위로 오게 된다는 말이다.), 피호출자(callee)가 스택을 정리한다.
  • Pascal Calling Convention -- 이 방식은 더 이상 사용할 수 없다. 이는 모두 Standard Call로 대체되었다. 어쨌든 원래의 Pascal Calling Convention은 파라미터를 코드 상의 왼쪽 것부터 먼저 푸쉬하고, 피호출자(callee)가 스택을 정리한다.
  •  

    Caller Clearing 호출자가 정리

    MyFunction, 1, 2, 3, 4

    PUSH    4  
    PUSH    3  
    PUSH    2  
    PUSH    1  
    CALL    MyFunction
    ADD     sp, 16      ; 스택 정리 코드  

    - 장점 : 호출하는 측에서 스택에다 몇 개의 파라미터를 집어넣었는지 알고, 스택 정리를 담당하므로, 가변 인자(variable arguments)를 사용할 수 있다.

    - 단점 : 프로그램의 크기가 커진다. 호출자가 스택을 정리한다는 말은, 위에서도 보듯이 함수 호출할 때마다 스택 정리하는 코드가 들어가야 한다는 말이고, 이는 프로그램의 크기 증가와 속도 감소를 가져온다.

    - printf 함수 같은 것이 가변 인자를 사용하는 대표적인 예이다. 하지만 피호출자는 인자가 몇개나 전달되었는지 정확히 알 수 없다. 그냥 포맷 문자열 같은 것을 이용해서 추측할 뿐이다. 예를 들어 printf 함수를 호출할 때, "%i %i %i" 문자열을 주면, printf 함수는 호출하는 측에서 추가적으로 스택에다 3개의 인자를 집어넣지 않은 경우(프로그래머의 실수!)에도, 스택에 있는 3개의 값을 이용해 문자열을 생성할 것이다. 이것이 크래시를 일으킬지, 아닐지는 정확히 알 수 없다. 3개 이상의 인수를 전달하는 경우는 어차피 호출하는 측에서 스택을 정리하므로 문제가 되지 않는다.

    Callee Clearing 피호출자가 정리

    MyFunction, 1, 2, 3, 4 

    PUSH    4  
    PUSH    3  
    PUSH    2  
    PUSH    1  
    CALL    MyFunction

    - 장점 : 피호출자가 스택 정리를 담당한다면, 프로그램의 크기는 작아진다. 또한 스택 정리 명령어를 매번 호출하지 않아도 되므로, 속도 또한 빨라진다.

    - 단점 : 피호출자가 자신이 정리할 스택의 크기(파라미터들의 총 크기)를 정확히 알고 있어야 하므로, 가변 인자를 사용할 수 없다.

    - 호출자가 하든, 피호출자가 하든 어딘가에서는 스택을 정리해야하지 않는가? 즉 위에서 없어진 ADD sp, 16는 어차피 피호출자, 즉 MyFunction 내부에서 호출해야 하지 않느냐는 말이다. 어차피 어디에선가 호출해야 한다면 프로그램의 크기가 왜 작아지며, 빨라지는가? 이는 인텔 계열에서 지원하는 특수한 명령어 때문이다. 즉 리턴하면서 스택 정리를 동시에 하는 명령어가 있기 때문에, 프로그램의 크기가 작아지고, 빨라진다는 말이다.


    Calling Conventions

       매개변수    전달방향

    Stack Clearing Name Decorations Notes
    __cdecl Right → Left  Callee 피호출자 함수 이름 앞에 _를 붙인다. Ex) _foo C/C++함수의 기본 호출 규약
    __stdcall Right → Left  Caller 호출자 _가앞에 붙고 뒤에 @와 인자의 크기가 10진수로 붙는다. Ex) _foo@12 대부분의 System 함수가 사용. VB에서 내부함수가 사용.
    __fastcall 첫번째 2개의 DWORD 파라미터는 ECX, EDX 레지스터 사용. 나머지는 Right→Left  Callee 피호출자 @이 앞에 붙고 @과 인자의 크기가 10진수로 뒤에 붙는다. Ex) @foo@12 Intel CPU 만 사용. Borland의 Delphi 컴파일러가 사용.
    this Right → Left this 매개변수는 ECX 레지스터사용. 호출자가 Stack에서 인자를 제거한다. None C++클래스의 멤버 함수가 사용. COM에서 사용.
    naked Right → Left 호출자가 Stack에서 인자를 제거한다. None VxD에서 사용. Custom Prolog 와 Epilog를 만들때 사용.



    출처 : http://serious-code.net/moin.cgi/CallingConvention
    참고 : http://kkamagui.springnote.com/pages/339546

    반응형

    'OLD_posting' 카테고리의 다른 글

    PE (Portable Executable) 포맷  (0) 2010.11.24
    Caller/Callee Seaved Register  (0) 2010.11.15
    ALU (arithmetic-logic unit) - 산술논리 연산장치  (0) 2010.11.15
    끄적끄적..  (0) 2010.01.09
    Big & Little Endian  (0) 2010.01.09