自作エミュレータで学ぶx86アーキテクチャ 2.3
自作エミュレータで学ぶx86アーキテクチャを読んでます。
この本ではあらかじめc言語で書かれているエミュレータのプログラムを元に
解説していくのですが、理解を深めるためにgaucheでエミュレータを作成しています。
(use gauche.uvector) (define-class <registers> () ((eax :init-keyword :eax :init-value 0) (ecx :init-keyword :ecx :init-value 0) (edx :init-keyword :edx :init-value 0) (ebx :init-keyword :ebx :init-value 0) (esp :init-keyword :esp :init-value 0) (ebp :init-keyword :ebp :init-value 0) (esi :init-keyword :esi :init-value 0) (edi :init-keyword :edi :init-value 0)) ) (define register-name '(eax ecx edx ebx esp ebp esi edi)) (define memory-size (* 1024 1024)) (define-class <emulator> () ((memory :init-keyword :memory) (registers :init-keyword :registers) (eflags :init-keyword :eflags) (eip :init-keyword :eip)) ) (define-method dump-registers ((reg <registers>)) (for-each (lambda (s) (format #t "~a = ~8,'0x\n" s (ref reg s))) register-name)) (define-method dump-registers ((emu <emulator>)) (dump-registers (ref emu 'registers)) (format #t "~a = ~8,'0x\n" 'eip (ref emu 'eip))) (define (mov-r32-imm32 emu) (let1 reg (- (get-code8 emu 0) #xb8) (set! (ref (ref emu 'registers) (list-ref register-name reg)) (get-code32 emu 1)) (set! (ref emu 'eip) (+ (ref emu 'eip) 5)))) (define (short-jump emu) (set! (ref emu 'eip) (+ (ref emu 'eip) (get-sign-code8 emu 1) 2))) (define (get-instructions code) (cond [(and (<= #xb8 code) (<= code (+ #xb8 7))) mov-r32-imm32] [(= code #xeb) short-jump] [else '()])) (define (create-emu size eip esp) (make <emulator> :memory (make-u8vector size 0) :registers (make <registers> :esp esp) :eip eip)) (define (get-code8 emu index) (let ((memory (ref emu 'memory)) (eip (ref emu 'eip))) (u8vector-ref memory (+ index eip)))) (define (get-sign-code8-1 num) (if (not (= (logand #b10000000 num) 0)) (- (+ (logxor num #b11111111) 1)) num)) (define (get-sign-code8 emu index) (get-sign-code8-1 (get-code8 emu index))) (define (get-code32 emu index) (let loop ((i 0) (ret 0)) (if (< i 4) (loop (+ i 1) (logior ret (ash (get-code8 emu (+ index i)) (* i 8)))) ret))) (define (run1 emu code) (let1 inst (get-instructions code) (format #t "eip = ~x, code = ~2,'0x\n" (ref emu 'eip) code) (cond [(null? inst) (format #t "\n\nNot Implemented: ~x\n" code) '()] [else (inst emu) #t]))) (define (run-1 emu) (cond [(= (ref emu 'eip) #x00) (display "\n\nend of program.\n\n")] [(< (ref emu 'eip) memory-size) (if (run1 emu (get-code8 emu 0)) (run-1 emu))])) (define (run emu) (if (run1 emu (get-code8 emu 0)) (run-1 emu))) (define (memory-load emu p) (read-block! (ref emu 'memory) p 0 511)) (define (emu-run file) (let [(emu (create-emu memory-size 0 #x7c00))] (call-with-input-file file (lambda (p) (memory-load emu p) (run emu) (dump-registers emu))))) (define test-emu (create-emu memory-size 0 0))
emu-runにhelloworld.binのパスを渡せば、c言語版と同じ結果が表示されます。
gosh> (emu-run "./helloworld.bin") eip = 0, code = b8 eip = 5, code = eb end of program. eax = 00000029 ecx = 00000000 edx = 00000000 ebx = 00000000 esp = 00007c00 ebp = 00000000 esi = 00000000 edi = 00000000 eip = 00000000 #<undef> gosh>