Add MIPS and MIPS64 seed decoding
ober
8f00c55519dc111b93243a5740a5590b90a0739e
--- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ make test make binary ./jasm raw --arch x86-64 --syntax intel --hex '55 48 89 e5 c3' ./jasm raw --arch aarch64 --hex '20 00 80 d2 c0 03 5f d6' +./jasm raw --arch mips --hex '27 bd ff e0 03 e0 00 08' ``` ## Library API --- a/docs/plan.md +++ b/docs/plan.md @@ -36,8 +36,8 @@ reference material and parity targets. - Seed raw-byte support exists for: - `x86-64`: common prologue/control-flow instructions. - `aarch64`: common move, add/sub, load/store, return instructions. -- MIPS and MIPS64 are planned as first-class architecture targets, including - both raw-byte decoding and object-file architecture mapping. + - `mips`, `mipsel`, `mips64`, `mips64el`: seed fixed-width raw-byte decoding + for common integer/load/store/branch/jump instructions. - Tests cover the initial decoder slice. ## Core Architecture @@ -688,6 +688,8 @@ Status: started. - ABI register-name output. - ELF `EM_MIPS` integration. +Status: raw seed decoder started; ELF integration remains. + ### Milestone 9: CLI Parity Pass - `llvm-objdump`-like option names. Binary files a/jasm and b/jasm differ --- a/lib/jasm/disasm.ss +++ b/lib/jasm/disasm.ss @@ -162,6 +162,15 @@ (* (bv-u8 bv (+ offset 2)) #x10000) (* (bv-u8 bv (+ offset 3)) #x1000000))) + (define (bv-u32-be bv offset) + (+ (* (bv-u8 bv offset) #x1000000) + (* (bv-u8 bv (+ offset 1)) #x10000) + (* (bv-u8 bv (+ offset 2)) #x100) + (bv-u8 bv (+ offset 3)))) + + (define (signed16 x) + (if (>= x #x8000) (- x #x10000) x)) + (define (bits n hi lo) (modulo (quotient n (expt 2 lo)) (expt 2 (+ 1 (- hi lo))))) @@ -370,10 +379,211 @@ (bv-slice bv offset 4)))] [else (a64-unknown-word bv offset address remaining)]))))) + (define mips-abi-regs + '#("$zero" "$at" "$v0" "$v1" "$a0" "$a1" "$a2" "$a3" + "$t0" "$t1" "$t2" "$t3" "$t4" "$t5" "$t6" "$t7" + "$s0" "$s1" "$s2" "$s3" "$s4" "$s5" "$s6" "$s7" + "$t8" "$t9" "$k0" "$k1" "$gp" "$sp" "$fp" "$ra")) + + (define (mips-reg n options) + (if (eq? (option-ref options 'registers 'abi) 'numeric) + (string-append "$" (number->string n)) + (vector-ref mips-abi-regs n))) + + (define (mips-word arch bv offset) + ((if (eq? (architecture-endianness arch) 'little) bv-u32-le bv-u32-be) bv offset)) + + (define (mips-unknown arch bv offset address remaining) + (if (< remaining 4) + (make-instruction arch address 1 + (string-append ".byte\t0x" (byte->hex2 (bv-u8 bv offset))) + (bv-slice bv offset 1)) + (let ([word (mips-word arch bv offset)]) + (make-instruction arch address 4 + (string-append ".word\t0x" (number->hex word)) + (bv-slice bv offset 4))))) + + (define (mips-r3 mnemonic rd rs rt options) + (string-append mnemonic "\t" + (mips-reg rd options) ", " + (mips-reg rs options) ", " + (mips-reg rt options))) + + (define (mips-r-shift mnemonic rd rt shamt options) + (string-append mnemonic "\t" + (mips-reg rd options) ", " + (mips-reg rt options) ", " + (number->string shamt))) + + (define (mips-i3 mnemonic rt rs imm options) + (string-append mnemonic "\t" + (mips-reg rt options) ", " + (mips-reg rs options) ", " + (number->string imm))) + + (define (mips-i3-hex mnemonic rt rs imm options) + (string-append mnemonic "\t" + (mips-reg rt options) ", " + (mips-reg rs options) ", " + (hex-number imm))) + + (define (mips-load-store mnemonic rt rs imm options) + (string-append mnemonic "\t" + (mips-reg rt options) ", " + (number->string imm) + "(" (mips-reg rs options) ")")) + + (define (mips-branch-target address imm) + (+ address 4 (* (signed16 imm) 4))) + + (define (mips-jump-target address index) + (+ (bitwise-and (+ address 4) #xf0000000) + (* index 4))) + + (define (mips64? arch) + (or (eq? arch 'mips64) (eq? arch 'mips64el))) + + (define (decode-mips-special arch word bv offset address options) + (let* ([rs (bits word 25 21)] + [rt (bits word 20 16)] + [rd (bits word 15 11)] + [shamt (bits word 10 6)] + [funct (bits word 5 0)] + [text + (cond + [(= word 0) "nop"] + [(= funct #x00) (mips-r-shift "sll" rd rt shamt options)] + [(= funct #x02) (mips-r-shift "srl" rd rt shamt options)] + [(= funct #x03) (mips-r-shift "sra" rd rt shamt options)] + [(= funct #x08) (string-append "jr\t" (mips-reg rs options))] + [(= funct #x09) (string-append "jalr\t" (mips-reg rd options) ", " (mips-reg rs options))] + [(= funct #x0c) "syscall"] + [(= funct #x0d) "break"] + [(= funct #x21) (mips-r3 "addu" rd rs rt options)] + [(= funct #x23) (mips-r3 "subu" rd rs rt options)] + [(= funct #x24) (mips-r3 "and" rd rs rt options)] + [(= funct #x25) (mips-r3 "or" rd rs rt options)] + [(= funct #x26) (mips-r3 "xor" rd rs rt options)] + [(= funct #x27) (mips-r3 "nor" rd rs rt options)] + [(= funct #x2a) (mips-r3 "slt" rd rs rt options)] + [(= funct #x2b) (mips-r3 "sltu" rd rs rt options)] + [(and (mips64? arch) (= funct #x2d)) (mips-r3 "daddu" rd rs rt options)] + [(and (mips64? arch) (= funct #x2f)) (mips-r3 "dsubu" rd rs rt options)] + [(and (mips64? arch) (= funct #x38)) (mips-r-shift "dsll" rd rt shamt options)] + [(and (mips64? arch) (= funct #x3a)) (mips-r-shift "dsrl" rd rt shamt options)] + [(and (mips64? arch) (= funct #x3b)) (mips-r-shift "dsra" rd rt shamt options)] + [(and (mips64? arch) (= funct #x3c)) (mips-r-shift "dsll32" rd rt shamt options)] + [(and (mips64? arch) (= funct #x3e)) (mips-r-shift "dsrl32" rd rt shamt options)] + [(and (mips64? arch) (= funct #x3f)) (mips-r-shift "dsra32" rd rt shamt options)] + [else #f])]) + (if text + (make-instruction arch address 4 text (bv-slice bv offset 4)) + (mips-unknown arch bv offset address 4)))) + + (define (decode-mips-regimm arch word bv offset address options) + (let* ([rs (bits word 25 21)] + [rt (bits word 20 16)] + [imm (bits word 15 0)] + [target (hex-number (mips-branch-target address imm))] + [mnemonic (case rt + [(#x00) "bltz"] + [(#x01) "bgez"] + [(#x10) "bltzal"] + [(#x11) "bgezal"] + [else #f])]) + (if mnemonic + (make-instruction arch address 4 + (string-append mnemonic "\t" (mips-reg rs options) ", " target) + (bv-slice bv offset 4)) + (mips-unknown arch bv offset address 4)))) + + (define (decode-mips arch bv offset address options) + (let* ([arch (normalize-architecture arch)] + [remaining (- (bytevector-length bv) offset)]) + (if (< remaining 4) + (mips-unknown arch bv offset address remaining) + (let* ([word (mips-word arch bv offset)] + [op (bits word 31 26)] + [rs (bits word 25 21)] + [rt (bits word 20 16)] + [imm16 (bits word 15 0)] + [simm (signed16 imm16)] + [index (bits word 25 0)]) + (case op + [(#x00) (decode-mips-special arch word bv offset address options)] + [(#x01) (decode-mips-regimm arch word bv offset address options)] + [(#x02 #x03) + (let ([mnemonic (if (= op #x02) "j" "jal")]) + (make-instruction arch address 4 + (string-append mnemonic "\t" (hex-number (mips-jump-target address index))) + (bv-slice bv offset 4)))] + [(#x04 #x05) + (let ([mnemonic (if (= op #x04) "beq" "bne")]) + (make-instruction arch address 4 + (string-append mnemonic "\t" + (mips-reg rs options) ", " + (mips-reg rt options) ", " + (hex-number (mips-branch-target address imm16))) + (bv-slice bv offset 4)))] + [(#x06 #x07) + (let ([mnemonic (if (= op #x06) "blez" "bgtz")]) + (make-instruction arch address 4 + (string-append mnemonic "\t" + (mips-reg rs options) ", " + (hex-number (mips-branch-target address imm16))) + (bv-slice bv offset 4)))] + [(#x09) + (make-instruction arch address 4 + (mips-i3 "addiu" rt rs simm options) + (bv-slice bv offset 4))] + [(#x0c) + (make-instruction arch address 4 + (mips-i3-hex "andi" rt rs imm16 options) + (bv-slice bv offset 4))] + [(#x0d) + (make-instruction arch address 4 + (mips-i3-hex "ori" rt rs imm16 options) + (bv-slice bv offset 4))] + [(#x0e) + (make-instruction arch address 4 + (mips-i3-hex "xori" rt rs imm16 options) + (bv-slice bv offset 4))] + [(#x0f) + (make-instruction arch address 4 + (string-append "lui\t" (mips-reg rt options) ", " (hex-number imm16)) + (bv-slice bv offset 4))] + [(#x19) + (if (mips64? arch) + (make-instruction arch address 4 + (mips-i3 "daddiu" rt rs simm options) + (bv-slice bv offset 4)) + (mips-unknown arch bv offset address remaining))] + [(#x20 #x21 #x23 #x24 #x25 #x28 #x29 #x2b #x37 #x3f) + (let ([mnemonic + (case op + [(#x20) "lb"] + [(#x21) "lh"] + [(#x23) "lw"] + [(#x24) "lbu"] + [(#x25) "lhu"] + [(#x28) "sb"] + [(#x29) "sh"] + [(#x2b) "sw"] + [(#x37) (and (mips64? arch) "ld")] + [(#x3f) (and (mips64? arch) "sd")] + [else #f])]) + (if mnemonic + (make-instruction arch address 4 + (mips-load-store mnemonic rt rs simm options) + (bv-slice bv offset 4)) + (mips-unknown arch bv offset address remaining)))] + [else (mips-unknown arch bv offset address remaining)]))))) + (define (decode-instruction arch bv offset address options) (case (normalize-architecture arch) [(x86-64) (decode-x86-64 bv offset address options)] [(aarch64) (decode-aarch64 bv offset address options)] + [(mips mipsel mips64 mips64el) (decode-mips arch bv offset address options)] [else (error 'decode-instruction "decoder not implemented for architecture" arch)])) (define (disassemble-bytevector arch bv . maybe-options) --- a/main.ss +++ b/main.ss @@ -7,7 +7,8 @@ (display " jasm raw --arch <x86-64|aarch64|mips|mipsel|mips64|mips64el> [--syntax att|intel] [--address n] --hex <bytes>\n") (display "\nExamples:\n") (display " jasm raw --arch x86-64 --syntax intel --hex '55 48 89 e5 c3'\n") - (display " jasm raw --arch aarch64 --hex '20 00 80 d2 c0 03 5f d6'\n")) + (display " jasm raw --arch aarch64 --hex '20 00 80 d2 c0 03 5f d6'\n") + (display " jasm raw --arch mips --hex '27 bd ff e0 03 e0 00 08'\n")) (define (string->arch s) (normalize-architecture s)) --- a/tests/test-jasm.ss +++ b/tests/test-jasm.ss @@ -77,6 +77,42 @@ (one-text 'aarch64 "e0 0f 00 b9") "str\tw0, [sp, #0xc]") +(test "mips nop" + (one-text 'mips "00 00 00 00") + "nop") + +(test "mips addiu big endian" + (one-text 'mips "27 bd ff e0") + "addiu\t$sp, $sp, -32") + +(test "mips addiu little endian" + (one-text 'mipsel "e0 ff bd 27") + "addiu\t$sp, $sp, -32") + +(test "mips jr" + (one-text 'mips "03 e0 00 08") + "jr\t$ra") + +(test "mips beq target" + (one-text 'mips "11 09 ff fe" '((address . #x1000))) + "beq\t$t0, $t1, 0xffc") + +(test "mips64 daddiu big endian" + (one-text 'mips64 "67 bd ff e0") + "daddiu\t$sp, $sp, -32") + +(test "mips64 daddiu little endian" + (one-text 'mips64el "e0 ff bd 67") + "daddiu\t$sp, $sp, -32") + +(test "mips64 ld" + (one-text 'mips64 "dc 82 00 00") + "ld\t$v0, 0($a0)") + +(test "mips numeric registers" + (one-text 'mips "27 bd ff e0" '((registers . numeric))) + "addiu\t$29, $29, -32") + (test "format string" (disassemble->string 'x86-64 (hex-string->bytevector "55 c3")