C関数呼び出し
- 2018-02-05 14:53:00
- Bigshot, Renee
- オリジナル 3649
コンパイルの観点からのC関数呼び出しの解析
以下の関数呼び出しを見て、
1 int Add(int x、int y) 2 { 3 int sum = 0; 4 sum = x + y; 5リターンサム。 6} 7 8 int main() 9 { 10 int a = 10; 11 int b = 12; 12 int ret = 0; 13 ret = Add(a、b); 14は0を返します。 15}
アセンブリはC関数呼び出しの説明に使用されるため、いくつかのレジスタとアセンブリ命令を見てみましょう。
登録
-esp:拡張スタックポインター。常に指すポインターを保持しますシステムスタックの上部にあるスタックフレームの上部に。
-ebp:拡張ベースポインター。常に指すポインターを保持しますシステムスタックの上部にあるスタックフレームの下部。
-eax:アキュムレータ。加算および乗算命令のデフォルトレジスタです。
-ebx:ベース。メモリアドレス指定でベースアドレスを保持します。
-ecx:カウンター。REPおよびLOOP命令のデフォルトのカウンターです。
-edx:整数除算によって生成された剰余を保持します。
-esi / edi:送信元/宛先インデックス。文字列操作では、DS:ESはソース文字列を指し、ES:EDIはターゲット文字列を指します。
32ビットプラットフォームでは、ESPは一度に4バイトずつ削減されます。
組立説明書
-mov:データ移動命令。これは非常に基本的なプログラミングです命令。データをソースアドレスから-ターゲットに移動することですアドレス、基本的にデータ移動の性質と同じですレジスタ間。
-sub:減算命令
-lea:オフセットアドレスを取得
-プッシュ:プッシュ命令
-pop:ポップ命令
-電話:保存する 現在の命令の次の命令で、ターゲット関数にジャンプします。
これらの指示を理解できれば、関数呼び出しを理解するのに役立ちます。そうでない場合は、私の説明を読むことができます。
解析する前に、以下のメモリアドレス空間の分布を見てみましょう。
===============最高アドレス(例:0xFFFF) | | |カーネルアドレス| <-カーネルアドレス空間| | | ------------- | | | | ENV VAR | <-環境変数| ------------- | | | |パラメータ| <-コマンドラインパラメーター| | | ------------- | | | |スタック| | | | ------------- | | | | | <-共有ライブラリとマップされたメモリ| | | ------------- | | | | HEAP | | | | ------------- | | | |データセグメント| | | | ------------- | | | |コードセグメント| | | ===============最低住所
スタックスペースはメモリアドレスの減少に向かって増加し、関数のフレームを保存するためのものです。そのサイズは限られていますが、数MB。
アセンブラーコード
-メイン
int main() { 011B26E0プッシュebp 011B26E1 mov ebp、esp 011B26E3 sub esp、0E4h 011B26E9プッシュebx 011B26EAプッシュESI 011B26EB push edi 011B26EC lea edi、[ebp-0E4h] 011B26F2 mov ecx、39h 011B26F7 mov eax、0CCCCCCCCh 011B26FC担当者はdword ptr es:[edi] int a = 10; 011B26FE mov dword ptr [a]、0Ah int b = 12; 011B2705 mov dword ptr [b]、0Ch int ret = 0; 011B270C mov dword ptr [ret]、0 ret = Add(a、b); 011B2713 mov eax、dword ptr [b] 011B2716プッシュeax 011B2717 mov ecx、dword ptr [a] 011B271AプッシュECX 011B271B呼び出し@ ILT + 640(_Add)(11B1285h) 011B2720、esp、8を追加 011B2723 mov dword ptr [ret]、eax 0を返します。 011B2726 xor eax、eax } 011B2728ポップエディ 011B2729ポップESI 011B272AポップEBX 011B272Bは、esp、0E4hを追加します 011B2731 cmp ebp、esp 011B2733 @ ILT + 450(__ RTC_CheckEsp)を呼び出す(11B11C7h) 011B2738 mov esp、ebp 011B273AポップEBP 011B273B ret
- 追加
int Add(int x、int y) { 011B26A0 push ebp 011B26A1 mov ebp、esp 011B26A3 sub esp、0CCh 011B26A9 push ebx 011B26AAプッシュESI 011B26AB push edi 011B26AC lea edi、[ebp-0CCh] 011B26B2 mov ecx、33h 011B26B7 mov eax、0CCCCCCCCh 011B26BC担当者はdword ptr es:[edi] int sum = 0; 011B26BE mov dword ptr [合計]、0 sum = x + y; 011B26C5 mov eax、dword ptr [x] 011B26C8 eax、dword ptr [y]を追加 011B26CB mov dword ptr [sum]、eax 戻り値; 011B26CE mov eax、dword ptr [合計] } 011B26D1ポップエディ 011B26D2ポップESI 011B26D3ポップebx 011B26D4 mov esp、ebp 011B26D6ポップEBP 011B26D7 ret
下の画像は住所の変更を説明しています呼び出し中。イメージ内のすべてのアドレスは、VS Codeのデバッグからのものです。32ビットWindows。
コール
1.パラメーターをコピーします。
2.現在の命令の次の命令を保存し、呼び出された関数にジャンプします。
2つのステップは主な機能で実行されます。
追加機能を呼び出して、以下を実行します。
1. ebpとespを移動して、フレームの新しい構造を作成します。
2一時変数などをプッシュして生成します。
2.値を返します。
3つのステップは、追加機能で行われます。
これで、main関数のAdd関数の呼び出しが終了しました。一般的に、3つのステップが含まれます。
1.呼び出された関数名で関数へのエントリを見つけます。
2.関数のパラメーターと、スタック内の関数内で定義された変数のメモリ空間の呼び出しに適用します。
3。関数が実行された後、パラメーターとメモリ空間を解放しますスタック内の関数によって適用され、次の場合に値を復元しますどれか。
マイクロコンピューターの原理を知っていれば、CPU割り込みを考えることができます。はい、関数呼び出しはCPU割り込みの処理と同じです。
関数呼び出し規約
_stdcall
すべてのパラメーターは、右から左にスタックにプッシュされます。呼び出されたサブプログラムはスタックをクリアします。
_cdecl(Cのデフォルトの呼び出し規約)
すべてのパラメーターは、右から左にスタックにプッシュされます。呼び出し元はスタックをクリアします。
_fastcall
それ関数を高速で呼び出すことです。主にレジスタに依存して渡すパラメーター、および残りのパラメーターは右からスタックにプッシュされます左へ。呼び出されたサブプログラムはスタックをクリアします。
このブログでは、_stdcall規則に従って関数が呼び出されます。