[추천] 논 호출 약정

C 언어에서는 다음과 같은 함수가 있다고 가정합니다.
 
int function(int a,int b)
 
호출할 때result=function(1,2) 이런 방식으로 이 함수를 사용할 수 있습니다.그러나 고급 언어가 컴퓨터가 식별할 수 있는 기계 코드로 컴파일되었을 때 문제가 하나 드러났다. CPU에서 컴퓨터는 함수 호출에 몇 개, 어떤 파라미터가 필요한지 알 수 없고 하드웨어도 이 파라미터를 저장할 수 없다.즉, 컴퓨터는 이 함수에 어떻게 매개 변수를 전달하는지 모른다. 매개 변수를 전달하는 작업은 반드시 함수 호출자와 함수 자체가 조화를 이루어야 한다.이를 위해 컴퓨터는 인자 전달을 지원하기 위해 창고라고 불리는 데이터 구조를 제공했다.
 
창고는 선진적인 데이터 구조로 창고에 저장 구역과 창고 꼭대기 바늘이 있다.창고 꼭대기 바늘은 창고에서 사용할 수 있는 첫 번째 데이터 항목을 가리킨다.사용자는 창고 위에 있는 방향 창고에 데이터를 추가할 수 있습니다. 이 동작을 창고 압축(Push)이라고 합니다. 창고를 압축한 후에 창고 지붕은 자동으로 데이터 항목이 추가된 위치가 되고 창고 지붕 바늘도 수정됩니다.사용자도 창고에서 창고 꼭대기를 가져갈 수 있는데 팝업 창고(pop)라고 부른다. 창고가 나오면 창고 꼭대기 아래의 요소가 창고 꼭대기로 변하고 창고 꼭대기 바늘이 수정된다.
 
함수 호출을 할 때 호출자는 순서대로 매개 변수를 압축한 다음에 함수를 호출합니다. 함수가 호출된 후에 창고에서 데이터를 얻고 계산합니다.함수 계산이 끝난 후, 또는 호출자, 또는 함수 자체가 창고를 수정하여 창고를 원래의 상태로 복원합니다.
 
