スタックと関数呼び出し

1730 ワード

スタックは重要なプログラミング概念(コンパイル授業とプログラム設計授業で関連内容を話したことがある)であり、コンパイラとプログラミング言語と密接に関連している.呼び出しスタックを理解する上で最も重要な点は、スタックの構造、EBPレジスタの役割です.1つの関数呼び出し動作は、0から複数のPUSH命令(パラメータインスタック用)、1つのCALL命令に分解することができる.CALL命令内部には、実際には、戻りアドレス(すなわち、CALL命令の次の命令のアドレス)をスタックに戻す動作(ハードウェアによって完了)が暗黙的に含まれている.ほとんどのローカルコンパイラは、各関数体の前に次のようなアセンブリ命令を挿入します.
pushl   %ebp
movl   %esp , %ebp

これにより、プログラムが1つの関数の実際の命令を実行する前に、パラメータ、戻りアドレス、ebpレジスタの順にスタックに入るデータが既に存在する.これにより、以下のようなスタック構造が得られる(パラメータのスタック順序は呼び出し方法に関係し、ここではC言語のデフォルトのCDECLを例に挙げる).
+|               |     
  |    ...           |
  |    ...           |
  |    3            |
  |    2            |
  |    1            |
  |               |
  |     [ebp]    | <-------- [ebp]
  |               |      

     関数呼び出しスタック構造
この2つのアセンブリ命令は、まずebpレジスタをスタックに入れ、その後、スタックトップポインタespをebpに付与することを意味する.「mov ebp esp」という命令は表面的にはespでebpの元の値を上書きしているように見えますが、実はそうではありません.ebpに値を割り当てる前に、元のebp値はスタック(スタックトップに位置する)に圧縮され、新しいebpはスタックトップを指しているからである.このときebpレジスタはすでに非常に重要な地位にあり、このレジスタにはスタックの中の1つのアドレス(元のebpがスタックに入った後のスタックトップ)が格納されており、このアドレスを基準として、上(スタックの底方向)に戻るアドレス、パラメータ値を取得することができ、下(スタックの頂方向)に関数の局所変数値を取得することができ、このアドレスには前層関数呼び出し時のebp値が格納されている.
一般に、ss:[ebp+4]は戻りアドレス、ss:[ebp+8]は最初のパラメータ値(最後のスタックのパラメータ値、ここでは4バイトのメモリを占有すると仮定)、ss:[ebp-4]は最初のローカル変数、ss:[ebp]は前のレベルのebp値である.ebpのアドレスは常に「上位関数呼び出し時のebp値」であるため、各階層関数呼び出しでは、その時のebp値「上(スタック底方向)」で戻りアドレス、パラメータ値を取得することができ、「下(スタック頂方向)」で関数局所変数値を取得することができる.このようにして、スタックの底に達するまで再帰が形成される.これが関数呼び出しスタックです.
では、最初にmain関数に入る前に、ebpの初期値はいくらですか?
EBPとESPレジスタについては、このブログを参照してください.
スタックの詳細については、『Understanding the Stack』を参照してください.
関連記事、「逆アセンブリ手段を利用してC言語関数を解析する」、「C関数呼び出しメカニズムとスタックフレームポインタ」.