LoginSignup
18
13

More than 5 years have passed since last update.

リバースエンジニアリングへの道 - その10

Last updated at Posted at 2018-07-15

リバースエンジニアリングへの道

出田 守です。
最近、情報セキュリティに興味を持ち、『リバースエンジニアリング-Pythonによるバイナリ解析技法』という本(以降、「教科書」と呼びます)を読みました。
「こんな世界があるのか!かっこいい!」と感動し、私も触れてみたいということでド素人からリバースエンジニアリングができるまでを書いていきたいと思います。
ちなみに、教科書ではPython言語が使用されているので私もPython言語を使用しています。
ここを見ていただいた諸先輩方からの意見をお待ちしております。

軌跡

リバースエンジニアリングへの道 - その9

環境

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命令の全てを実行できます。

[IDMより掲載]
image.png

6.2.1 Setting Up a Stack

スタックを用意し、カレントスタックとして確立するときに、プログラムまたはOS/実行環境では次のことを行わなければなりません。

  1. スタックセグメントの確立
  2. MOV, POPまたはLSS命令を使用してスタックセグメントを認識するためのセグメントセレクタをSSレジスタに読み込み
  3. 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を実行するとき、プロセッサは以下に従います。

  1. スタックに現在のEIPレジスタの値をpush
  2. 呼びだされたプロシージャのオフセットをEIPレジスタに読み込み
  3. 呼びだされたプロシージャの実行を開始

near returnを実行したとき、プロセッサは以下を実行します。

  1. EIPレジスタにスタックの先頭の値(リターン命令ポインタ)をpop
  2. RET命令に任意のnの引数が指定されていた場合、スタックからパラメータを解放するためにnオペランドで指定されたByteの数のインクリメントをスタックポインタに対して実行
  3. 呼び出し元の実行を再開

6.3.2 Far CALL and RET Operation

far callを実行したときは、プロセッサは以下を実行します。

  1. スタックに現在のCSレジスタの値をpush
  2. スタックに現在のEIPレジスタの値をpush
  3. 呼びだされたプロシージャに含まれるセグメントのセグメントセレクタをCSレジスタに読み込み
  4. 呼び出されたプロシージャのオフセットをEIPレジスタに読み込み
  5. 呼びだされたプロシージャを実行

far returnを実行したときは、プロセッサは以下に従います。

  1. EIPレジスタにスタックの先頭の値(リターン命令ポインタ)をpop
  2. CSレジスタにスタックの先頭の値(返されるコードセグメントのセグメントセレクタ)をpop
  3. RET命令に任意のnの引数が指定されていた場合、スタックからパラメータを解放するためにnオペランドで指定されたByteの数のインクリメントをスタックポインタに対して実行
  4. 呼び出し元の実行を再開

[IDMより掲載]
image.png

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に似ています。
違いを以下に示します。

  • アクセス権情報
  • 呼び出されたプロシージャのコードセグメントのセグメントセレクタ
  • コードセグメントへのオフセット(つまり、呼び出されたプロシージャの命令ポインタ)

[IDMより掲載]
image.png

6.3.6 CALL and RET Operation Between Privilege Levels

より特権の高い保護レベルを呼び出すと、プロセッサは次の処理を実行します。

  1. アクセス権チェック(特権チェック)を実行します。
  2. SS、ESP、CS、EIPレジスタの現在の内容を一時的に(内部的に)保存します。
  3. 新しいスタック(つまり特権レベルで呼ばれるためのスタック)のセグメントセレクタとスタックポインタをTSSからSSレジスタとESPレジスタに読み込み、新しいスタックに切り替えます。
  4. 呼び出したプロシージャのスタックの一時的に保存されたSSとESP値を新しいスタックにプッシュします。
  5. 呼び出し元のプロシージャのスタックから新しいスタックにパラメータをコピーします。 コールゲートディスクリプタの値によって、新しいスタックにコピーするパラメータの数が決まります。
  6. 呼び出したプロシージャの一時的に保存されたCSおよびEIP値を新しいスタックにプッシュします。
  7. 新しいコードセグメントのセグメントセレクタと新しい命令ポインタをコールゲートからCSレジスタとEIPレジスタにそれぞれロードします。
  8. 新しい特権レベルで呼び出されたプロシージャの実行を開始します。

