Add seed RISC-V decoder
ober
da2b1f1dc79156f072e15cf5122200b8180de3b5
--- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ test-binary: binary ./$(BINARY) headers ./$(BINARY) ./$(BINARY) raw --arch x86-64 --syntax intel --hex '55 48 89 e5 c3' ./$(BINARY) raw --arch aarch64 --hex '20 00 80 d2 c0 03 5f d6' + ./$(BINARY) raw --arch riscv64 --hex '13 01 01 ff 67 80 00 00' $(SCHEME) --libdirs $(LIBDIRS) --script tests/write-raw-bytes.ss ./.jasm-raw.bin ./$(BINARY) raw --arch x86-64 --syntax intel --max-bytes 2 --file ./.jasm-raw.bin $(SCHEME) --libdirs $(LIBDIRS) --script tests/write-sample-elf.ss ./.jasm-sample.o --- a/docs/plan.md +++ b/docs/plan.md @@ -38,6 +38,8 @@ reference material and parity targets. - `aarch64`: common move, add/sub, load/store, return instructions. - `mips`, `mipsel`, `mips64`, `mips64el`: seed fixed-width raw-byte decoding for common integer/load/store/branch/jump instructions. + - `riscv32`, `riscv64`: seed RV32/RV64 base integer decoding for common + arithmetic, load/store, branch, jump, and system instructions. - Tests cover the initial decoder slice. - `(jasm object elf)` can parse ELF32/ELF64 headers and section tables from a bytevector or file, map supported machines to decoder architectures, discover @@ -387,8 +389,9 @@ RISC-V is a good third architecture because its encoding is regular. Required: -- RV32 and RV64. -- Base integer ISA. +- RV32 and RV64. Initial raw decoder support is implemented. +- Base integer ISA. Initial arithmetic/load/store/branch/jump/system coverage + is implemented. - compressed `C` extension. - `M`, `A`, `F`, `D`. - vector extension later. @@ -696,7 +699,7 @@ Status: started. - RV32/RV64 base integer. - compressed extension. -- ELF integration. +- ELF integration. Initial `EM_RISCV` mapping is implemented. ### Milestone 8: MIPS/MIPS64 Baseline Binary files a/jasm and b/jasm differ --- a/lib/jasm/disasm.ss +++ b/lib/jasm/disasm.ss @@ -20,7 +20,7 @@ (jasm instruction) (jasm format)) - (define supported-architectures '(x86-64 aarch64 mips mipsel mips64 mips64el)) + (define supported-architectures '(x86-64 aarch64 mips mipsel mips64 mips64el riscv32 riscv64)) (define architecture-aliases '((x86-64 . x86-64) @@ -36,7 +36,15 @@ (mips32el . mipsel) (mips64 . mips64) (mips64eb . mips64) - (mips64el . mips64el))) + (mips64el . mips64el) + (riscv . riscv64) + (risc-v . riscv64) + (riscv32 . riscv32) + (risc-v32 . riscv32) + (rv32 . riscv32) + (riscv64 . riscv64) + (risc-v64 . riscv64) + (rv64 . riscv64))) (define (normalize-architecture arch) (let* ([sym (cond @@ -152,6 +160,11 @@ (define (signed16 x) (if (>= x #x8000) (- x #x10000) x)) + (define (sign-extend x width) + (let ([sign (expt 2 (- width 1))] + [limit (expt 2 width)]) + (if (>= x sign) (- x limit) x))) + (define (bits n hi lo) (modulo (quotient n (expt 2 lo)) (expt 2 (+ 1 (- hi lo))))) @@ -560,11 +573,225 @@ (mips-unknown arch bv offset address remaining)))] [else (mips-unknown arch bv offset address remaining)]))))) + (define riscv-reg-abi + '#("zero" "ra" "sp" "gp" "tp" "t0" "t1" "t2" + "s0" "s1" "a0" "a1" "a2" "a3" "a4" "a5" + "a6" "a7" "s2" "s3" "s4" "s5" "s6" "s7" + "s8" "s9" "s10" "s11" "t3" "t4" "t5" "t6")) + + (define (riscv64? arch) + (eq? (normalize-architecture arch) 'riscv64)) + + (define (riscv-reg n options) + (if (eq? (option-ref options 'registers 'abi) 'numeric) + (string-append "x" (number->string n)) + (vector-ref riscv-reg-abi n))) + + (define (riscv-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 (bv-u32-le bv offset)]) + (make-instruction arch address 4 + (string-append ".word\t0x" (number->hex word)) + (bv-slice bv offset 4))))) + + (define (riscv-r mnemonic rd rs1 rs2 options) + (string-append mnemonic "\t" + (riscv-reg rd options) ", " + (riscv-reg rs1 options) ", " + (riscv-reg rs2 options))) + + (define (riscv-i mnemonic rd rs1 imm options) + (string-append mnemonic "\t" + (riscv-reg rd options) ", " + (riscv-reg rs1 options) ", " + (number->string imm))) + + (define (riscv-load mnemonic rd rs1 imm options) + (string-append mnemonic "\t" + (riscv-reg rd options) ", " + (number->string imm) "(" + (riscv-reg rs1 options) ")")) + + (define (riscv-store mnemonic rs2 rs1 imm options) + (string-append mnemonic "\t" + (riscv-reg rs2 options) ", " + (number->string imm) "(" + (riscv-reg rs1 options) ")")) + + (define (riscv-branch mnemonic rs1 rs2 target options) + (string-append mnemonic "\t" + (riscv-reg rs1 options) ", " + (riscv-reg rs2 options) ", " + (hex-number target))) + + (define (riscv-u mnemonic rd imm options) + (string-append mnemonic "\t" + (riscv-reg rd options) ", " + (hex-number imm))) + + (define (riscv-jal rd target options) + (string-append "jal\t" (riscv-reg rd options) ", " (hex-number target))) + + (define (riscv-imm-i word) + (sign-extend (bits word 31 20) 12)) + + (define (riscv-imm-s word) + (sign-extend (+ (* (bits word 31 25) #x20) + (bits word 11 7)) + 12)) + + (define (riscv-imm-b word) + (sign-extend (+ (* (bits word 31 31) #x1000) + (* (bits word 7 7) #x800) + (* (bits word 30 25) #x20) + (* (bits word 11 8) #x2)) + 13)) + + (define (riscv-imm-u word) + (bitwise-and word #xfffff000)) + + (define (riscv-imm-j word) + (sign-extend (+ (* (bits word 31 31) #x100000) + (* (bits word 19 12) #x1000) + (* (bits word 20 20) #x800) + (* (bits word 30 21) #x2)) + 21)) + + (define (decode-riscv arch bv offset address options) + (let* ([arch (normalize-architecture arch)] + [len (bytevector-length bv)] + [remaining (- len offset)]) + (if (< remaining 4) + (riscv-unknown arch bv offset address remaining) + (let* ([word (bv-u32-le bv offset)] + [opcode (bits word 6 0)] + [rd (bits word 11 7)] + [funct3 (bits word 14 12)] + [rs1 (bits word 19 15)] + [rs2 (bits word 24 20)] + [funct7 (bits word 31 25)]) + (cond + [(= word #x00000073) + (make-instruction arch address 4 "ecall" (bv-slice bv offset 4))] + [(= word #x00100073) + (make-instruction arch address 4 "ebreak" (bv-slice bv offset 4))] + [(= word #x00000013) + (make-instruction arch address 4 "nop" (bv-slice bv offset 4))] + [(= word #x00008067) + (make-instruction arch address 4 "ret" (bv-slice bv offset 4))] + [(= opcode #x33) + (let ([mnemonic + (cond + [(and (= funct3 0) (= funct7 #x00)) "add"] + [(and (= funct3 0) (= funct7 #x20)) "sub"] + [(and (= funct3 1) (= funct7 #x00)) "sll"] + [(and (= funct3 2) (= funct7 #x00)) "slt"] + [(and (= funct3 3) (= funct7 #x00)) "sltu"] + [(and (= funct3 4) (= funct7 #x00)) "xor"] + [(and (= funct3 5) (= funct7 #x00)) "srl"] + [(and (= funct3 5) (= funct7 #x20)) "sra"] + [(and (= funct3 6) (= funct7 #x00)) "or"] + [(and (= funct3 7) (= funct7 #x00)) "and"] + [else #f])]) + (if mnemonic + (make-instruction arch address 4 + (riscv-r mnemonic rd rs1 rs2 options) + (bv-slice bv offset 4)) + (riscv-unknown arch bv offset address remaining)))] + [(= opcode #x13) + (let* ([imm (riscv-imm-i word)] + [shamt (bits word (if (riscv64? arch) 25 24) 20)] + [mnemonic + (case funct3 + [(0) "addi"] + [(2) "slti"] + [(3) "sltiu"] + [(4) "xori"] + [(6) "ori"] + [(7) "andi"] + [(1) (and (= funct7 #x00) "slli")] + [(5) (cond [(= funct7 #x00) "srli"] + [(= funct7 #x20) "srai"] + [else #f])] + [else #f])]) + (if mnemonic + (make-instruction arch address 4 + (if (or (= funct3 1) (= funct3 5)) + (riscv-i mnemonic rd rs1 shamt options) + (riscv-i mnemonic rd rs1 imm options)) + (bv-slice bv offset 4)) + (riscv-unknown arch bv offset address remaining)))] + [(= opcode #x03) + (let ([mnemonic + (case funct3 + [(0) "lb"] + [(1) "lh"] + [(2) "lw"] + [(3) (and (riscv64? arch) "ld")] + [(4) "lbu"] + [(5) "lhu"] + [(6) (and (riscv64? arch) "lwu")] + [else #f])]) + (if mnemonic + (make-instruction arch address 4 + (riscv-load mnemonic rd rs1 (riscv-imm-i word) options) + (bv-slice bv offset 4)) + (riscv-unknown arch bv offset address remaining)))] + [(= opcode #x23) + (let ([mnemonic + (case funct3 + [(0) "sb"] + [(1) "sh"] + [(2) "sw"] + [(3) (and (riscv64? arch) "sd")] + [else #f])]) + (if mnemonic + (make-instruction arch address 4 + (riscv-store mnemonic rs2 rs1 (riscv-imm-s word) options) + (bv-slice bv offset 4)) + (riscv-unknown arch bv offset address remaining)))] + [(= opcode #x63) + (let ([mnemonic + (case funct3 + [(0) "beq"] + [(1) "bne"] + [(4) "blt"] + [(5) "bge"] + [(6) "bltu"] + [(7) "bgeu"] + [else #f])]) + (if mnemonic + (make-instruction arch address 4 + (riscv-branch mnemonic rs1 rs2 (+ address (riscv-imm-b word)) options) + (bv-slice bv offset 4)) + (riscv-unknown arch bv offset address remaining)))] + [(= opcode #x37) + (make-instruction arch address 4 + (riscv-u "lui" rd (riscv-imm-u word) options) + (bv-slice bv offset 4))] + [(= opcode #x17) + (make-instruction arch address 4 + (riscv-u "auipc" rd (riscv-imm-u word) options) + (bv-slice bv offset 4))] + [(= opcode #x6f) + (make-instruction arch address 4 + (riscv-jal rd (+ address (riscv-imm-j word)) options) + (bv-slice bv offset 4))] + [(= opcode #x67) + (make-instruction arch address 4 + (riscv-load "jalr" rd rs1 (riscv-imm-i word) options) + (bv-slice bv offset 4))] + [else (riscv-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)] + [(riscv32 riscv64) (decode-riscv arch bv offset address options)] [else (error 'decode-instruction "decoder not implemented for architecture" arch)])) (define (disassemble-bytevector arch bv . maybe-options) --- a/lib/jasm/object/elf.ss +++ b/lib/jasm/object/elf.ss @@ -218,6 +218,10 @@ (case (elf-object-class obj) [(elf64) (if (eq? (elf-object-endian obj) 'little) 'mips64el 'mips64)] [else (if (eq? (elf-object-endian obj) 'little) 'mipsel 'mips)])] + [(243) + (case (elf-object-class obj) + [(elf64) 'riscv64] + [else 'riscv32])] [else (error 'elf-object-architecture "unsupported ELF machine" machine)]))) (define (read-c-string bv offset limit) --- a/main.ss +++ b/main.ss @@ -8,7 +8,7 @@ (define (usage) (display "Usage:\n") - (display " jasm raw --arch <x86-64|aarch64|mips|mipsel|mips64|mips64el> [--syntax att|intel] [--address n] [--max-bytes n] [--stop-address n] (--hex <bytes>|--file path)\n") + (display " jasm raw --arch <x86-64|aarch64|mips|mipsel|mips64|mips64el|riscv32|riscv64> [--syntax att|intel] [--address n] [--max-bytes n] [--stop-address n] (--hex <bytes>|--file path)\n") (display " jasm object [--arch <arch>] [--syntax att|intel] [--section name] <file>\n") (display " jasm headers <file>\n") (display " jasm sections <file>\n") --- a/tests/test-jasm.ss +++ b/tests/test-jasm.ss @@ -225,6 +225,10 @@ (normalize-architecture 'mips32el) 'mipsel) +(test "normalize architecture rv64" + (normalize-architecture 'rv64) + 'riscv64) + (test "architecture endianness" (list (architecture-endianness 'mips) (architecture-endianness 'mips64el)) @@ -293,6 +297,13 @@ result)) '(elf64 ".text")) +(test "elf riscv64 architecture" + (let ([bv (sample-elf64le)]) + (bytevector-u8-set! bv 18 #xf3) + (bytevector-u8-set! bv 19 0) + (elf-object-architecture (read-elf-bytevector bv))) + 'riscv64) + (test "mach-o section discovery" (let* ([obj (read-macho-bytevector (sample-macho64le))] [text (macho-section-by-name obj "__TEXT,__text")]) @@ -431,6 +442,26 @@ (one-text 'mips "27 bd ff e0" '((registers . numeric))) "addiu\t$29, $29, -32") +(test "riscv addi" + (one-text 'riscv64 "13 01 01 ff") + "addi\tsp, sp, -16") + +(test "riscv sd" + (one-text 'riscv64 "23 34 11 00") + "sd\tra, 8(sp)") + +(test "riscv ld" + (one-text 'riscv64 "83 30 81 00") + "ld\tra, 8(sp)") + +(test "riscv ret" + (one-text 'riscv64 "67 80 00 00") + "ret") + +(test "riscv numeric registers" + (one-text 'riscv64 "13 01 01 ff" '((registers . numeric))) + "addi\tx2, x2, -16") + (test "format string" (disassemble->string 'x86-64 (hex-string->bytevector "55 c3")