flat7th

+ 呼び出し規約

created 2005-12-22 modified 2024-01-09 

無料で入手できるWindowsのデバッガがある。これのドキュメントが結構よい。
前の現場で調べ物をしていたとき、x86アーキテクチャでの関数呼び出しの規約が書いてあるのを見つけて読んだ。とても勉強になった。

ちょっと引用。場所は

Debugging Tools for Windows
 |__Debugging Techniques
     |__Processor Architecture
         |__The x86 Architecture
のCalling Conventions。

Calling Conventions


The x86 architecture has several different calling conventions. Fortunately, they all follow the same register preservation and function return rules:

  • Functions must preserve all registers, except for eax, ecx, and edx, which can be changed across a function call, and esp, which must be updated according to the calling convention.
  • The eax register receives function return values if the result is 32 bits or smaller. If the result is 64 bits, then the result is stored in the edx:eax pair.

The following is a list of calling conventions used on the x86 architecture:

  • Win32 (__stdcall)
    • Function parameters are passed on the stack, pushed right to left, and the callee cleans the stack.
  • Native C++ method call (also known as thiscall)
    • Function parameters are passed on the stack, pushed right to left, the "this" pointer is passed in the ecx register, and the callee cleans the stack.
  • COM (__stdcall for C++ method calls)
    • Function parameters are passed on the stack, pushed right to left, then the "this" pointer is pushed on the stack, and then the function is called. The callee cleans the stack.
  • __fastcall
    • The first two DWORD-or-smaller arguments are passed in the ecx and edx registers. The remaining parameters are passed on the stack, pushed right to left. The callee cleans the stack.
  • __cdecl
    • Function parameters are passed on the stack, pushed right to left, and the caller cleans the stack. The __cdecl calling convention is used for all functions with variable-length parameters.

DeepLによる翻訳(少し修正)


x86アーキテクチャには、いくつかの異なる呼び出し規約がある。幸いなことに、これらはすべて同じレジスタ保存と関数リターンルールに従います:

  • 関数はすべてのレジスタを保持しなければならないが、eax、ecx、edxは関数の呼び出しにまたがって変更可能であり、espは呼び出し規約に従って更新されなければならない。
  • eaxレジスタは、結果が32ビット以下の場合、関数の戻り値を受け取ります。結果が 64 ビットの場合、結果は edx:eax のペアに格納されます。


以下は、x86アーキテクチャで使用される呼び出し規約のリストである:

  • Win32 (__stdcall)
    • 関数パラメータはスタック上に渡され、右から左にプッシュされ、呼ばれた側がスタックを掃除する。
  • ネイティブC++メソッド呼び出し(thiscallとしても知られる)
    • 関数パラメータがスタックに渡され、右から左にプッシュされ、"this" ポインタがecxレジスタに渡され、呼ばれた側がスタックを掃除する。
  • COM(C++メソッド呼び出しの__stdcall)
    • 関数パラメータがスタックに渡され、右から左にプッシュされ、次に "this" ポインタがスタックにプッシュされ、関数が呼び出されます。呼ばれた側がスタックを掃除する。
  • __fastcall
    • DWORDまたはそれより小さい最初の2つの引数は、ecxとedxレジスタにて渡される。残りの引数はスタック上に渡され、右から左にプッシュされる。呼ばれた側がスタックを掃除する。
  • __cdecl
    • 関数のパラメータはスタックに渡され、右から左にプッシュされ、呼び出し元がスタックを掃除する。__cdecl呼び出し規約は、可変長パラメータを持つすべての関数に使用される。


えーとつまり、

読むまでは呼び出し側と呼ばれる側でどっちがスタックから降ろすなんて意識していなかった。

普通の関数では、呼び側がパラメータを積んで、呼び出された側がreturnするときにスタックから降ろす(__stdcall)。スタックから降ろす操作は共通だもんね。
でも、可変引数の関数では、いくつ積むかは呼び側次第なので、共通処理でスタックから降ろすのはムリ。呼び側がスタックから降ろす(__cdecl)。
わかれば当たり前なんですけどね。

お客様から提供されたcoreファイルを解析するときには、こういう知識も必要になるのです。