特権付きプロシージャからのリターンを実行するとき、プロセッサは次のアクションを実行します。

  1. 特権チェックを実行します。
  2. CSおよびEIPレジスタを呼び出し前の値に復元します。
  3. RET命令にオプションのn引数がある場合は、nオペランドで指定されたバイト数だけスタックポインタをインクリメントして、スタックからパラメータを解放します。 コールゲートディスクリプタが1つ以上のパラメータをあるスタックから他のスタックにコピーするように指定する場合は、両方のスタックからパラメータを解放するためにRET n命令を使用する必要があります。 ここで、nオペランドは、各スタックで占有されているバイト数をパラメータで指定します。 戻り時に、プロセッサは、各スタックのESPをnずつ増やして、スタックからこれらのパラメータをステップオーバー(効果的に削除)します。
  4. 呼び出し前にSSレジスタとESPレジスタを復元し、呼び出し元のプロシージャのスタックに入れ替えます。
  5. RET命令にオプションのn引数がある場合は、nオペランドで指定されたバイト数だけスタックポインタをインクリメントして、スタックからパラメータを解放します(手順3の説明を参照)。
  6. 呼び出し元プロシージャの実行を再開します。

[IDMより掲載]
image.png

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フラグの状態は変更されません。
ハンドラプロシージャのコードセグメントが現在実行中のプログラムまたはタスクと同じ特権レベルを持つ場合、ハンドラプロシージャは現在のスタックを使用します。 ハンドラがより特権レベルで実行される場合、プロセッサはハンドラの特権レベルのスタックに切り替えます。
スタック切り替えが発生しない場合、プロセッサは割り込みまたは例外ハンドラを呼び出すときに次の処理を行います。

[IDMより掲載]
image.png

image.png

  1. EFLAGS、CS、EIPレジスタの現在の内容を(その順序で)スタックにpush
  2. (該当する場合)エラーコードをスタックにpush
  3. 新しいコードセグメント用のセグメントセレクタと(割り込みゲートまたはトラップゲートから)新しい命令ポインタをそれぞれCSレジスタとEIPレジスタに読み込み
  4. 呼び出しが割り込みゲートを介している場合は、EFLAGSレジスタのIFフラグをクリア
  5. ハンドラプロシージャの実行を開始

[IDMより掲載]
image.png

スタック切り替えが発生する場合、次の処理を行います。

  1. SS、ESP、EFLAGS、CS、EIPレジスタの現在の内容を一時的に(内部的に)保存します。
  2. 新しいスタック(つまり、呼び出される特権レベルのスタック)のセグメントセレクタとスタックポインタをTSSからSSレジスタとESPレジスタに読み込み、新しいスタックに切り替えます。
  3. 中断されたプロシージャのスタックの一時的に保存されたSS、ESP、EFLAGS、CS、EIPの値を新しいスタックにpush
  4. 新しいスタックに(該当する場合)エラーコードをpush
  5. 新しいコードセグメント用のセグメントセレクタと(割り込みゲートまたはトラップゲートから)新しい命令ポインタをそれぞれCSレジスタとEIPレジスタに読み込み
  6. 呼び出しが割り込みゲートを介している場合は、EFLAGSレジスタのIFフラグをクリア
  7. 新しい特権レベルでハンドラプロシージャの実行を開始

割り込みまたは例外ハンドラからのリターンは、IRET命令で開始されます。 IRET命令は、中断されたプロシージャのEFLAGSレジスタの内容も復元する点を除いて、far RET命令に似ています。 割り込まれたプロシージャと同じ特権レベルから割り込みまたは例外ハンドラからのリターンを実行するとき、プロセッサは次のアクションを実行します。

  1. 割込みまたは例外の前のCSおよびEIPレジスタの値に復元
  2. EFLAGSレジスタを復元
  3. スタックポインタを適切にインクリメント
  4. 中断されたプロシージャの実行を再開

異なる特権レベルから戻るさいは以下を実行します。

  1. 特権レベルのチェック
  2. 割込みまたは例外の前のCSおよびEIPレジスタの値に復元
  3. EFLAGSレジスタを復元
  4. 割り込みまたは例外の前のSSおよびESPレジスタの値に復元し、スタックの切り替えで中断されたプロシージャのスタックも復元
  5. 中断されたプロシージャの実行を再開

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より掲載]
image.png

(以下省略)

6.5.2 LEAVE Instruction

オペランドを持たないLEAVE命令は、前のENTER命令と逆の動作をします。LEAVE命令は、EBPレジスタの内容をESPレジスタにコピーして、プロシージャに割り当てられたすべてのスタック領域を解放します。次に、EBPレジスタの古い値をスタックから復元します。この同時実行は、ESPレジスタを元の値に戻します。その後のRET命令は、プロシージャで使用するために呼び出しプログラムによってスタック上にプッシュされた引数および戻りアドレスをすべて削除できます。

Chapter6まとめ

  1. スタックについて
  2. プロシージャの呼び出しについて
  3. CALL命令とRET命令とその特権レベルについて
  4. 割り込みと例外について
  5. ENTER命令とLEAVE命令について

プロセッサの命令の数が多いのに驚きました。命令にも同じような名前があるので使うときに間違えそうです。
Chapter6を読んでいると、いたるところでスタックが使用されています。非常に重要な構造なのだと思いしらされます。あと擬似コードが分かりやすかったです。早くアセンブリ言語に触れたくなりました。

18
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13