매개 변수 전달에서 두 가지 중요한 문제는 반드시 명확하게 설명해야 한다.
 
  • 매개 변수의 개수가 하나보다 많을 때 어떤 순서에 따라 매개 변수를 창고에 눌러넣을까
  • 함수 호출 후 창고를 원상태로 복구하는 사람
  •  
    고급 언어에서는 함수 호출 약정을 통해 이 두 문제를 설명한다.일반적인 호출 약속은 다음과 같습니다.
  • stdcall
  • cdecl
  • fastcall
  • thiscall
  • naked call

  •  

    stdcall 호출 규약


     
    stdcall은 초기에 흔히 볼 수 있는 교수용 컴퓨터 프로그램 설계 언어이기 때문에 문법이 엄격하고 사용하는 함수 호출 약정은 stdcall이다.icrosoft C++ 시리즈의 C/C++ 컴파일러에서는 PASCAL 매크로로 이 호출 약정을 설명하는데, 유사한 매크로는 WINAPI와 CALLBACK도 있다.
     
    stdcall 호출 규약의 구문은 (앞의 함수를 예로 들면):
     
    int __stdcall function(int a,int b)
     
    stdcall의 호출 약속은 다음과 같다. 1) 매개 변수가 오른쪽에서 왼쪽으로 눌러 창고에 들어가고, 2) 함수 자체가 창고를 수정하는 것을 의미한다. 3) 함수 이름은 자동으로 전도된 밑줄을 긋고, 뒤에 @ 기호를 따라가고, 그 다음에 매개 변수의 크기를 따라간다.
     
    상기 이 함수를 예로 들면 매개 변수 b가 먼저 압축되고 그 다음에 매개 변수 a, 함수 호출 function(1,2) 호출처에서 어셈블리 언어로 번역하면 다음과 같다.
    
      
      
      
      

    push 2
    push 1
    call function , cs:eip


     
    함수 자체에 대해서는 다음과 같이 번역할 수 있습니다.
    
      
      
      
      

    push ebp ebp , ,
    mov ebp,esp
    mov eax,[ebp + 8H] ebp ebp,cs:eip,a,b,ebp +8 a
    add eax,[ebp + 0CH] ebp + 12 b
    mov esp,ebp esp
    pop ebp
    ret 8

     
    번역할 때 이 함수의 이름은 _로 번역된다function@8
    서로 다른 컴파일러가 컴파일의 통용성을 제공하기 위해 자신의 어셈블리 코드를 삽입할 수 있음을 주의하십시오. 그러나 대체적인 코드는 이렇습니다.그 중에서 함수 시작 부분에서esp를 ebp에 보존하고 함수 종료 복구는 컴파일러가 자주 사용하는 방법입니다.
     
    함수 호출을 보면 2와 1은 순서대로push에 의해 창고에 들어가고, 함수에서는 ebp(즉 함수에 들어갔을 때의 창고 바늘)에 대한 편이량 접근 매개 변수를 통과한다.함수가 끝난 후,ret8은 8바이트의 창고를 정리하고, 함수는 스스로 창고를 복구했습니다.
     

    cdecl 호출 약정


     
    cdecl 호출 약정은 C 호출 약정이라고도 하는데 C 언어의 부족한 호출 약정으로 그 정의 문법은 다음과 같다.
    
      
      
      
      

    int function (int a ,int b) // C
    int __cdecl function(int a,int b)// C


     
    본문을 쓸 때, 예상을 벗어나, cdecl 호출에 약정된 매개 변수의 창고 압축 순서는 stdcall과 같고, 매개 변수는 먼저 왼쪽으로 창고에 눌러 넣는 것을 발견했다.다른 것은 함수 자체가 창고를 정리하지 않고 호출자가 창고를 정리하는 것을 책임진다는 것이다.이러한 변화로 인해 C 호출 약정 허용 함수의 매개 변수의 개수는 고정되지 않고 이것도 C 언어의 큰 특색이다.앞의 function 함수에 대해 cdecl를 사용한 후 송금 인코딩은 다음과 같습니다.
    
      
      
      
      


    push 1
    push 2
    call function
    add esp,8
    _function
    push ebp ebp , ,
    mov ebp,esp
    mov eax,[ebp + 8H] ebp ebp,cs:eip,a,b,ebp +8 a
    add eax,[ebp + 0CH] ebp + 12 b
    mov esp,ebp esp
    pop ebp
    ret


     
    MSDN에 따르면 이 수식은 자동으로 함수 이름 앞에 선도적인 밑줄을 긋기 때문에 함수 이름은 기호표에 _로 기록된다function, 하지만 나는 컴파일할 때 이런 변화를 보지 못한 것 같다.
     
    매개 변수는 오른쪽에서 왼쪽 순서에 따라 창고를 누르기 때문에 최초의 매개 변수는 창고 꼭대기에 가장 가까운 위치에 있기 때문에 부정확한 개수 매개 변수를 사용할 때 첫 번째 매개 변수가 창고에 있는 위치를 알 수 있다. 부정확한 매개 변수 개수는 첫 번째 후자의 후속적인 명확한 매개 변수에 따라 확정할 수 있다면 부정확한 매개 변수를 사용할 수 있다. 예를 들어 CRT의sprintf 함수에 대해 다음과 같이 정의한다.
    int sprintf(char* buffer,const char* format,...)
     
    모든 부정확한 매개 변수는format을 통해 확정할 수 있기 때문에 부정확한 개수의 매개 변수를 사용하는 것은 문제없다.
     

    fastcall


     
    fastcall 호출 약정은 stdcall과 유사합니다. 이것은 다음과 같습니다.
  • 함수의 첫 번째와 두 번째 DWORD 매개 변수(또는 크기가 더 작은 것)는 ecx와 edx를 통해 전달되고, 다른 매개 변수는 오른쪽에서 왼쪽으로 순서대로 창고를 눌러
  • 호출된 함수로 창고 정리
  • 함수 이름 수정 규칙과 stdcall
  •  
    그 성명 문법은:intfastcallfunction(inta,intb)
     

    thiscall


     
    thiscall은 키워드가 아니기 때문에 명확하게 가리킬 수 없는 유일한 함수 수식이다.이것은 C++ 클래스 구성원 함수의 기본 호출 규칙입니다.구성원 함수 호출에 this 포인터가 하나 더 있기 때문에 특수하게 처리해야 합니다.thiscall은 다음과 같습니다.
  • 파라미터가 오른쪽에서 왼쪽으로 창고에 들어간다
  • 매개 변수의 개수가 확정되면this 바늘은ecx를 통해 피호출자에게 전달된다.매개 변수의 개수가 확실하지 않으면,this 바늘은 모든 매개 변수가 압축된 후에 압축됩니다.
  • 매개 변수 개수에 대해 정해지지 않은 경우 호출자는 창고를 정리하고, 그렇지 않으면 함수는 스스로 창고를 정리한다
  •  
    이 호출 규칙을 설명하기 위해 다음과 같은 종류와 사용 코드를 정의합니다.
    class A
    {
    public:
       int function1(int a,int b);
       int function2(int a,...);
    };
    int A::function1 (int a,int b)
    {
       return a+b;
    }
    #include
    int A::function2(int a,...)
    {
       va_list ap;
       va_start(ap,a);
       int i;
       int result = 0;
       for(i = 0 ; i < a ; i ++)
       {
         result += va_arg(ap,int);
       }
       return result;
    }
    void callee()
    {
       A a;
       a.function1 (1,2);
       a.function2(3,1,2,3);
    }

     
    callee 함수가 어셈블리로 번역되면 다음과 같이 됩니다.
    
      
      
      
      

    // function1
    0401C1D push 2
    00401C1F push 1
    00401C21 lea ecx,[ebp-8]
    00401C24 call function1 , this
    // function2
    00401C29 push 3
    00401C2B push 2
    00401C2D push 1
    00401C2F push 3
    00401C31 lea eax,[ebp-8] this
    00401C34 push eax
    00401C35 call function2
    00401C3A add esp,14h

     
    매개 변수 개수가 고정된 경우, stdcall과 유사하고, 정해진 시간에 cdecl와 유사하다는 것을 알 수 있다
     

    naked call


     
    이것은 매우 보기 드문 호출 약정으로 일반 프로그래머들은 사용하지 말라고 건의한다.컴파일러는 이 함수에 초기화와 코드 정리를 추가하지 않습니다. 더 특수한 것은return으로 반환값을 되돌릴 수 없고, 삽입 어셈블리로만 결과를 되돌릴 수 있습니다.이것은 일반적으로 실제 모드 드라이버 설계에 사용되며, 화합을 구하는 덧셈 프로그램을 정의한다고 가정하면 다음과 같이 정의할 수 있다.
    __declspec(naked) int  add(int a,int b)
    {
    __asm mov eax,a
    __asm add eax,b
    __asm ret
    }

     
    이 함수는 리턴 반환 값이 없습니다. 리턴은 eax 레지스터를 수정하여 실행되며, 함수를 종료하는 리턴 명령도 반드시 리턴 삽입해야 합니다.위의 코드는 어셈블리로 번역된 후 다음과 같이 변경됩니다.
    
      
      
      
      

    mov eax,[ebp+8]
    add eax,[ebp+12]
    ret 8


     
    이 코스메틱은 및 _stdcall 및 cdecl를 결합하여 사용하는 코드는 앞에서 cdecl와 결합하여 사용하는 코드이고 stdcall과 결합된 코드는 다음과 같다.
    __declspec(naked) int __stdcall function(int a,int b)
    {
    __asm mov eax,a
    __asm add eax,b
    __asm ret 8 // 8
    }

     
    이 함수가 호출되면 일반적인 cdecl 및 stdcall 호출 함수와 일치합니다.
     

    함수 호출 약정으로 인한 흔한 문제


     
    정의된 약정과 사용된 약정이 일치하지 않으면 창고가 파괴되고 심각한 문제가 발생할 수 있습니다. 다음은 두 가지 흔히 볼 수 있는 문제입니다.
  • 함수 원형 성명과 함수체 정의가 일치하지 않음
  • DLL에서 함수를 가져올 때 서로 다른 함수 규칙이 선언됨
  •  
    후자를 예로 들면, 우리가 dll에서 함수를 다음과 같이 성명했다고 가정하자.
    __declspec(dllexport) int func(int a,int b);// , stdcall, cdecl

     
    사용 시 코드:

    typedef int (*WINAPI DLLFUNC)func(int a,int b);
    hLib = LoadLibrary(...);
    DLLFUNC func = (DLLFUNC)GetProcAddress(...)//
    result = func(1,2);//

     
    호출자가 WINAPI의 의미를 이해하지 못해 이 수식이 추가되었습니다. 상기 코드는 창고가 파괴될 것입니다. MFC가 컴파일할 때 삽입한 checkesp 함수는 창고가 파괴되었다는 것을 알려 줍니다.
     
    다음에서 시작합니다.http://blog.vckbase.com/arong/archive/2004/06/09/409.aspx

    좋은 웹페이지 즐겨찾기