d-kami x86-2

45
1 汎汎汎汎汎汎汎汎汎 汎汎汎汎汎汎汎汎汎 汎汎汎 汎汎汎 x86 x86 汎汎汎汎汎汎 汎汎汎汎汎汎 2012 2012 5 5 26 26 汎汎汎汎 汎汎汎汎

Upload: daisuke-kamikawa

Post on 26-May-2015

875 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: d-kami x86-2

11

汎用レジスタの説明汎用レジスタの説明そしてそして x86x86 エミュレータエミュレータ

20122012 年年 55 月月 2626 日日上川大介上川大介

Page 2: d-kami x86-2

22

自己紹介自己紹介

• 名前 上川大介• Twitter d_kami

• はてな d-kami

• Java をメインに使っているが、自作 OSを作り始めたことにより、 x86 エミュレータを作り始めた

Page 3: d-kami x86-2

33

目的目的

• 前半– x86 のレジスタに慣れてもらう– x86 の一部なら意外と簡単だということをわ

かってもうらう• 後半

– 自分なりのエミュレータの作り方をふーんと思いながら見てもらう

Page 4: d-kami x86-2

44

このスライドのルールこのスライドのルール

• アセンブリ言語は Intel 記法で記述する• 命令名やレジスタ名は大文字にする• 16 進数が突然でてきます• 10 進数も混ざったりしてます

Page 5: d-kami x86-2

55

汎用レジスタ汎用レジスタ

• 多種の目的で利用できるレジスタ• 代入命令で自由に書き換え可能• 命令によっては利用方法が決まっている• x86 には汎用レジスタが8つある

Page 6: d-kami x86-2

66

汎用レジスタの種類(汎用レジスタの種類( 32bit32bit ))

• EAX• ECX• EDX• EBX• ESP• EBP• ESI• EDI

Page 7: d-kami x86-2

77

EAXEAX

• アキュムレータ• 演算命令で EAX 専用の命令が存在する• MUL や DIV では必ず使う• C 言語の関数の返り値が入ってたりする

Page 8: d-kami x86-2

88

アキュムレータを使うとこうなアキュムレータを使うとこうなるる

• ADD AL, 0xA0 だと…– 04 A0

• ADD CL, 0xA0 だと…– 80 C1 A0

Page 9: d-kami x86-2

99

ECXECX

• カウンタ• ループでカウンタとして利用される• シフトローテート命令でも利用される

Page 10: d-kami x86-2

1010

EDXEDX

• データ• MUL や DIV で利用される• I/O 操作でも利用される

Page 11: d-kami x86-2

1111

EBXEBX

• ベースアドレス• BIOS Function Call で利用される• 詳しくないのでわかりません!

Page 12: d-kami x86-2

1212

ESPESP

• スタックポインタ• スタックの操作で利用される (PUSH,

POP)

• サブルーチンコールで利用される (CALL, RET)

Page 13: d-kami x86-2

1313

EBPEBP

• ベーススタックポインタ• C 言語のプログラムをコンパイルすると

よくでてくる

Page 14: d-kami x86-2

1414

ESIESI

• ソースインデックス• メモリ転送命令で利用される• メモリの転送元番地を表す

Page 15: d-kami x86-2

1515

EDIEDI

• デスティネーションインデックス• メモリ転送命令で利用される• メモリの転送先番地を表す

Page 16: d-kami x86-2

1616

32bit32bit レジスタレジスタ

• EAX 、 ECX など– 下位 16bit に AX や CX と言った名前でアクセス

– 下位 16bit の下位 8bit に AL 、 CL と言った名前でアクセス( EAX 、 ECX 、 EDX 、 EBX のみ)

– 下位 16bit の上位 8bit に AH 、 CH と言った名前でアクセス( EAX 、 ECX 、 EDX 、 EBX のみ)

Page 17: d-kami x86-2

1717

レジスタの選び方レジスタの選び方

1. オペコードにレジスタ番号を埋め込む2. ModR/M で指定する3. 命令によっては使うレジスタが固定さ

れる

Page 18: d-kami x86-2

1818

オペコードにレジスタ番号を埋めオペコードにレジスタ番号を埋め込む込む

• MOV 命令( B0 ~ BF )• XCHG 命令( 90 ~ 97 )• PUSH 命令( 50 ~ 57 )• POP 命令( 58 ~ 5F )• INC 命令 (40 ~ 47)

• DEC 命令 (48 ~ 4F)

Page 19: d-kami x86-2

1919

ModR/MModR/M で指定するで指定する

• ModR/M はオペコードの次のバイト• ModR/M があるかどうかは命令次第• ModR/M の構造ModR/M が 55 だった場合01010101

01 が Mod 、 101 が R/M010 がレジスタインデックスまたはオペコー

Page 20: d-kami x86-2

2020

レジスタが固定される場合レジスタが固定される場合

• 演算命令の一部で EAX が利用される• MUL 、 DIV で EAX と EDX

• 繰り返し命令で ECX

