6.3. CALL と RET によるプロシージャのコール
6.3.2. far コール操作と far リターン操作
far
コールを実行するときには、プロセッサは以下の動作を行う(図6-4.
を参照)。1. CSレジスタの現在値を、スタックにプッシュする。
2. EIPレジスタの現在値を、スタックにプッシュする。
3. コールされたプロシージャを格納しているセグメントのセグメント・セレクタを、CS レジスタにロードする。
4. コールされたプロシージャのオフセットを、EIPレジスタにロードする。
5. コールされたプロシージャの実行を開始する。
far
リターンを実行するときには、プロセッサは次の動作を行う。1. スタックのトップ値(リターン命令ポインタ)をEIPレジスタにポップする。
プロシージャ・コール、 割り込み、例外
6
2. スタックのトップ値(戻り先となるコード・セグメントのセグメント・セレクタ)を、
CSレジスタにポップする。
3. RET命令にオプション引き数のnがある場合は、パラメータをスタックから開放する
ため、n オペランドで指定されたバイト数だけスタックポインタをインクリメントす る。
4. コール元プロシージャの実行を再開する。
6.3.3.
パラメータの受け渡しパラメータをプロシージャ間で受け渡すには、汎用レジスタを介する方法、引き数リ ストを使用する方法、スタックを利用する方法、の
3
種類の方法がある。6.3.3.1. 汎用レジスタによるパラメータの受け渡し
プロセッサは、プロシージャ・コールに際して汎用レジスタのステートをセーブしな い。したがって、コール元プロシージャは、CALL命令を実行する前に、パラメータ を(ESPレジスタと
EBP
レジスタを除く)任意の汎用レジスタにコピーすると、コー ルされるプロシージャに最大6
つのパラメータを渡すことができる。コールされたプ図6-2. nearコールとfarコールでのスタック パラメータ 1
パラメータ 2
コール前のESP nearコール時の
スタック far
コール時の スタック
コール元のCS パラメータ 1 パラメータ 2
コール元のEIP パラメータ 3 パラメータ 3
リターン後のESP
コール元のCS パラメータ 1 パラメータ 2
コール元のEIP パラメータ 3 パラメータ 1
パラメータ 2 パラメータ 3
注記: nearリターンまたはfarリターン時には、
コール元のEIP コール後のESP
nearリターン時の スタック
コール元のEIP
RET n 命令の n オペランドに対して正しい 値が与えられた場合に、パラメータが スタックから開放される。
リターン前のESP
コール前のESP コール後のESP
リターン前のESP リターン後のESP farリターン時の
スタック コール前の
スタック フレーム
コール後の スタック フレーム
コール前の スタック フレーム
コール後の スタック フレーム
IA-32 インテル® アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 上巻:基本アーキテクチャ
6-8
ロシージャも同様に、汎用レジスタを介してコール元プロシージャにパラメータを返 すことができる。
6.3.3.2. スタックによるパラメータの受け渡し
多数のパラメータをコールされるプロシージャに渡す場合は、コール元プロシージャ のスタックフレーム内のスタック上にパラメータを配置できる。このとき、(
EBP
レ ジスタ内にある)スタックフレームのベースポインタを使用してフレーム境界を設定 すれば、パラメータへのアクセスが容易になる。また、コールされたプロシージャからコール元プロシージャにパラメータを返す際に も、スタックを使用できる。
6.3.3.3. 引き数リストによるパラメータの受け渡し
多数のパラメータ(またはデータ構造)をコールされるプロシージャに渡すもう
1
つ の方法として、メモリ上のいずれかのデータ・セグメントにある引き数リストにパラ メータも配置できる。この後、汎用レジスタまたはスタックを介して、引き数リスト に対するポインタをコールされたプロシージャに渡すことができる。また、同じ方法 で、コール元プロシージャにパラメータを返すことができる。6.3.4.
プロシージャのステート情報のセーブプロセッサは、プロシージャ・コールに際して汎用レジスタ、セグメント・レジスタ、
EFLAGS
レジスタのいずれの内容もセーブしない。したがって、コール元プロシージャは、リターン後に実行を再開するにあたって必要な汎用レジスタの値を明示的に セーブしなければならない。これらの値は、スタック上、あるいはメモリ上のいずれ かのデータ・セグメントにセーブできる。
PUSHA
命令やPOPA
命令を使用すれば、汎用レジスタの内容を容易にセーブしリストアすることができる。
PUSHA
命令は、すべての汎用レジスタ内の値をスタックにプッ シュする。プッシュする順序は、EAX、ECX、EDX、EBX、ESP(PUSHA命令を実行 する前の値)、EBP、 ESI、 EDI
である。これに対し、POPA
命令は、PUSHA
命令でセー ブしたすべてのレジスタ値(ESI値を除く)を、スタックからそれぞれの対応するレ ジスタにポップする。コールされたプロシージャにおいて、いずれかのセグメント・レジスタのステートが 明示的に変更された場合は、コール元プロシージャへのリターンを実行する前に、そ れらの値を元の値にリストアしなければならない。
コール元プロシージャが
EFLAGS
レジスタのステートを保持しておく必要がある場合 には、PUSHF/PUSHFD
命令とPOPF/POPFD
命令を使用することで、レジスタの全部、プロシージャ・コール、 割り込み、例外
6
または一部をセーブし、リストアすることができる。
PUSHF
命令は、EFLAGS
レジス タの下位ワードをスタックにプッシュし、PUSHFD
命令は、レジスタ全体をスタック にプッシュする。POPF
命令は、スタックからEFLAGS
レジスタの下位ワードに1
ワー ドをポップする。POPFD
命令は、スタックからレジスタに1
ダブルワードをポップす る。6.3.5.
他の特権レベルに対するコールIA-32
アーキテクチャの保護メカニズムにおいては、4
つの特権レベルを認識する。特権レベルはそれぞれ
0
~3
の番号が付けられ、数が大きくなるほど特権レベルは低く なる。特権レベルを使用する理由は、オペレーティング・システムの信頼性を高める ことにある。例えば、図6-3.
に、保護のリングとして見立てた場合、それぞれの特権 レベルがどのように解釈できるかを示す。この例では、最高の特権レベル
0(図の中央)が、システム内の最も重要なコード・
モジュール(通常は、オペレーティング・システムのカーネル)を格納しているセグ メントに対して使用されている。外側のリング(外にいくほど特権は小さくなる)に 行くほど、重要度が低いソフトウェアのコード・モジュールを格納しているセグメン トになる。
低い特権のセグメント内にあるコード・モジュールから高い特権のセグメントで動作 するモジュールにアクセスするには、ゲートと呼ばれる厳密に制御され保護されてい
図6-3. 保護のリング レベル 0
レベル 1 レベル 2 レベル 3 保護のリング
オペレーティング・
オペレーティング・
システムのサービス
アプリケーション
0 1 2 3
最高位 最低位
特権レベル システムのカーネル
(デバイスドライバなど)
IA-32 インテル® アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 上巻:基本アーキテクチャ
6-10
るインターフェイスを使用しなければならない。保護ゲートを介さず、しかも十分な アクセス権を持たないで高い特権のセグメントにアクセスしようとすると、一般保護 例外(
#GP
)が発生する。オペレーティング・システムやエグゼクティブがこのマルチレベルの保護機構を使用 する場合は、コール元プロシージャより高い特権保護レベルにあるプロシージャへの コールは、
far
コールと同様の方法で処理される(6.3.2.
項「far
コール操作とfar
リター ン操作」を参照)。ただし、次の点で異なる。・ CALL
命令で与えられるセグメント・セレクタは、コール・ゲート・ディスクリプ タと呼ばれる特殊なデータ構造を参照する。コール・ゲート・ディスクリプタは、次の内容を保持している。
- アクセス権に関する情報
- コールされるプロシージャのコード・セグメントのセグメント・セレクタ
- コード・セグメントに対するオフセット(すなわち、コールされるプロシージャの命令 ポインタ)
・
プロセッサは、コールされたプロシージャを実行するために、新しいスタックに 切り替える(スタックスイッチ)。それぞれの特権レベルは、自身のスタックを持 つ。特権レベル3
のスタックのセグメント・セレクタとスタックポインタは、それ ぞれSS
レジスタとESP
レジスタに格納され、さらに、より高い特権レベルに対す るコールが発生した時点で自動的にセーブされる。特権レベル2
、1
、0
の各スタッ クのセグメント・セレクタとスタックポインタは、タスク・ステート・セグメン ト(TSS
)と呼ばれるシステム・セグメント内に格納される。スタックスイッチ実行時にコールゲートと
TSS
を使用することは、一般保護例外が発 生した場合を除き、コール元プロシージャにとっては透過である。6.3.6.
特権レベル間のコール操作とリターン操作より高い特権保護レベルに対してコールを実行するときには、プロセッサは次の動作 を行う(図
6-4.
を参照)。1. アクセス権のチェック(特権チェック)を実行する。
2. SS、ESP、CS、EIPの各レジスタの現在値を一時的に内部にセーブする。