d-kami x86-2
TRANSCRIPT
11
汎用レジスタの説明汎用レジスタの説明そしてそして x86x86 エミュレータエミュレータ
20122012 年年 55 月月 2626 日日上川大介上川大介
22
自己紹介自己紹介
• 名前 上川大介• Twitter d_kami
• はてな d-kami
• Java をメインに使っているが、自作 OSを作り始めたことにより、 x86 エミュレータを作り始めた
33
目的目的
• 前半– x86 のレジスタに慣れてもらう– x86 の一部なら意外と簡単だということをわ
かってもうらう• 後半
– 自分なりのエミュレータの作り方をふーんと思いながら見てもらう
44
このスライドのルールこのスライドのルール
• アセンブリ言語は Intel 記法で記述する• 命令名やレジスタ名は大文字にする• 16 進数が突然でてきます• 10 進数も混ざったりしてます
55
汎用レジスタ汎用レジスタ
• 多種の目的で利用できるレジスタ• 代入命令で自由に書き換え可能• 命令によっては利用方法が決まっている• x86 には汎用レジスタが8つある
66
汎用レジスタの種類(汎用レジスタの種類( 32bit32bit ))
• EAX• ECX• EDX• EBX• ESP• EBP• ESI• EDI
77
EAXEAX
• アキュムレータ• 演算命令で EAX 専用の命令が存在する• MUL や DIV では必ず使う• C 言語の関数の返り値が入ってたりする
88
アキュムレータを使うとこうなアキュムレータを使うとこうなるる
• ADD AL, 0xA0 だと…– 04 A0
• ADD CL, 0xA0 だと…– 80 C1 A0
99
ECXECX
• カウンタ• ループでカウンタとして利用される• シフトローテート命令でも利用される
1010
EDXEDX
• データ• MUL や DIV で利用される• I/O 操作でも利用される
1111
EBXEBX
• ベースアドレス• BIOS Function Call で利用される• 詳しくないのでわかりません!
1212
ESPESP
• スタックポインタ• スタックの操作で利用される (PUSH,
POP)
• サブルーチンコールで利用される (CALL, RET)
1313
EBPEBP
• ベーススタックポインタ• C 言語のプログラムをコンパイルすると
よくでてくる
1414
ESIESI
• ソースインデックス• メモリ転送命令で利用される• メモリの転送元番地を表す
1515
EDIEDI
• デスティネーションインデックス• メモリ転送命令で利用される• メモリの転送先番地を表す
1616
32bit32bit レジスタレジスタ
• EAX 、 ECX など– 下位 16bit に AX や CX と言った名前でアクセス
– 下位 16bit の下位 8bit に AL 、 CL と言った名前でアクセス( EAX 、 ECX 、 EDX 、 EBX のみ)
– 下位 16bit の上位 8bit に AH 、 CH と言った名前でアクセス( EAX 、 ECX 、 EDX 、 EBX のみ)
1717
レジスタの選び方レジスタの選び方
1. オペコードにレジスタ番号を埋め込む2. ModR/M で指定する3. 命令によっては使うレジスタが固定さ
れる
1818
オペコードにレジスタ番号を埋めオペコードにレジスタ番号を埋め込む込む
• MOV 命令( B0 ~ BF )• XCHG 命令( 90 ~ 97 )• PUSH 命令( 50 ~ 57 )• POP 命令( 58 ~ 5F )• INC 命令 (40 ~ 47)
• DEC 命令 (48 ~ 4F)
1919
ModR/MModR/M で指定するで指定する
• ModR/M はオペコードの次のバイト• ModR/M があるかどうかは命令次第• ModR/M の構造ModR/M が 55 だった場合01010101
01 が Mod 、 101 が R/M010 がレジスタインデックスまたはオペコー
ド
2020
レジスタが固定される場合レジスタが固定される場合
• 演算命令の一部で EAX が利用される• MUL 、 DIV で EAX と EDX
• 繰り返し命令で ECX
• メモリ転送命令で ESI 、 EDI
2121
メモリのアドレス指定方法メモリのアドレス指定方法
• ModR/M で指定する– [EBX], 0xA0000000– C7 03 00 00 00 A0
• string 命令を使う– MOVSB
• ESI や EDI がメモリアドレスになる• MOV [EDI], [ESI] のようなことをやる• MOV [EDI], [ESI] をアセンブラにかけると怒られ
ますが
2222
前半のまとめ前半のまとめ
• 汎用レジスタが 8 つある• メモリアドレスは ModR/M や string 命令
2323
x86x86 エミュレータ編エミュレータ編
• 初期の頃のソースコードの解説• エミュレータは Java で作った• Google Web Toolkit を使った HTML5 版も
あるよ!
2424
初期の頃の問題点初期の頃の問題点
• 知らないことが多すぎた• ModR/M を知らなかった
2525
x86x86 エミュレータ 命令実装前エミュレータ 命令実装前
public class VM{ private int eip; private byte[] memory;
// イメージファイルの読み込み public void load(String name){ /* 読み込む処理 */ }}
2626
命令実装命令実装 :: エミュレータ本体編 エミュレータ本体編 1/21/2
public void execute(){
int code = memory[eip] & 0xFF;
Instruction instruction = instructions[code];
instruction.execute(this);
}
2727
命令実装命令実装 : : エミュレータ本体編 エミュレータ本体編 2/22/2
public void execute(){
int code = getCode8(0);
Instruction instruction = instructions[code];
instruction.execute(this);
}
2828
エミュレータ本体の変化エミュレータ本体の変化
• 最初はオペコードを取得するためにプログラムカウンタの値をそのまま利用
• getCode8 を作成• getCode8 ではセグメントやページングで
のアドレス変換を行って、オペコードの位置を求めている
2929
InstructionsInstructions
Instruction[] instructions = new Instruction[256];
instructions[0x00] = new AddRM8R8();
instructions[0x01] = new AddRMXRX();
instructions[0x02] = new AddR8RM8();
instructions[0x03] = new AddRXRMX();
instructions[0x04] = new AddALImm8();
3030
命令実行命令実装編 命令実行命令実装編 1/31/3
public class MoveALImm8 implements Instruction{
public void exeucte(VM vm){
int imm = vm.getCode8(1);
vm.setRegister8Low(EAX, imm);
vm.addEIP(2);
}
}
3131
命令実行命令実装編 命令実行命令実装編 2/32/3
public class MoveR8RM8 implements …
public void exeucte(VM vm){
int modrm = vm.getCode8(1);
int mod = modrm >> 6;
int register = (modrm >> 3) & 0x07;
int rm = modrm & 0x07;
/* 処理 */
3232
命令実行命令実装編 命令実行命令実装編 3/33/3
public class MoveR8RM8 implements … public void execute(VM vm){ ModRM modrm = vm.getModRM(); int value = modrm.getRMValue8(); modrm.setRegister8(value); vm.addEIP(2); }}
3333
ModR/MModR/M で命令分岐で命令分岐
• ModR/M にオペコードが含まれる命令がある
3434
ModR/MModR/M による命令分岐の実装 による命令分岐の実装 1/21/2
public class ExtD0 implements Instruction{ private Instruction[] instructions;
public ExtD0(){ instructions = new Instruction[8]; instructions[0] = new ROLRM8(); instructions[1] = new RORRM8(); }
3535
ModR/MModR/M による命令分岐の実装 による命令分岐の実装 2/22/2
public void execute(VM vm){
int code =
vm.getModRM(false).getOpecode();
instruction = instructions[code];
instruction.execute(vm)
}
3636
オペコードが多バイトの場合オペコードが多バイトの場合
public class Ext0F implements Instruction{ private Instruction[] instructions; private Instruction instruction;
public Ext0F(){ instructions = new Instruction[256];
instructions[0x00] = new Descriptor0(); instructions[0x01] = new Descriptor(); instructions[0x20] = new MoveR32CRX();
3737
自作エミュレータの現状 自作エミュレータの現状 1/21/2
• 200 命令ぐらいを適当に実装• BIOS を適当に実装• セグメントを適当に実装• ページングを適当に実装• 割り込みを適当に実装
3838
真面目にやれって言ったって真面目にやれって言ったって……
まともにやってたら大変なんですよ!
3939
自作エミュレータの現状 自作エミュレータの現状 2/22/2
• Mona 0.1.1 を実行できる• Mona 0.1.5 が途中でエラーになる• xv6 が途中で無限ループに陥る• Plan9 が途中でエラーになる
4040
デモデモ
デモ
4141
自作エミュレータのデバッグ方自作エミュレータのデバッグ方法法
• 最初の頃は…– 実行順に命令名を全て表示– 命令実行ごとにレジスタの値表示– メモリの特定のアドレスにある値表示
• 現状– 数百万命令~数千万命令実行されるので全て
ダンプすると時間かかったり、ログファイルが巨大化して死ぬ
4242
現時点でのデバッグ方法現時点でのデバッグ方法
• いい方法が思いつかない• とりあえずできること
– OS にデバッグ用命令を埋め込む• コンパイラのバージョンの違いにより、今ではコ
ンパイルできない OS があってめんどい
– ある命令が実行される、もしくはメモリのあるアドレスがアクセスされるまでダンプを行わない
• それでも数千~数万命令がダンプされるのはよくあること
4343
厄介なバグ厄介なバグ
• 無限ループに陥る– 止まってくれると有難い– ループ内の条件付きジャンプが多すぎてどの
ジャンプでループを抜けるのかがわからない
4444
良いデバッグ方法があったら良いデバッグ方法があったら
教えてください!
4545
まとめまとめ
• 適当に作ってると酷い目にあう• 無限ループは敵• x86 エミュレータは他人が作る物