リバースエンジニアリングへの道
出田 守です。
最近、情報セキュリティに興味を持ち、『リバースエンジニアリング-Pythonによるバイナリ解析技法』という本(以降、「教科書」と呼びます)を読みました。
「こんな世界があるのか!かっこいい!」と感動し、私も触れてみたいということでド素人からリバースエンジニアリングができるまでを書いていきたいと思います。
ちなみに、教科書ではPython言語が使用されているので私もPython言語を使用しています。
ここを見ていただいた諸先輩方からの意見をお待ちしております。
軌跡
環境
OS: Windows10 64bit Home (日本語)
CPU: Intel® Core™ i3-6006U CPU @ 2.00GHz × 1
メモリ: 2048MB
Python: 3.6.5
私の環境は、普段Ubuntu16.04を使っていますが、ここではWindows10 64bitを仮想マシン上で立ち上げております。
ちなみに教科書では、Windowsの32bitで紹介されています。
Intel Deveroper's Manual [1] Chapter5 - Chapter6
現在CPU周りの知識をつけるために『Intel Software Developer's Manual(IDMと呼びます)』をさっくりまとめています。
前回はIDMのChapter4までをまとめました。
今回はChapter5 - Chapter6までをまとめます。
Chapter5 INSTRUCTION SET SUMMARY
この章ではIntel64とIA-32の命令を要約します。
2つ以上のニーモニックが与えられている場合(たとえば、CMOVA/CMOVNBE)、それらは同じ命令オペコードの異なるニーモニックを表します。アセンブラは、コードリストを読みやすくするために、いくつかの命令で冗長ニーモニックをサポートしています。CMOVA(より上の場合の条件付き移動)とCMOVNBE(以下でない場合の条件付き移動)は同じ条件を表します。
5.1 GENERAL-PURPOSE INSTRUCTIONS
汎用命令はプログラマがIntel64とIA-32のアプリケーションとシステムソフトウェアを書く際によく使う、基本データ移動、算術演算、論理演算、プログラムフロー(分岐?)、文字列操作を実行します。これらはメモリ、汎用レジスタ、EFLAGSレジスタを含むデータを操作します。さらに、メモリ、汎用レジスタ、セグメントレジスタを含むアドレス情報も操作します。
命令グループには、データ転送、バイナリ整数算術演算、10進算術演算、論理演算、シフトおよび回転、ビットおよびバイト演算、プログラム制御、文字列操作、フラグ制御、セグメントレジスタ演算、およびその他のサブグループが含まれます。以下の節では、各サブグループについて説明します。
5.1.1 Data Transfer Instructions
データ転送命令はメモリあるいは汎用レジスタ、セグメントレジスタの間でデータを移動します。さらにこれら命令は条件移動やスタックアクセス、データ変換のような操作を指定して実行できます。
命令 | 説明 |
---|---|
MOV | 汎用レジスタ間、メモリと汎用レジスタまたはセグメントレジスタ間、汎用レジスタから即値でデータ移動 |
CMOVE/CMOVZ | 等しければ条件移動/ゼロであれば条件移動 |
CMOVNE/CMOVNZ | 等しくなれば条件移動/ゼロでなければ条件移動 |
CMOVA/CMOVNBE | より上なら条件移動/以下でなければ条件移動 |
CMOVAE/CMOVNB | 以上なら条件移動/より下でなければ条件移動 |
CMOVB/CMOVNAE | より下なら条件移動/以上でなければ条件移動 |
CMOVBE/CMOVNA | 以下なら条件移動/より上でなければ条件移動 |
CMOVG/CMOVNLE | より上なら条件移動/以下でなければ条件移動 |
CMOVGE/CMOVNL | 以上なら条件移動/より下でなければ条件移動 |
CMOVL/CMOVNGE | より下なら条件移動/以上でなければ条件移動 |
CMOVLE/CMOVNG | 以下なら条件移動/より上でなければ条件移動 |
CMOVC | carryなら条件移動 |
CMOVNC | carryでないなら条件移動 |
CMOVO | overflowなら条件移動 |
CMOVNO | overflowでなければ条件移動 |
CMOVS | signなら(負)条件移動 |
CMOVNS | signでなければ(正)条件移動 |
CMOVP/CMOVPE | parityなら条件移動/parity even(パリティが偶数?)なら条件移動 |
CMOVNP/CMOVPO | parityでなければ条件移動/parity odd(パリティが奇数?)なら条件移動 |
XCHG | 交換 |
BSWAP | Byte入れ替え |
XADD | 交換して加算 |
CMPXCHG | 比較と交換 |
CMPXCHG8B | 8Byte比較と交換 |
PUSH | スタックにPush |
POP | スタックからPop |
PUSHA/PUSHAD | 汎用レジスタをスタックにPush |
POPA/POPAD | スタックから汎用レジスタにPop |
CWD/CDQ | WordからDwordへ変換/DwordからQwordへ変換 |
CBW/CWDE | ByteからWordへ変換/EAXレジスタのWordをDwordに変換 |
MOVSX | 移動と符号拡張 |
MOVZX | 移動とゼロ拡張 |
CMOV命令で条件が重複しているような命令がありました。調べると解説していただいているWebページが見つかりました。他の命令について解説されているので非常に勉強になります。以下にそのURLを示します。
http://softwaretechnique.jp/OS_Development/Tips/IA32_P6_Instructions/CMOVCC.html#CMOVCC
5.1.2 Binary Arithmetic Instructions
バイナリ算術命令はメモリと/または汎用レジスタに格納されたByte, Word, Dword整数を基本バイナリ整数演算を実行します。
命令 | 説明 |
---|---|
ADCX | carryと符号なし整数の加算 |
ADOX | overflowと符号なし整数の加算 |
ADD | 整数加算 |
SUB | 整数減算 |
SBB | borrowと減算 |
IMUL | 符号付き乗算 |
MUL | 符号なし乗算 |
IDIV | 符号付き除算 |
DIV | 符号なし除算 |
INC | インクリメント |
DEC | デクリメント |
NEG | 否定 |
CMP | 比較 |
5.1.3 Decimal Arithmetic Instructions
十進整数命令はBCDデータに十進整数演算を実行します。
命令 | 説明 |
---|---|
DAA | 加算後に十進調整 |
DAS | 減算後に十進調整 |
AAA | 加算後にASCII調整 |
AAS | 減算後にASCII調整 |
AAM | 乗算後にASCII調整 |
AAD | 除算後にASCII調整 |
5.1.4 Logical Instructions
論理演算命令はByte, Word, Dwordの基本AND, OR, XOR, NOT論理操作を実行します。
命令 | 説明 |
---|---|
AND | bit毎にANDを実行 |
OR | bit毎にORを実行 |
XOR | bit毎にXORを実行 |
NOT | bit毎にNOTを実行 |
5.1.5 Shift and Rotate Instructions
シフトと回転命令はWord, Dwordオペランドのシフト操作と回転操作をします。
命令 | 説明 |
---|---|
SAR | 算術右にシフト |
SHR | 論理右にシフト |
SAL/SHL | 算術左にシフト/論理左にシフト |
SHRD | 右にダブルシフト |
SHLD | 左にダブルシフト |
ROR | 右に回転 |
ROL | 左に回転 |
RCR | carryと右に回転 |
RCL | carryと左に回転 |
5.1.6 Bit and Byte Instructions
bit命令はWord, Dwordオペランドの個々のbitをテストと編集します。Byte命令はEFLAGSレジスタのフラグの状態を示すためにByteオペランドをセットします。
命令 | 説明 |
---|---|
BT | bitをテスト |
BTS | bitをテストしセット |
BTR | bitをテストしリセット |
BTC | bitをテストし補数を取得 |
BSF | bitを順方向にScan |
BSR | bitを逆方向にScan |
SETE/SETZ | 等しいならByteをセット/ゼロならByteをセット |
SETNE/SETNZ | 等しくなければByteをセット/ゼロでなければByteをセット |
SETA/SETNBE | より上ならByteをセット/以下でなければByteをセット |
SETAE/SETNB/SETNC | 以上ならByteをセット/より下でなければByteをセット/carryでなければByteをセット |
SETB/SETNAE/SETC | より下ならByteをセット/以上でなければByteをセット/carryならByteをセット |
SETBE/SETNA | 以下ならByteをセット/より上でなければByteをセット |
SETG/SETNLE | 以上ならByteをセット/以下でなければByteをセット |
SETGE/SETNL | 以上ならByteをセット/より下でなければByteをセット |
SETL/SETNGE | より下ならByteをセット/以上でなければByteをセット |
SETLE/SETNG | 以下ならByteをセット/より上でなければByteをセット |
SETS | signなら(負)Byteをセット |
SETNS | signでなければ(正)Byteをセット |
SETO | overflowならByteをセット |
SETNO | overflowでなければByteをセット |
SETPE/SETP | parity even(パリティが偶数)ならByteをセット/parityならByteをセット |
SETPO/SETNP | parity odd(パリティが奇数)ならByteをセット/parityでなければByteをセット |
TEST | 論理比較 |
CRC32 | データ完全性プロトコルの迅速かつ効率的な実装のための巡回冗長検査を計算するハードウェアアクセラレーションを提供 |
POPCNT | この命令は、第2オペランド(ソース)で1にセットされたbit数を計算し、第1オペランド(宛先レジスタ)にカウントを返却 |
5.1.7 Control Transfer Instructions
制御転送(分岐)命令はジャンプ、条件ジャンプ、ループ、コール、リターンなどのプログラムフロー操作をします。
命令 | 説明 |
---|---|
JMP | ジャンプ |
JE/JZ | 等しいならジャンプ/ゼロならジャンプ |
JNE/JNZ | 等しくなければジャンプ/ゼロでなければジャンプ |
JA/JNBE | より上ならジャンプ/以下でなければジャンプ |
JAE/JNB | 以上ならジャンプ/より下でなければジャンプ |
JB/JNAE | より下ならジャンプ/以上でなければジャンプ |
JBE/JNA | 以下ならジャンプ/より上でなければジャンプ |
JG/JNLE | より上ならジャンプ/以下でなければジャンプ |
JGE/JNL | 以上ならジャンプ/より下でなければジャンプ |
JL/JNGE | より下ならジャンプ/以上でなければジャンプ |
JLE/JNG | 以下ならジャンプ/より上でなければジャンプ |
JC | carryならジャンプ |
JNC | carryでなければジャンプ |
JO | overflowならジャンプ |
JNO | overflowでなければジャンプ |
JS | signなら(負)ジャンプ |
JNS | signでなければ(正)ジャンプ |
JPO/JNP | parity odd(パリティが奇数)ならジャンプ/パリティでないならジャンプ |
JPE/JP | parity even(パリティが偶数)ならジャンプ/パリティならジャンプ |
JCXZ/JECXZ | CXレジスタがゼロならジャンプ/ECXレジスタがゼロならジャンプ |
LOOP | ECXをカウンタとしループ |
LOOPZ/LOOPE | ECXをカウンタとしゼロならループ/ECXをカウンタとし等しいならループ |
LOOPNZ/LOOPNE | ECXをカウンタとしゼロでなければループ/ECXをカウンタとし等しくなければループ |
CALL | プロシージャを呼び出す |
RET | リターン(復帰) |
IRET | 割り込みハンドラからリターン(復帰) |
INT | ソフトウェア割り込み |
INTO | overflowで割り込み |
BOUND | 範囲外の値を検出 |
ENTER | 高レベルプロシージャ入り口(entry) |
LEAVE | 高レベルプロシージャ出口(exit) |
5.1.8 String Instructions
文字列命令はByte文字列をメモリから移動させるなどの操作をします。
命令 | 説明 |
---|---|
MOVS/MOVSB | 文字列移動/Byte文字列移動 |
MOVS/MOVSW | 文字列移動/Word文字列移動 |
MOVS/MOVSD | 文字列移動/Dword文字列移動 |
CMPS/CMPSB | 文字列比較/Byte文字列比較 |
CMPS/CMPSW | 文字列比較/Word文字列比較 |
CMPS/CMPSD | 文字列比較/Dword文字列比較 |
SCAS/SCASB | 文字列スキャン/Byte文字列スキャン |
SCAS/SCASW | 文字列スキャン/Word文字列スキャン |
SCAS/SCASD | 文字列スキャン/Dword文字列スキャン |
LODS/LODSB | 文字列読み込み/Byte文字列読み込み |
LODS/LODSW | 文字列読み込み/Word文字列読み込み |
LODS/LODSD | 文字列読み込み/Dword文字列読み込み |
STOS/STOSB | 文字列格納/Byte文字列格納 |
STOS/STOSW | 文字列格納/Word文字列格納 |
STOS/STOSD | 文字列格納/Dword文字列格納 |
REP | ECXがゼロでなければ繰り返し |
REPE/REPZ | 等しいなら繰り返し/ゼロなら繰り返し |
REPNE/REPNZ | 等しくないなら繰り返し/ゼロでないなら繰り返し |
5.1.9 I/O Instructions
I/O命令はプロセッサのI/Oとレジスタ間またはメモリ間でデータを移動させます。
命令 | 説明 |
---|---|
IN | ポートから読み込み |
OUT | ポートへ書き込み |
INS/INSB | ポートから文字列読み込み/ポートからByte文字列読み込み |
INS/INSW | ポートから文字列読み込み/ポートからWord文字列読み込み |
INS/INSD | ポートから文字列読み込み/ポートからDword文字列読み込み |
OUTS/OUTSB | ポートへ文字列書き込み/ポートへByte文字列書き込み |
OUTS/OUTSW | ポートへ文字列書き込み/ポートへWord文字列書き込み |
OUTS/OUTSD | ポートへ文字列書き込み/ポートへDword文字列書き込み |
5.1.10 Enter and Leave Instructions
(省略)
5.1.11 Flag Control (EFLAG) Instructions
フラグ制御命令はEFLAGSレジスタのフラグを操作します。
命令 | 説明 |
---|---|
STC | carryフラグをセット |
CLC | carryフラグをクリア |
CMC | carryフラグを反転 |
CLD | directionフラグをクリア |
STD | directionフラグをセット |
LAHF | AHレジスタのフラグを読み込み |
SAHF | AHレジスタのフラグを格納 |
PUSHF/PUSHFD | stackにEFLAGSをpush |
POPF/POPFD | stackからEFLAGSをpop |
STI | 割り込みフラグをセット |
CLI | 割り込みフラグをクリア |
5.1.12 Segment Register Instructions
セグメントレジスタ命令はセグメントレジスタからfar pointer(セグメントアドレス)を読み込むのを許可します。
命令 | 説明 |
---|---|
LDS | DSで使用されているfar pointerを読み込み |
LES | ESで使用されているfar pointerを読み込み |
LFS | FSで使用されているfar pointerを読み込み |
LGS | GSで使用されているfar pointerを読み込み |
LSS | SSで使用されているfar pointerを読み込み |
5.1.13 Miscellaneous Instructions
その他の命令は実効アドレスをロード、「何もしない」を実行、プロセッサ識別情報を取り出すなどを提供します。
命令 | 説明 |
---|---|
LEA | 実効アドレスをロード |
NOP | 何もしない |
UD | 未定義命令 |
XLAT/XLATB | テーブル参照変換 |
CPUID | プロセッサ識別 |
MOVBE | Byteデータを入れ替えて移動 |
PREFETCHW | 書き込まれそうなキャッシュのデータを先読み |
PREFETCHWT1 | 書き込みを意図するヒントT1を先読み? |
CLFLUSH | メモリオペランドおよびそれに関連するキャッシュラインを、プロセッサのキャッシュ階層のすべてのレベルからフラッシュして無効化 |
CLFLUSHOPT | メモリオペランドおよびそれに関連するキャッシュラインを、最適化されたメモリシステムスループットを備えたキャッシュ階層のすべてのレベルからフラッシュして無効化 |
5.1.14 User Mode Extended Sate Save/Restore Instructions
命令 | 説明 |
---|---|
XSAVE | メモリへ拡張された状態を保存 |
XSAVEC | メモリへ拡張された状態を圧縮して保存 |
XSAVEOPT | メモリへ拡張された状態を最適化して保存 |
XRSTOR | メモリから拡張データを格納 |
XGETBV | 制御レジスタの拡張された状態を読み込み |
5.1.15 Random Number Generator Instructions
命令 | 説明 |
---|---|
RDRAND | ハードウェアから乱数を取得 |
RDSEED | ハードウェアから乱数を取得 |
5.1.16 BMI1, BMI2
命令 | 説明 |
---|---|
ANDN | 第1のソースと反転された第2のソースオペランドとbit毎にAND |
BEXTR | 連続したbitを抽出 |
BLSI | セットされたbitの一番低いbitを抽出 |
BLSMSK | はじめにセットされたbitからしたを全てセット |
BLSR | セットされたbitの一番低いbitをリセット |
BZHI | 特定のbit位置から始まる高いbitをゼロにクリア |
LZCNT | 上位からゼロのbitを1がくるまでカウント |
MULX | 算術フラグに影響を与えずに符号なし乗算 |
PDEP | マスクを使って並列にbitを格納 |
PEXT | マスクを使って並列にbitを抽出 |
RORX | 算術フラグに影響を与えずに右回転 |
SARX | 右に算術シフト |
SHLX | 左に論理シフト |
SHLX | 右に論理シフト |
TZCNT | 下位からゼロのbitを1がくるまでカウント |
5.1.16.1 Detection of VEX-encoded GPR Instructions, LZCNT and TZCNT, PREFETCHW
(省略)
5.2 X87 FPU INSTRUCTIONS
x86 FPU命令はx86 FPUプロセッサによって実行されます。これらの命令は浮動小数点、整数、BCDオペランドを操作します。
5.2.1 x87 FPU Data Transfer Instructions
データ転送命令は浮動小数点、整数、BCDをx87 FPUレジスタ間で移動させます。さらにこれらの命令は浮動小数点を条件付き移動ができます。
命令 | 説明 |
---|---|
FLD | 浮動小数点を読み込み |
FST | 浮動小数点を格納 |
FSTP | 浮動小数点を格納しpop |
FILD | 整数を読み込み |
FISTP | 整数を格納しpop |
FBLD | BCDを読み込み |
FBSTP | BCDを格納しpop |
FXCH | レジスタ交換 |
FCMOVE | 等しいなら浮動小数点を移動 |
FCMOVNE | 等しくなければ浮動小数点を移動 |
FCMOVB | より下なら浮動小数点を移動 |
FCMOVBE | 以下なら浮動小数点を移動 |
FCMOVNB | より下でないなら浮動小数点を移動 |
FCMOVNBE | 以下でないなら浮動小数点を移動 |
FCMOVU | 順序付けられていなければ浮動小数点を移動 |
FCMOVNU | 順序付けられているなら浮動小数点を移動 |
5.2.2 x87 FPU Basic Arithmetic Instructions
基本算術命令は浮動小数点、整数オペランドに基本算術演算をします。
命令 | 説明 |
---|---|
FADD | 浮動小数点を加算 |
FADDP | 浮動小数点を加算しpop |
FIADD | 整数を加算 |
FSUB | 浮動小数点を減算 |
FISUB | 整数を減算 |
FSUBR | 浮動小数点を逆減算 |
FSUBRP | 浮動小数点を逆減算しpop |
FISUBR | 整数を逆減算 |
FMUL | 浮動小数点を乗算 |
FMULP | 浮動小数点を乗算しpop |
FIMUL | 整数を乗算 |
FDIV | 浮動小数点を除算 |
FDIVP | 浮動小数点を除算しpop |
FIDIV | 整数を除算 |
FDIVR | 浮動小数点を逆除算 |
FDIVRP | 浮動小数点を逆除算しpop |
FIDIVR | 整数を逆除算 |
FPREM | 剰余 |
FPREM1 | IEEEの剰余 |
FABS | 絶対値 |
FCHS | sign変更 |
FRNDINT | 整数へ丸める |
FSCALE | 2乗で拡大 |
FSQRT | 平方根 |
FXTRACT | 指数と仮数を抽出 |
5.2.3 x87 FPU Comparison Instructions
比較命令は浮動小数点または整数を調査または比較します。
命令 | 説明 |
---|---|
FCOM | 浮動小数点を比較 |
FCOMP | 浮動小数点を比較しpop |
FCOMPP | 浮動小数点を比較し2回pop |
FUCOM | 浮動小数点を順序付けずに比較 |
FUCOMP | 浮動小数点を順序付けずに比較しpop |
FUCOMPP | 浮動小数点を順序付けずに比較し2回pop |
FICOM | 整数を比較 |
FICOMP | 整数を比較しpop |
FCOMI | 浮動小数点を比較しEFLAGSにセット |
FUCOMI | 浮動小数点を順序付けずに比較しEFLAGSにセット |
FCOMIP | 浮動小数点を比較しEFLAGSにセットしpop |
FCOMIP | 浮動小数点を順序付けずに比較しEFLAGSにセットしpop |
FTST | 浮動小数点をテスト(0.0と比較) |
FXAM | 浮動小数点を調査 |
5.2.4 x87 FPU Transcendental Instructions
すぐれた(?)命令は浮動小数点に対して、基本三角関数と対数関数を実行します。
命令 | 説明 |
---|---|
FSIN | サイン |
FCOS | コサイン |
FSINCOS | サインとコサイン |
FPTAN | (部分?)タンジェント |
FPATAN | (部分?)アークタンジェント |
F2XM1 | $2^x-1$ |
FYL2X | $y*log_2x$ |
FYL2XP1 | $y*log_2(x+1)$ |
5.2.5 x87 FPU Load Constants Instructions
定数読み込み命令はx86 FPUレジスタの中に$\pi$のような共通した定数を読み込みます。
命令 | 説明 |
---|---|
FLD1 | +1.0を読み込み |
FLDZ | +0.0を読み込み |
FLDPI | $\pi$を読み込み |
FLDL2E | $log_2e$を読み込み |
FLDLN2 | $log_22$を読み込み |
FLDL2T | $log_210$を読み込み |
FLDLG2 | $log_102$を読み込み |
5.2.6 x87 FPU Control Instructions
x87 FPU制御命令はx87 FPU状態をx87 FPUレジスタにスタック、保存、取り出しをします。
命令 | 説明 |
---|---|
FINCSTP | FPUレジスタのスタックポインタをインクリメント |
FDECSTP | FPUレジスタのスタックポインタをデクリメント |
FFREE | 浮動小数点レジスタを開放 |
FINIT | エラー条件をチェックしてからFPUを初期化 |
FNINIT | エラー条件をチェックせずにFPUを初期化 |
FCLEX | エラー条件をチェックしてから浮動小数点例外フラグをクリア |
FNCLEX | エラー条件をチェックせずに浮動小数点例外フラグをクリア |
FSTCW | エラー条件をチェックしてからFPU制御をワードで格納 |
FNSTCW | エラー条件をチェックせずにFPU制御をワードで格納 |
FLDCW | FPU制御をワードで読み込み |
FSTENV | エラー条件をチェックしてからFPU環境をワードで格納 |
FNSTENV | エラー条件をチェックせずにFPU環境をワードで格納 |
FLDENV | FPU環境をワードで読み込み |
FSAVE | エラー条件をチェックしてからFPU状態をワードで保存 |
FNSAVE | エラー条件をチェックせずにFPU状態をワードで保存 |
FRSTOR | FPU状態を取り出し |
FSTSW | エラー条件をチェックしてからFPU状態をワードで格納 |
FNSTSW | エラー条件をチェックせずにFPU状態をワードで格納 |
WAIT/FWAIT | FPUの待ち |
FNOP | FPU版NOP |
5.3 X87 FPU AND SIMD STATE MANAGEMENT INSTRUCTIONS
2つの状態管理命令はPentium ⅡのIA-32のなかで実装されています。
命令 | 説明 |
---|---|
FXSAVE | x87 FPUとSIMD状態を保存 |
FXRSTOR | x87 FPUとSIMD状態を取り出し |
(以下省略)
5.4 MMX INSTRUCTIONS
MMX命令は汎用レジスタと/またはMMXレジスタのメモリにパックされたWord, Dword, Qword整数を操作します。
MMX命令はIntel64とIA-32プロセッサがMMXテクノロジにサポートしている場合のみ実行できます。サポートされているかはCPUID命令で検出することが出来ます。
5.4.1 MMX Data Transfer Instructions
データ転送命令はMMXレジスタ間あるいはMMXレジスタとメモリ間でDword、Qwordオペランドを移動します。
命令 | 説明 |
---|---|
MOVD | Dwordを移動 |
MOVQ | Qwordを移動 |
5.4.2 MMX Conversion Instructions
変換命令はByte, Word, Dwordをパックとアンパックします。
命令 | 説明 |
---|---|
PACKSSWB | signed saturation(?)でByteからWordにパック |
PACKSSDW | signed saturation(?)でWordからDwordにパック |
PACKUSWB | unsigned saturation(?)でByteからWordでパック |
PUNPCKHBW | 上位Byteをアンパック |
PUNPCKHWD | 上位Wordをアンパック |
PUNPCKLDQ | 上位Dwordをアンパック |
PUNPCKLBW | 下位Byteをアンパック |
PUNPCKLWD | 下位Wordをアンパック |
PUNPCKLDQ | 下位Dwordをアンパック |
5.4.3 MMX Packed Arithmetic Instructions
パックされた算術命令はパックされたByte, Word, Dword整数にたいしてパックされた整数算術演算を実行します。
命令 | 説明 |
---|---|
PADDB | パックされたByte整数を加算 |
PADDW | パックされたWord整数を加算 |
PADDD | パックされたDword整数を加算 |
PADDSB | signed saturationでパックされた符号付きByte整数を加算 |
PADDSW | signed saturationでパックされた符号付きWord整数を加算 |
PADDUSB | unsigned saturationでパックされた符号なしByte整数を加算 |
PADDUSW | unsigned saturationでパックされた符号なしWord整数を加算 |
PSUBB | パックされたByte整数を減算 |
PSUBW | パックされたWord整数を減算 |
PSUBD | パックされたDword整数を減算 |
PSUBSB | signed saturationでパックされた符号付きByte整数を減算 |
PSUBSW | signed saturationでパックされた符号付きWord整数を減算 |
PSUBUSB | unsigned saturationでパックされた符号なしByte整数を減算 |
PSUBUSW | unsigned saturationでパックされた符号なしWord整数を減算 |
PMULHW | パックされた符号付きWord整数を乗算し、上位の結果を格納 |
PMULLW | パックされた符号付きWord整数を乗算し、下位の結果を格納 |
PMADDWD | パックされたWord整数を乗算し、加算 |
5.4.4 MMX Comparison Instructions
比較命令はパックされたByte, Word, Dwordを比較します。
命令 | 説明 |
---|---|
PCMPEQB | パックされたByteが等しいか比較 |
PCMPEQW | パックされたWordが等しいか比較 |
PCMPEQD | パックされたDwordが等しいか比較 |
PCMPGTB | パックされた符号付きByteが以上か比較 |
PCMPGTW | パックされた符号付きWordが以上か比較 |
PCMPGTD | パックされた符号付きDwordが以上か比較 |
5.4.5 MMX Logical Instructions
論理命令はQwordオペランドをAND, AND NOT, OR, XOR操作を実行します。
命令 | 説明 |
---|---|
PAND | bit毎にAND |
PANDN | bit毎にAND NOT |
POR | bit毎にOR |
PXOR | bit毎にXOR |
5.4.6 MMX Shift and Rotate Instructions
シフトと回転命令は64bitのパックされたByte, Word, Dword, Qwordオペランドをシフトと回転をします。
命令 | 説明 |
---|---|
PSLLW | パックされたWordを左論理シフト |
PSLLD | パックされたDwordを左論理シフト |
PSLLQ | パックされたQwordを左論理シフト |
PSRLW | パックされたWordを右論理シフト |
PSRLD | パックされたDwordを右論理シフト |
PSRLQ | パックされたQwordを右論理シフト |
PSRAW | パックされたWordを右算術シフト |
PSRAD | パックされたDwordを右算術シフト |
5.4.7 MMX State Management Instructions
EMMS命令はMMXレジスタのMMX状態をクリアします。
命令 | 説明 |
---|---|
EMMS | MMX状態をクリア |
5.5 SSE INSTRUCTIONS
SSE命令はIntel64とIA-32プロセッサがSSE拡張をサポートしている場合のみ実行できます。サポートされているかはCPUID命令で検出することが出来ます。
5.5.1 SSE SIMD Single-Precision Floating-Point Instructions
この命令は、XMMレジスタと/またはメモリに格納されたパックされたあるいはスカラ単精度浮動小数点を操作します このサブグループは、データ転送、パックされた算術演算、比較、論理、シャッフルとアンパック、および変換命令の下位サブグループにさらに分割されます。
5.5.1.1 SSE Data Transfer Instructions
SSEデータ転送命令はXMMレジスタ間あるいはXMMレジスタとメモリ間に対してパックされたスカラ単精度浮動小数点を移動します。
命令 | 説明 |
---|---|
MOVAPS | 4つの整列されたパックされた単精度浮動小数点をXMMレジスタ間あるいはXMMレジスタとメモリ間で移動 |
MOVUPS | 4つの整列されていないパックされた単精度浮動小数点をXMMレジスタ間あるいはXMMレジスタとメモリ間で移動 |
MOVHPS | 2つの整列されたパックされた単精度浮動小数点を上位QwordのXMMレジスタとメモリ間で移動 |
MOVHLPS | 2つの整列されたパックされた単精度浮動小数点を上位QwordのXMMレジスタと下位QwordのもうひとつのXMMレジスタ間で移動 |
MOVLPS | 2つの整列されたパックされた単精度浮動小数点を下位QwordのXMMレジスタとメモリ間で移動 |
MOVLHPS | 2つの整列されたパックされた単精度浮動小数点を下位QwordのXMMレジスタと上位QwordのもうひとつのXMMレジスタ間で移動 |
MOVMSKPS | 4つのパックされた単精度浮動小数点からsigh maskを抽出 |
MOVSS | スカラ単精度浮動小数点をXMMレジスタ間あるいはXMMレジスタとメモリ間で移動 |
5.5.1.2 SSE Packed Arithmetic Instructions
パックされたSSE算術命令はパックされたあるいはスカラ単精度浮動小数点オペランドをパックされたスカラ算術演算します。
命令 | 説明 |
---|---|
ADDPS | パックされた単精度浮動小数点を加算 |
ADDSS | スカラ単精度浮動小数点を加算 |
SUBPS | パックされた単精度浮動小数点を減算 |
SUBSS | スカラ単精度浮動小数点を減算 |
MULPS | パックされた単精度浮動小数点を乗算 |
MULSS | スカラ単精度浮動小数点を乗算 |
DIVPS | パックされた単精度浮動小数点を除算 |
DIVSS | スカラ単精度浮動小数点を除算 |
RCPPS | パックされた単精度浮動小数点値の逆数を計算 |
RCPSS | スカラ単精度浮動小数点値の逆数を計算 |
SQRTPS | パックされた単精度浮動小数点値の平方根を計算 |
SQRTSS | スカラ単精度浮動小数点値の平方根を計算 |
RSQRTPS | パックされた単精度浮動小数点値の平方根の逆数を計算 |
RSQRTSS | スカラ単精度浮動小数点値の平方根の逆数を計算 |
MAXPS | パックされた単精度浮動小数点値の最大値を計算 |
MAXSS | スカラ単精度浮動小数点値の最大値を計算 |
MINPS | パックされた単精度浮動小数点値の最小値を計算 |
MAXSS | スカラ単精度浮動小数点値の最小値を計算 |
5.5.1.3 SSE Comparison Instructions
SSE比較命令はパックされたあるいはスカラ単精度浮動小数点オペランドを比較します。
命令 | 説明 |
---|---|
CMPPS | パックされた単精度浮動小数点を比較 |
CMPSS | スカラ単精度浮動小数点を比較 |
COMISS | スカラ単精度浮動小数点値の順序付けられた比較を実行し、EFLAGSレジスタのフラグを設定 |
UCOMISS | スカラ単精度浮動小数点値の順序付けずに比較を実行し、EFLAGSレジスタのフラグを設定 |
5.5.1.4 SSE Logical Instructions
SSE論理命令はパックされた単精度浮動小数点オペランドをbit毎にAND, AND NOT, OR, XOR操作します。
命令 | 説明 |
---|---|
ANDPS | パックされた単精度浮動小数点をbit毎にAND |
ANDNPS | パックされた単精度浮動小数点をbit毎にNOt AND |
ORPS | パックされた単精度浮動小数点をbit毎にOR |
XORPS | パックされた単精度浮動小数点をbit毎にXOR |
5.5.1.5 SSE Shuffle and Unpack Instructions
SSEシャッフルとアンパック命令はパックされた単精度浮動小数点オペランドをシャッフルまたはインターリーブします。
命令 | 説明 |
---|---|
SHUFPS | パックされた単精度浮動小数点オペランドの値をシャッフル |
UNPCKHPS | 2つの単精度浮動小数点オペランドから2つの上位の値をアンパックしインターリーブ |
UNPCKLPS | 2つの単精度浮動小数点オペランドから2つの下位の値をアンパックしインターリーブ |
5.5.1.6 SSE Conversion Instructions
SSEへんかん命令はパックされたあるいはスカラ単精度浮動小数点のパックされたあるいは個々のDword整数を変換します。
命令 | 説明 |
---|---|
CVTPI2PS | パックされたDword整数をパックされた単精度浮動小数点に変換 |
CVTSI2SS | Dword整数をスカラ単精度浮動小数点に変換 |
CVTPS2PI | パックされた単精度浮動小数点をパックされたDword整数に変換 |
CVTTPS2PI | パックされた単精度浮動小数点値をパックされたDword整数に変換 |
CVTSS2SI | スカラ単精度浮動小数点をDword整数に変換 |
CVTTSS2SI | スカラDword整数をスカラ単精度浮動小数点に変換 |
5.5.2 SSE MXCSR State Management Instructions
MXCSR状態管理命令はMXCSR制御と状態レジスタの状態を保存あるいはリストアします。
命令 | 説明 |
---|---|
LDMXCSR | MXCSRレジスタの読み込み |
STMXCSR | MXCSRレジスタの状態を保存 |
5.5.3 SSE 64-Bit SIMD Integer Instructions
SSE 64bit SIMD整数命令はMMXレジスタに含まれるパックされたByte、Word、Dwordに対して追加の操作を実行します。
命令 | 説明 |
---|---|
PAVGB | パックされた符号なしByte整数の平均を計算 |
PAVGW | パックされた符号なしWord整数の平均を計算 |
PEXTRW | Word抽出 |
PINSRW | Word挿入 |
PMAXUB | パックされた符号なしByte整数の最大値を取得 |
PMAXSW | パックされた符号付きWord整数の最大値を取得 |
PMINUB | パックされた符号なしByte整数の最小値を取得 |
PMINSW | パックされた符号付きWord整数の最小値を取得 |
PMOVMSKB | Byteマスクを移動 |
PMULHUW | パックされた符号なし整数を乗算し、上位の結果をリストア |
PSADBW | 絶対差の合計を計算 |
PHUFW | MMXレジスタのパックされたWord整数をシャッフル |
5.5.4 SSE Cacheability Control, Prefetch, and Instruction Ordering Instructions
キャッシュ制御命令はMMXおよびXMMレジスタからメモリにデータを格納するときに、non-temporalデータのキャッシングを制御します。
命令 | 説明 |
---|---|
MASKMOVQ | メモリの中のMMXレジスタから選択されたByteをnon-temporalストア |
MOVNTQ | メモリの中のMMXレジスタから選択されたQwordをnon-temporalストア |
MOVNTPS | メモリの中のXMMレジスタから4つのパックされた単精度浮動小数点をnon-temporalストア |
PREFETCHh | メモリから32bit以上のByteをキャッシュ階層の選択されたレベルへ読み込み |
SFENCE | シリアライズストア操作 |
とりあえずまだまだ命令がありますが、必要なときに参照するということで6章まで省略します。
CHAPTER 6 PROCEDURE CALLS, INTERRUPTS, AND EXCEPTIONS
この章ではプロシージャまたはサブルーチンを呼び出すためのIntel64とIA-32の機能について説明します。さらにアプリケーションプログラマの視点で割り込みや例外処理の方法についても説明します。
6.1 PROCEDURE CALL TYPES
プロセッサは2つのプロシージャコールをサポートします。
- CALLとRET命令
- CALLとRET命令と組み合わせて使うENTERとLEAVE命令
これら2つはプロシージャのスタックを使ってプロシージャを呼び出します。一般的にスタックと呼ばれるこの機能はプロシージャの呼び出し状態を保存したり、呼びだされたプロシージャの引数を渡したり、現在実行中のプロシージャのローカル変数をストアしたりします。
6.2 STACK
スタックは連続したメモリ位置の配列です。これにはSSレジスタのセグメントセレクタによるセグメントと認識も含まれます。フラットメモリモデルを使用している場合、スタックはプログラムをリニアアドレススペースでどこでも見つけることが出来ます。スタックはセグメント最大サイズを最大4GBの長さまで確保できます。
要素はPUSH命令を使用すればスタックに積むことができ、POP命令を使用すればスタックから取り出すことができます。スタックに要素をpushしたとき、プロセッサはESPレジスタをデクリメントし、新しいスタックの先頭に要素を書き込みます。またスタックから要素をpopしたときは、プロセッサはスタックの先頭から要素を読みだし、ESPレジスタをインクリメントします。この方法は要素をスタックにpushした時にスタックのメモリが(より下位のアドレスに向かって)成長し、要素をスタックからpopした時はスタックのメモリを(より上位のアドレスに向かって)縮小させます。
プログラムまたはOS/実行環境は多くのスタックを用意します。例えば、マルチタスクシステムではそれぞれのタスクには個々のスタックが与えられます。スタックの数は最大セグメントと物理メモリの利用可能範囲によるシステムの限度で決定されます。
多くのスタックをシステムに用意した時、一度に一つのスタック(カレントスタック)のみ利用できます。カレントスタックはSSレジスタと呼ばれるセグメントに含まれます。
プロセッサは全てのスタック操作からSSレジスタを自動的に参照します。例えば、メモリアドレスとしてESPレジスタが使用された時は、自動的にカレントスタックのアドレスのポインタを自動的に参照します。さらに、カレントスタックに対してCALL, RET, PUSH, POP, ENTER, LEAVE命令の全てを実行できます。
6.2.1 Setting Up a Stack
スタックを用意し、カレントスタックとして確立するときに、プログラムまたはOS/実行環境では次のことを行わなければなりません。
- スタックセグメントの確立
- MOV, POPまたはLSS命令を使用してスタックセグメントを認識するためのセグメントセレクタをSSレジスタに読み込み
- MOV, POP, LSS命令を使用してスタックのスタックポインタをESPレジスタに読み込み。LSS命令を使用してSSレジスタとESPレジスタを一度に読み込むことができます。
6.2.2 Stack Alignment
スタックセグメントのスタックポインタはスタックセグメント幅によって16bit(Word)または32bit(Dword)境界で整列されているべきです。カレントコードセグメントのセグメントデスクリプタのDフラグはスタックセグメント幅をセットします。PUSHとPOP命令はそれぞれの命令でスタックポインタをデクリメントまたはインクリメントする際にDフラグを使用します。スタック幅が16bitのとき、スタックポインタは16bitでインクリメントあるいはデクリメントされます。同様に32bitのときは32bitでインクリメントあるいはデクリメントされます。32bit幅のスタックに16bit値をpushすると、スタックの位置がずれることがあります(つまりスタックポインタはDword境界で整列されていないということ)。このルールの例外は32bit幅スタックにセグメントレジスタ(16bitセグメントセレクタ)の内容をプッシュしたときです。このとき、プロセッサは次の32bit境界へスタックポインタを自動的に整列します。
プロセッサはスタックポインタの整列をチェックしません。スタックポインタの適切な整列を維持するのは、プロセッサ上で実行されるプログラム、タスク、およびシステムプロシージャの責任です。スタックポインタが整列されていないと深刻なパフォーマンス低下やプログラムの失敗の原因になります。
6.2.3 Address-Size Attributes for Stack Accesses
(PUSHやPOP命令などの)スタックで使用される命令は暗黙に16bitまたは32bitそれぞれ2つのアドレスサイズ属性を持っています。これはスタックの先頭が暗黙のアドレスと明示的なメモリアドレスを持つことがあるためです(たとえばPUSH Array1[EBX])。明示的なアドレスの属性はカレントコードセグメントのDフラグと67Hアドレスサイズプレフィックスの有無で決定されます。
スタックの先頭アドレスサイズ属性は、SPまたはESPがスタックアクセスに使用されるかどうかを決定します。アドレスサイズ属性が16のスタック操作は16bitSPスタックポインタレジスタを使用し、最大スタックアドレスはFFFFHです。アドレスサイズ属性が32のスタック操作は32bitESPスタックポインタレジスタを使用し、最大スタックアドレスはFFFFFFFFHです。スタックとして使用されるデータセグメントのデフォルトアドレスサイズ属性はセグメントデスクリプタのBフラグによって制御されます。このフラグがクリアの時はデフォルトアドレス属性は16で、セットされているときは32になります。
6.2.4 Procedure Linking Information
プロセッサはプロシージャをリンクするための2つのポインタ(スタックフレームベースポインタとリターン命令ポインタ)を提供します。標準なソフトウェアプロシージャ呼び出しの組み合わせを使用するとき、これらのポインタはプロシージャのリンクするための信頼性と一貫性を提供します。
6.2.4.1 Stack-Frame Base Pointer
スタックは一般的にフレームの中で分割されています。それぞれのスタックフレームはローカル変数の確保、他のプロシージャへの引数渡し、プロシージャへ情報をリンクすることができます。スタックフレームベースポインタ(EBPレジスタに含まれる)はプロシージャを呼び出すためのスタックフレーム内の固定された参照ポインタを識別します。スタックフレームベースポインタを使用するには、呼び出されたプロシージャは通常スタック上のローカル変数をプッシュする前に、ESPレジスタの内容をEBPレジスタにコピーします。スタックフレームベースポインタは、スタックに渡されたデータ構造、リターン命令ポインタ、呼び出されたプロシージャによってスタックに追加されたローカル変数に簡単にアクセスできるようにします。EBPレジスタは、ESPレジスタと同様に、現在のスタックセグメント内のアドレス(つまり、SSレジスタの現在の内容で指定されたセグメント)を自動的に指し示します。
6.2.4.2 Return Instruction Pointer
呼び出されたプロシージャの最初の命令に移る前に、CALL命令はカレントスタック上にEIP命令のアドレスをpushします。このアドレスは、リターン命令ポインタと呼ばれ、呼び出されたプロシージャからの復帰後に呼び出し元のプロシージャの実行を再開する命令を指します。呼びだされたプロシージャから戻るときに、RET命令はスタックの後ろからリターン命令ポインタをpopしEIPレジスタのなかに戻します。その後呼び出し元のプロシージャの実行が再開されます。
プロセッサはリターン命令ポインタが呼び出し元プロシージャを指し示している必要はありません。RET命令を実行する前に、リターン命令ポインタはカレントコードセグメント(near return)または他のコードセグメント(far return)アドレスをソフトウェアが操作することが出来ます。しかし、このような操作を明確に定義されたコードエントリポイントを使ってのみで非常に慎重に実行すべきです。
6.2.5 Stack Behavior in 64-Bit Mode
64bitモードでは、SSセグメントを参照するアドレス計算は、セグメントベースがゼロであるかのように扱われます。セグメントデスクリプタレジスタの領域(ベース、リミット、属性)は無視されます。SS DPLは常にCPLに等しくなるように変更されます。これは、変更されたSSディスクリプタ内の唯一のフィールドであっても当てはまります。
ESP, EIP, EBPレジスタはそれぞれ64bitのRSP, RIP, RBPに名前が変更されます。セグメントを読み込むための命令の一部の形式は無効です(LDS、POP ESなど)。
PUSH/POP命令は64bit幅でスタックをインクリメント/デクリメントされます。セグメントレジスタの内容を64bitスタックにpushするときに、ポインタは自動的に(32ビット幅のスタックと同様に)64bitに整列されます。
6.3 CALLING PROCEDURES USING CALL AND RET
CALL命令はカレントコードセグメント(near call)あるいは異なるコードセグメント(far call)内でプロシージャへの制御を転送するのを許可します。near return(あれ、far callではなく?)は通常OSプロシージャあるいは異なるタスクのプロシージャにアクセスするために使用されます。
さらにRET命令はCALL命令のnearのバージョンあるいはfarのバージョンがnear returnあるいはfar returnと一致することも可能にします。加えてRET命令はスタックからパラメータを解放するためのリターンでスタックポインタをインクリメントすることをプログラムに許可します。スタックから解放するためのByteの数はRET命令の任意の引数によって決定されます。
6.3.1 Near CALL and RET Operation
near callを実行するとき、プロセッサは以下に従います。
- スタックに現在のEIPレジスタの値をpush
- 呼びだされたプロシージャのオフセットをEIPレジスタに読み込み
- 呼びだされたプロシージャの実行を開始
near returnを実行したとき、プロセッサは以下を実行します。
- EIPレジスタにスタックの先頭の値(リターン命令ポインタ)をpop
- RET命令に任意のnの引数が指定されていた場合、スタックからパラメータを解放するためにnオペランドで指定されたByteの数のインクリメントをスタックポインタに対して実行
- 呼び出し元の実行を再開
6.3.2 Far CALL and RET Operation
far callを実行したときは、プロセッサは以下を実行します。
- スタックに現在のCSレジスタの値をpush
- スタックに現在のEIPレジスタの値をpush
- 呼びだされたプロシージャに含まれるセグメントのセグメントセレクタをCSレジスタに読み込み
- 呼び出されたプロシージャのオフセットをEIPレジスタに読み込み
- 呼びだされたプロシージャを実行
far returnを実行したときは、プロセッサは以下に従います。
- EIPレジスタにスタックの先頭の値(リターン命令ポインタ)をpop
- CSレジスタにスタックの先頭の値(返されるコードセグメントのセグメントセレクタ)をpop
- RET命令に任意のnの引数が指定されていた場合、スタックからパラメータを解放するためにnオペランドで指定されたByteの数のインクリメントをスタックポインタに対して実行
- 呼び出し元の実行を再開
6.3.3 Parameter Passing
パラメータはプロシージャ間で3つの方法(汎用レジスタ、引数リスト、スタック)を使って渡されます。
6.3.3.1 Passing Parameters Through the General-Purpose Registers
プロセッサはプロシージャ呼び出しで汎用レジスタの状態を保存しません。したがって、呼び出しプロシージャは、CALL命令を実行する前に、パラメータをこれらのレジスタ(ESPおよびEBPレジスタを除く)のいずれかにコピーすることによって、呼び出されたプロシージャに対して最大6つのパラメータを渡すことができます。 呼び出されたプロシージャは、同様に、汎用レジスタを介してパラメータを呼び出し側のプロシージャに戻すことができます。
6.3.3.2 Passing Parameters on the Stack
呼び出されたプロシージャに多数のパラメータを渡すには、パラメータを呼び出しプロシージャのスタックフレーム内のスタックに配置できます。 このとき、(EBPレジスタ内の)スタックフレームベースポインタを使用して、パラメータに簡単にアクセスできるようにフレーム境界を設定すると便利です。
さらにスタックは呼びだされたプロシージャから呼びだし元のプロシージャへパラメータを渡すことも出来ます。
6.3.3.3 Passing Parameters in an Argument List
多数のパラメータ(またはデータ構造)を呼び出されたプロシージャに渡す別の方法は、メモリ内のデータセグメントの1つの引数リストにパラメータを配置することです。 引数リストへのポインタは、汎用レジスタまたはスタックを介して呼び出されたプロシージャに渡すことができます。 同じ方法でパラメータを呼び出し側のプロシージャに戻すこともできます。
6.3.4 Saving Procedure State Information
プロセッサはプロシージャ呼び出しで汎用レジスタ、セグメントレジスタ、EFLAGSレジスタの内容を保存しません。呼び出し元のプロシージャは、戻り後に実行を再開するときに必要となる汎用レジスタの値を明示的に保存する必要があります。
PUSHAとPOPA命令は汎用レジスタの内容を保存、リストアが容易に出来ます。PUSHAはEAX, ECX, EDX, EBX, ESP(PUSHA命令を実行する前の値), EBP, ESI, EDIをスタックにpushします。POPAはこれらのレジスタをpopします。
呼び出されたプロシージャがセグメントレジスタのいずれかの状態を明示的に変更する場合は、呼び出し元のプロシージャへの戻り値を実行する前に、以前の値に復元する必要があります。
呼び出し側のプロシージャがEFLAGSレジスタの状態を維持する必要がある場合は、PUSHF/PUSHFDおよびPOPF/POPFD命令を使用してレジスタの全部または一部を保存および復元できます。PUSHF命令はスタック上のEFLAGSレジスタの下位Wordをpushし、PUSHFD命令はレジスタ全体をプッシュします。POPF命令は、スタックからEFLAGSレジスタの下位Wordをポップし、POPFD命令はスタックからレジスタにダブルワードをポップします。
6.3.5 Calls to Other Privilege Levels
IA-32保護メカニズムは大きい数ほど権限が低い4つの特権レベル(0-3)を認識します。特権レベルを使用する理由は、オペレーティングシステムの信頼性を向上させるためです。
この例では、システムの中で最も重要なコードモジュール(通常はオペレーティングシステムのカーネル)を含むセグメントに対して、最も高い特権レベル0(図の中央)が使用されます。重要度の低いソフトウェア用のコードモジュールを含むセグメントには、外側のリング(特権が徐々に低くなります)が使用されます。
低特権セグメントのコードモジュールは、ゲートと呼ばれる厳重に制御され保護されたインターフェイスによって、上位特権セグメントで動作するモジュールにのみアクセスできます。保護ゲートを通過せずに十分なアクセス権を持たずに上位の特権セグメントにアクセスしようとすると、一般保護例外(#GP)が生成されます。
もしOS/実行環境でマルチ保護メカニズムを使用する場合、呼び出したプロシージャの多くの特権保護レベルの処理はfar callに似ています。
違いを以下に示します。
- アクセス権情報
- 呼び出されたプロシージャのコードセグメントのセグメントセレクタ
- コードセグメントへのオフセット(つまり、呼び出されたプロシージャの命令ポインタ)
6.3.6 CALL and RET Operation Between Privilege Levels
より特権の高い保護レベルを呼び出すと、プロセッサは次の処理を実行します。
- アクセス権チェック(特権チェック)を実行します。
- SS、ESP、CS、EIPレジスタの現在の内容を一時的に(内部的に)保存します。
- 新しいスタック(つまり特権レベルで呼ばれるためのスタック)のセグメントセレクタとスタックポインタをTSSからSSレジスタとESPレジスタに読み込み、新しいスタックに切り替えます。
- 呼び出したプロシージャのスタックの一時的に保存されたSSとESP値を新しいスタックにプッシュします。
- 呼び出し元のプロシージャのスタックから新しいスタックにパラメータをコピーします。 コールゲートディスクリプタの値によって、新しいスタックにコピーするパラメータの数が決まります。
- 呼び出したプロシージャの一時的に保存されたCSおよびEIP値を新しいスタックにプッシュします。
- 新しいコードセグメントのセグメントセレクタと新しい命令ポインタをコールゲートからCSレジスタとEIPレジスタにそれぞれロードします。
- 新しい特権レベルで呼び出されたプロシージャの実行を開始します。
特権付きプロシージャからのリターンを実行するとき、プロセッサは次のアクションを実行します。
- 特権チェックを実行します。
- CSおよびEIPレジスタを呼び出し前の値に復元します。
- RET命令にオプションのn引数がある場合は、nオペランドで指定されたバイト数だけスタックポインタをインクリメントして、スタックからパラメータを解放します。 コールゲートディスクリプタが1つ以上のパラメータをあるスタックから他のスタックにコピーするように指定する場合は、両方のスタックからパラメータを解放するためにRET n命令を使用する必要があります。 ここで、nオペランドは、各スタックで占有されているバイト数をパラメータで指定します。 戻り時に、プロセッサは、各スタックのESPをnずつ増やして、スタックからこれらのパラメータをステップオーバー(効果的に削除)します。
- 呼び出し前にSSレジスタとESPレジスタを復元し、呼び出し元のプロシージャのスタックに入れ替えます。
- RET命令にオプションのn引数がある場合は、nオペランドで指定されたバイト数だけスタックポインタをインクリメントして、スタックからパラメータを解放します(手順3の説明を参照)。
- 呼び出し元プロシージャの実行を再開します。
6.3.7 Branch Functions in 64-Bit Mode
64bitの拡張は、分岐メカニズムを拡張して、64bitのリニアアドレススペースの分岐に対応します。
-
near branch semanticsは64bitモードで再定義されています
-
64bitモードと互換モードでは、far call用の64bitコールゲートディスクリプタを使用できます。
64bitモードでは、すべてのnear branch(CALL、RET、JCC、JCXZ、JMP、およびLOOP)のオペランドサイズは64bitに強制されます。
これらの命令は、REXオペランドサイズプレフィックスを必要とせずに64bit RIPを更新します。
near branchの次の側面は、有効オペランドサイズによって制御されます。 -
命令ポインタのサイズの切り捨て
-
CALLまたはRETによるスタックポップまたはプッシュのサイズ
-
CALLまたはRETによるスタックポインタの増分または減分のサイズ
-
間接分岐オペランドサイズ
(以下省略)
6.4 INTERRUPTS AND EXCEPTIONS
プロセッサは割り込みと例外の2つのメカニズムを提供します。
- 割り込みは、I / Oデバイスによって通常トリガされる非同期イベントです。
- 例外は、プロセッサが命令を実行している間に1つまたは複数の事前定義された条件を検出したときに生成される同期イベントです。 IA-32アーキテクチャでは、フォールト、トラップ、アボートの3種類の例外が指定されています。
プロセッサは基本的に同じ方法で割り込みと例外に応答します。 割り込みまたは例外が通知されると、プロセッサは現在のプログラムまたはタスクの実行を停止し、割り込みまたは例外条件を処理するために特別に書き込まれたハンドラプロシージャに切り替えます。 プロセッサは、割り込みディスクリプタテーブル(IDT)のエントリを介してハンドラプロシージャにアクセスします。 ハンドラが割り込みまたは例外の処理を完了すると、中断されたプログラムまたはタスクにプログラム制御が戻されます。
OS、実行環境、デバイスドライバは、通常、アプリケーションプログラムまたはタスクとは独立して、割り込みおよび例外を処理します。 ただし、アプリケーションプログラムは、アセンブリ言語の呼び出しを介して、OSまたは実行環境に組み込まれている割り込みハンドラおよび例外ハンドラにアクセスできます。 このセクションの残りの部分では、プロセッサの割り込みと例外処理のメカニズムについて簡単に説明します。
6.4.1 Call and Return Operation for Interrupt or Exception Handling Procedures
割り込みまたは例外ハンドラプロシージャの呼び出しはプロシージャの他の保護レベルの呼び出しに似ています。
このときベクトルはIDTの2種類のゲート(割り込みゲートとトラップゲート)のうち一つを参照します。割り込みあるいはトラップゲートは次の情報を提供します。
- アクセス権情報
- ハンドラプロシージャのコードセグメントのセグメントセレクタ
- ハンドラプロシージャのはじめの命令のコードセグメントへのオフセット
割り込みゲートとトラップゲートの違いは次のとおりです。割り込みまたは例外ハンドラが割り込みゲートを通じて呼び出された場合、プロセッサは、EFLAGSレジスタの割り込みイネーブルフラグ(IF)をクリアして、後続の割り込みがハンドラの実行を妨害しないようにします。ハンドラがトラップゲートを介して呼び出されると、IFフラグの状態は変更されません。
ハンドラプロシージャのコードセグメントが現在実行中のプログラムまたはタスクと同じ特権レベルを持つ場合、ハンドラプロシージャは現在のスタックを使用します。 ハンドラがより特権レベルで実行される場合、プロセッサはハンドラの特権レベルのスタックに切り替えます。
スタック切り替えが発生しない場合、プロセッサは割り込みまたは例外ハンドラを呼び出すときに次の処理を行います。
- EFLAGS、CS、EIPレジスタの現在の内容を(その順序で)スタックにpush
- (該当する場合)エラーコードをスタックにpush
- 新しいコードセグメント用のセグメントセレクタと(割り込みゲートまたはトラップゲートから)新しい命令ポインタをそれぞれCSレジスタとEIPレジスタに読み込み
- 呼び出しが割り込みゲートを介している場合は、EFLAGSレジスタのIFフラグをクリア
- ハンドラプロシージャの実行を開始
スタック切り替えが発生する場合、次の処理を行います。
- SS、ESP、EFLAGS、CS、EIPレジスタの現在の内容を一時的に(内部的に)保存します。
- 新しいスタック(つまり、呼び出される特権レベルのスタック)のセグメントセレクタとスタックポインタをTSSからSSレジスタとESPレジスタに読み込み、新しいスタックに切り替えます。
- 中断されたプロシージャのスタックの一時的に保存されたSS、ESP、EFLAGS、CS、EIPの値を新しいスタックにpush
- 新しいスタックに(該当する場合)エラーコードをpush
- 新しいコードセグメント用のセグメントセレクタと(割り込みゲートまたはトラップゲートから)新しい命令ポインタをそれぞれCSレジスタとEIPレジスタに読み込み
- 呼び出しが割り込みゲートを介している場合は、EFLAGSレジスタのIFフラグをクリア
- 新しい特権レベルでハンドラプロシージャの実行を開始
割り込みまたは例外ハンドラからのリターンは、IRET命令で開始されます。 IRET命令は、中断されたプロシージャのEFLAGSレジスタの内容も復元する点を除いて、far RET命令に似ています。 割り込まれたプロシージャと同じ特権レベルから割り込みまたは例外ハンドラからのリターンを実行するとき、プロセッサは次のアクションを実行します。
- 割込みまたは例外の前のCSおよびEIPレジスタの値に復元
- EFLAGSレジスタを復元
- スタックポインタを適切にインクリメント
- 中断されたプロシージャの実行を再開
異なる特権レベルから戻るさいは以下を実行します。
- 特権レベルのチェック
- 割込みまたは例外の前のCSおよびEIPレジスタの値に復元
- EFLAGSレジスタを復元
- 割り込みまたは例外の前のSSおよびESPレジスタの値に復元し、スタックの切り替えで中断されたプロシージャのスタックも復元
- 中断されたプロシージャの実行を再開
6.4.2 Calls to Interrupt or Exception Handler Tasks
割り込みハンドラと例外ハンドラルーチンは別々のタスクで実行することもできます。 このとき、割り込みまたは例外によって、タスクがハンドラタスクに切り替わります。ハンドラ・タスクには独自のアドレススペースが与えられ、(オプションで)アプリケーションプログラムやタスクより高い保護レベルで実行できます。
ハンドラタスクへの切り替えは、タスクゲートディスクリプタを参照する暗黙的なタスクコールによって実行されます。
タスクゲートは、ハンドラタスクのためのアドレススペースへのアクセスを提供します。タスクスイッチの一部として、プロセッサは中断されたプログラムまたはタスクに関する完全な状態情報を保存します。ハンドラタスクから戻ると、中断されたプログラムまたはタスクの状態が復元され、実行が継続されます。
6.4.3 Interrupt and Exception Handling in Real-Address Mode
real-addressモードで動作しているとき、プロセッサは、割り込みまたは例外ハンドラへの暗黙の遠隔呼び出しによって割り込みまたは例外に応答します。プロセッサは、割り込みまたは例外ベクトルを割り込みテーブルのインデックスとして使用します。割り込みテーブルには、割り込みハンドラと例外ハンドラプロシージャへの命令ポインタが含まれています。プロセッサは、ハンドラプロシージャに切り替える前に、EFLAGSレジスタ、EIPレジスタ、CSレジスタ、オプションのエラーコードの状態をスタックに保存します。
割り込みまたは例外ハンドラからの復帰は、IRET命令で実行されます。
6.4.4 INT n, INTO, INT3, INT1, and BOUND Instructions
INT n、INTO、INT3、BOUND命令は、プログラムまたはタスクが割り込みまたは例外ハンドラを明示的に呼び出すことを可能にします。INT n命令(オペコードCD)は、引数としてベクトルを使用します。これにより、プログラムは任意の割り込みハンドラを呼び出すことができます。
EFLAGSレジスタのオーバーフローフラグ(OF)がセットされている場合、INTO命令(オペコードCE)はオーバーフロー例外(#OF)ハンドラを明示的に呼び出します。OFフラグは算術命令のオーバーフローを示しますが、自動的にオーバーフロー例外は発生しません。オーバーフロー例外は、次のいずれかの方法でのみ明示的に発生させることができます。
- INTO命令の実行
- フラグがセットされていればOFフラグをテストし、引数が4のINT n命令((オーバーフロー例外のベクトル)を実行
オーバーフロー条件を処理する方法はどちらも、プログラムが命令ストリームの特定の場所でオーバーフローをテストできるようにします。
INT3命令(オペコードCC)は、明示的にブレークポイント例外(#BP)ハンドラを呼び出します。 同様に、INT1命令(オペコードF1)は、明示的にデバッグ例外(#DB)ハンドラを呼び出します。
オペランドがメモリ内のあらかじめ定義された境界内にないことが判明した場合、BOUND命令はBOUND-range exceeded例外(#BR)ハンドラを明示的に呼び出します。 この命令は、配列や他のデータ構造への参照をチェックするために提供されています。オーバーフロー例外と同様に、BOUND-range exceeded例外は、BOUND命令または引数が5のINT n命令(境界チェック例外のベクトル)でのみ明示的に発生させることができます。プロセッサは、暗黙的に境界チェックを実行せず、BOUND-range exceeded例外を発生させません。
6.4.5 Handling Floating-Point Exceptions
(省略)
6.4.6 Interrupt and Exception Behavior in 64-Bit Mode
64bit拡張は、従来のIA-32割り込み処理および例外処理メカニズムを拡張して、64bitのOSおよびアプリケーションをサポートします。
変更点は次のとおりです。
- IDTが指すすべての割り込みハンドラは64bitコードです(SMIハンドラには適用されません)。
- 新しいSSは、CPLに変更がある場合はNULLに設定されます。
- 割り込みスタックプッシュのサイズは64bitに固定されています。プロセッサは、8Byteのゼロ拡張ストアを使用します。
- 割り込み時にスタックポインタ(SS:RSP)が無条件にpushされます。従来の環境では、このpushは条件付きで、現在の特権レベル(CPL)の変更に基づいています。
- IRETの動作が変更
- 新しい割り込みスタックスイッチメカニズムがあります。
- 割り込みスタックフレームの整列が異なります。
6.5 PROCEDURE CALLS FOR BLOCK-STRUCTURED LANGUAGES
IA-32アーキテクチャーは、ENTERおよびLEAVE命令を使用してプロシージャー呼び出しを実行する別の方法をサポートしています。これらの命令は、呼び出されたプロシージャのフレームを自動的に作成して解放します。スタックフレームには、ローカル変数用に事前定義されたスペースと、呼び出されたプロシージャからの一貫した復元を可能にするために必要なポインタがあります。また、スコープルールを実装して、プロシージャが独自のローカル変数やその他のスタックフレームにあるいくつかの変数にアクセスできるようにします。
ENTERとLEAVEには2つの利点があります。
- CやPascalなどのブロック構造言語を実装するための機械語のサポートを提供
- コンパイラ生成コードでプロシージャの入力と終了を簡素化
6.5.1 ENTER Instruction
ENTER命令は一般的なブロック構造言語のルールに互換性があるスタックフレームを作成します。ブロック構造言語では、プロシージャのスコープは、アクセス可能な変数のセットです。
スコープ規則は言語によって異なります。それらは、プロシージャのネスト、プログラムを別々にコンパイルされたファイルに分割すること、または他のいくつかのモジュール化スキームに基づきます。
ENTERには2つのオペランドがあります。最初のものは、呼び出されるプロシージャの動的ストレージ用にスタック上に予約されるバイト数を指定します。動的記憶領域は、プロシージャの呼び出し時に作成される変数(自動変数とも呼ばれます)に割り当てられるメモリです。2番目のパラメータは、プロシージャのレキシカルネストレベル(0〜31)です。ネストレベルは、プロシージャコールの階層内のプロシージャの深さです。レキシカルネストレベルは、保護特権レベルまたは現在実行中のプログラムまたはタスクのI / O特権レベルとは無関係です。
次の例では、ENTERに2Kバイトのダイナミックストレージをスタックに割り当て、この手順のスタックフレーム内の2つ前のスタックフレームへのポインタを設定します。
ENTER 2048,3
レキシカルネストレベルは、前のフレームから新しいスタックフレームにコピーするスタックフレームポインタの数を決定します。スタックフレームポインタは、プロシージャの変数にアクセスするために使用されるDwordです。プロシージャが他のプロシージャの変数にアクセスするために使用するスタックフレームポインタのセットは、Displayと呼ばれます。Displayの最初のDwordは、前のスタックフレームへのポインタです。このポインタは、現在のスタックフレームを破棄してENTER命令の効果を元に戻すLEAVE命令によって使用されます。
ENTER命令は、プロシージャのDisplayを作成した後、ESPレジスタの内容を最初のパラメータで指定されたバイト数だけデクリメントすることによって、プロシージャの動的ローカル変数を割り当てます。
ESPレジスタのこの新しい値は、プロシージャ内のすべてのPUSHおよびPOP操作の初期スタックの先頭として機能します。
プロシージャがそのDisplayをアドレス可能にするために、ENTER命令は、EBPレジスタをDisplayの最初のDwordを指し示したままにします。スタックが減少するので、実際はDisplayの上位アドレスのDwordです。EBPレジスタをベースレジスタとして指定するデータ操作命令は、データセグメントの代わりにスタックセグメント内の位置を自動的にアドレス指定します。
ENTER命令は、ネストされたものとネストされていないものの2つの方法で使用できます。レキシカルネストレベルが0の場合は、ネストされていないフォームが使用されます。 ネストされていない形式は、スタック上のEBPレジスタの内容をpushし、ESPレジスタの内容をEBPレジスタにコピーし、ESPレジスタの内容から最初のオペランドを減算して動的記憶領域を割り当てます。ネストされていないフォームは、スタックフレームポインタがコピーされないという点でネストされたフォームとは異なります。ENTER命令のネストされた形式は、2番目のパラメータ(レキシカルネストレベル)がゼロでない場合に発生します。
次の疑似コードは、ENTER命令の正式な定義を示しています。STORAGEは、ローカル変数に割り当てる動的ストレージのバイト数で、LEVELはレキシカルネストレベルです。
PUSH EBP;
FRAME_PTR ← ESP;
IF LEVEL > 0
THEN
DO (LEVEL − 1) times
EBP ← EBP − 4;
PUSH Pointer(EBP); (* doubleword pointed to by EBP *)
OD;
PUSH FRAME_PTR;
FI;
EBP ← FRAME_PTR;
ESP ← ESP − STORAGE
メインプロシージャ(他のすべてのプロシージャがネストされている)は、最も高いレキシカルレベルのレベル1で動作します。呼び出す最初のプロシージャは、次に深いレキシカルレベルのレベル2で動作します。レベル2プロシージャは、メインプログラムコンパイラによって指定された固定の場所にあります。レベル1の場合、ENTER命令は、コピーする前のDisplayがないため、要求された動的ストレージのみをスタックに割り当てます。
より低いレキシカルネストレベルで別のプロシージャを呼び出すプロシージャは、呼び出されたプロシージャに呼び出し元の変数へのアクセスを与えます。ENTER命令は、呼び出し側のプロシージャのスタックフレームへのポインタをDisplayに配置することによって、このアクセスを提供します。
同じレキシカルネストレベルで別のプロシージャを呼び出すプロシージャは、その変数へのアクセスを許可すべきではありません。この場合、ENTER命令は、Displayのその部分のみを、より高いレキシカルネストレベルで動作する以前にネストされたプロシージャを参照する呼び出しプロシージャからコピーする。新しいスタックフレームには、呼び出し側のプロシージャのスタックフレームをアドレス指定するためのポインタは含まれていません。
ENTER命令は、re-entrantプロシージャを同じレキシカルネストレベルのプロシージャへのコールとして扱います。この場合、re-entrantプロシージャの後続の各反復は、それ自身の変数とそれがネストされているプロシージャの変数のみを扱うことができます。re-entrantプロシージャは、常にそれ自身の変数に対処できます。以前の反復のスタックフレームへのポインタは必要ありません。
より高いレキシカルネストレベルでプロシージャのスタックフレームポインタのみをコピーすることにより、プロシージャは、並列レキシカルレベルのものではなく、より高いレキシカルレベルの変数にアクセスすることが確実になります。
[IDMより掲載]
(以下省略)
6.5.2 LEAVE Instruction
オペランドを持たないLEAVE命令は、前のENTER命令と逆の動作をします。LEAVE命令は、EBPレジスタの内容をESPレジスタにコピーして、プロシージャに割り当てられたすべてのスタック領域を解放します。次に、EBPレジスタの古い値をスタックから復元します。この同時実行は、ESPレジスタを元の値に戻します。その後のRET命令は、プロシージャで使用するために呼び出しプログラムによってスタック上にプッシュされた引数および戻りアドレスをすべて削除できます。
Chapter6まとめ
- スタックについて
- プロシージャの呼び出しについて
- CALL命令とRET命令とその特権レベルについて
- 割り込みと例外について
- ENTER命令とLEAVE命令について
プロセッサの命令の数が多いのに驚きました。命令にも同じような名前があるので使うときに間違えそうです。
Chapter6を読んでいると、いたるところでスタックが使用されています。非常に重要な構造なのだと思いしらされます。あと擬似コードが分かりやすかったです。早くアセンブリ言語に触れたくなりました。