• メモリ転送命令で ESI 、 EDI

Page 21: d-kami x86-2

2121

メモリのアドレス指定方法メモリのアドレス指定方法

• ModR/M で指定する– [EBX], 0xA0000000– C7 03 00 00 00 A0

• string 命令を使う– MOVSB

• ESI や EDI がメモリアドレスになる• MOV [EDI], [ESI] のようなことをやる• MOV [EDI], [ESI] をアセンブラにかけると怒られ

ますが

Page 22: d-kami x86-2

2222

前半のまとめ前半のまとめ

• 汎用レジスタが 8 つある• メモリアドレスは ModR/M や string 命令

Page 23: d-kami x86-2

2323

x86x86 エミュレータ編エミュレータ編

• 初期の頃のソースコードの解説• エミュレータは Java で作った• Google Web Toolkit を使った HTML5 版も

あるよ!

Page 24: d-kami x86-2

2424

初期の頃の問題点初期の頃の問題点

• 知らないことが多すぎた• ModR/M を知らなかった

Page 25: d-kami x86-2

2525

x86x86 エミュレータ 命令実装前エミュレータ 命令実装前

public class VM{ private int eip; private byte[] memory;

// イメージファイルの読み込み public void load(String name){ /* 読み込む処理 */ }}

Page 26: d-kami x86-2

2626

命令実装命令実装 :: エミュレータ本体編 エミュレータ本体編 1/21/2

public void execute(){

int code = memory[eip] & 0xFF;

Instruction instruction = instructions[code];

instruction.execute(this);

}

Page 27: d-kami x86-2

2727

命令実装命令実装 : : エミュレータ本体編 エミュレータ本体編 2/22/2

public void execute(){

int code = getCode8(0);

Instruction instruction = instructions[code];

instruction.execute(this);

}

Page 28: d-kami x86-2

2828

エミュレータ本体の変化エミュレータ本体の変化

• 最初はオペコードを取得するためにプログラムカウンタの値をそのまま利用

• getCode8 を作成• getCode8 ではセグメントやページングで

のアドレス変換を行って、オペコードの位置を求めている

Page 29: d-kami x86-2

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();

Page 30: d-kami x86-2

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);

}

}

Page 31: d-kami x86-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;

/* 処理 */

Page 32: d-kami x86-2

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); }}

Page 33: d-kami x86-2

3333

ModR/MModR/M で命令分岐で命令分岐

• ModR/M にオペコードが含まれる命令がある

Page 34: d-kami x86-2

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(); }

Page 35: d-kami x86-2

3535

ModR/MModR/M による命令分岐の実装 による命令分岐の実装 2/22/2

public void execute(VM vm){

int code =

vm.getModRM(false).getOpecode();

instruction = instructions[code];

instruction.execute(vm)

}

Page 36: d-kami x86-2

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();

Page 37: d-kami x86-2

3737

自作エミュレータの現状 自作エミュレータの現状 1/21/2

• 200 命令ぐらいを適当に実装• BIOS を適当に実装• セグメントを適当に実装• ページングを適当に実装• 割り込みを適当に実装

Page 38: d-kami x86-2

3838

真面目にやれって言ったって真面目にやれって言ったって……

まともにやってたら大変なんですよ!

Page 39: d-kami x86-2

3939

自作エミュレータの現状 自作エミュレータの現状 2/22/2

• Mona 0.1.1 を実行できる• Mona 0.1.5 が途中でエラーになる• xv6 が途中で無限ループに陥る• Plan9 が途中でエラーになる

Page 40: d-kami x86-2

4040

デモデモ

デモ

Page 41: d-kami x86-2

4141

自作エミュレータのデバッグ方自作エミュレータのデバッグ方法法

• 最初の頃は…– 実行順に命令名を全て表示– 命令実行ごとにレジスタの値表示– メモリの特定のアドレスにある値表示

• 現状– 数百万命令~数千万命令実行されるので全て

ダンプすると時間かかったり、ログファイルが巨大化して死ぬ

Page 42: d-kami x86-2

4242

現時点でのデバッグ方法現時点でのデバッグ方法

• いい方法が思いつかない• とりあえずできること

– OS にデバッグ用命令を埋め込む• コンパイラのバージョンの違いにより、今ではコ

ンパイルできない OS があってめんどい

– ある命令が実行される、もしくはメモリのあるアドレスがアクセスされるまでダンプを行わない

• それでも数千~数万命令がダンプされるのはよくあること

Page 43: d-kami x86-2

4343

厄介なバグ厄介なバグ

• 無限ループに陥る– 止まってくれると有難い– ループ内の条件付きジャンプが多すぎてどの

ジャンプでループを抜けるのかがわからない

Page 44: d-kami x86-2

4444

良いデバッグ方法があったら良いデバッグ方法があったら

教えてください!

Page 45: d-kami x86-2

4545

まとめまとめ

• 適当に作ってると酷い目にあう• 無限ループは敵• x86 エミュレータは他人が作る物