Add bounded raw file disassembly
ober
0f6f582e524d139c380454bb21756fef8c5e6a96
--- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ scheme_boot.h program_boot.h .jasm-sample.o .jasm-test-sample.o +.jasm-raw.bin --- a/Makefile +++ b/Makefile @@ -24,14 +24,16 @@ test-binary: binary file ./$(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' + $(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 ./$(BINARY) headers ./.jasm-sample.o ./$(BINARY) sections ./.jasm-sample.o ./$(BINARY) symbols ./.jasm-sample.o ./$(BINARY) relocs ./.jasm-sample.o ./$(BINARY) object --section .text --syntax intel ./.jasm-sample.o - rm -f ./.jasm-sample.o + rm -f ./.jasm-sample.o ./.jasm-raw.bin clean: - rm -f $(BINARY) $(BINARY).wp.so $(BINARY)-main.c petite_boot.h scheme_boot.h program_boot.h .jasm-sample.o .jasm-test-sample.o + rm -f $(BINARY) $(BINARY).wp.so $(BINARY)-main.c petite_boot.h scheme_boot.h program_boot.h .jasm-sample.o .jasm-test-sample.o .jasm-raw.bin find . \( -name '*.so' -o -name '*.wpo' \) -print0 | xargs -0 rm -f --- a/docs/plan.md +++ b/docs/plan.md @@ -46,6 +46,8 @@ reference material and parity targets. - The `jasm` binary has initial ELF-oriented `object`, `headers`, and `sections`, `symbols`, and `relocs` commands. `object` disassembles executable sections or a selected section by name. +- Raw CLI mode accepts byte input from `--hex` or `--file`. The reusable + bytevector API supports `max-bytes` and `stop-address` limits. ## Core Architecture @@ -121,8 +123,8 @@ Options should include: - `syntax` - `features` - `cpu` -- `max-bytes` -- `stop-address` +- `max-bytes` (implemented) +- `stop-address` (implemented) - `show-bytes?` - `show-addresses?` - `unknown-policy`: emit `.byte`, error, or skip @@ -135,7 +137,7 @@ The binary should eventually support: ```sh jasm raw --arch x86-64 --syntax intel --hex '55 48 89 e5' -jasm raw --arch aarch64 --file code.bin --address 0x1000 +jasm raw --arch aarch64 --file code.bin --address #x1000 jasm -d file.o jasm -D file.o jasm --headers file.o Binary files a/jasm and b/jasm differ --- a/lib/jasm/disasm.ss +++ b/lib/jasm/disasm.ss @@ -571,11 +571,20 @@ (let* ([arch (normalize-architecture arch)] [options (if (null? maybe-options) '() (car maybe-options))] [base (option-ref options 'address 0)] - [len (bytevector-length bv)]) + [max-bytes (option-ref options 'max-bytes #f)] + [stop-address (option-ref options 'stop-address #f)] + [len (if max-bytes + (min (bytevector-length bv) max-bytes) + (bytevector-length bv))]) + (when (and max-bytes (< max-bytes 0)) + (error 'disassemble-bytevector "max-bytes must be non-negative" max-bytes)) (let loop ([offset 0] [address base] [out '()]) - (if (>= offset len) + (if (or (>= offset len) + (and stop-address (>= address stop-address))) (reverse out) (let ([insn (decode-instruction arch bv offset address options)]) + (when (<= (instruction-size insn) 0) + (error 'disassemble-bytevector "decoder returned a zero-sized instruction" insn)) (loop (+ offset (instruction-size insn)) (+ address (instruction-size insn)) (cons insn out))))))) --- a/main.ss +++ b/main.ss @@ -4,7 +4,7 @@ (define (usage) (display "Usage:\n") - (display " jasm raw --arch <x86-64|aarch64|mips|mipsel|mips64|mips64el> [--syntax att|intel] [--address n] --hex <bytes>\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 object [--arch <arch>] [--syntax att|intel] [--section name] <file>\n") (display " jasm headers <file>\n") (display " jasm sections <file>\n") @@ -20,10 +20,19 @@ (normalize-architecture s)) (define (parse-number s) - (let ([n (string->number s 0)]) + (let ([n (string->number s)]) (unless n (error 'jasm "invalid number" s)) n)) +(define (read-file-bytevector path) + (let ([port (open-file-input-port path)]) + (let ([bytes (get-bytevector-all port)]) + (close-port port) + bytes))) + +(define (option-cons key value options) + (if value (cons (cons key value) options) options)) + (define (ascii-downcase c) (if (and (char>=? c #\A) (char<=? c #\Z)) (integer->char (+ (char->integer #\a) @@ -164,25 +173,41 @@ [arch #f] [syntax 'att] [address 0] - [hex #f]) + [hex #f] + [file #f] + [max-bytes #f] + [stop-address #f]) (cond [(null? args) (unless arch (error 'jasm "missing --arch")) - (unless hex (error 'jasm "missing --hex")) - (let ([opts (list (cons 'syntax syntax) (cons 'address address))]) - (display (disassemble->string arch (hex-string->bytevector hex) opts)))] + (when (and hex file) (error 'jasm "use either --hex or --file, not both")) + (unless (or hex file) (error 'jasm "missing --hex or --file")) + (let* ([opts (option-cons 'stop-address stop-address + (option-cons 'max-bytes max-bytes + (list (cons 'syntax syntax) (cons 'address address))))] + [bytes (if hex (hex-string->bytevector hex) (read-file-bytevector file))]) + (display (disassemble->string arch bytes opts)))] [(string=? (car args) "--arch") (when (null? (cdr args)) (error 'jasm "--arch requires a value")) - (loop (cddr args) (string->arch (cadr args)) syntax address hex)] + (loop (cddr args) (string->arch (cadr args)) syntax address hex file max-bytes stop-address)] [(string=? (car args) "--syntax") (when (null? (cdr args)) (error 'jasm "--syntax requires a value")) - (loop (cddr args) arch (parse-syntax (cadr args)) address hex)] + (loop (cddr args) arch (parse-syntax (cadr args)) address hex file max-bytes stop-address)] [(string=? (car args) "--address") (when (null? (cdr args)) (error 'jasm "--address requires a value")) - (loop (cddr args) arch syntax (parse-number (cadr args)) hex)] + (loop (cddr args) arch syntax (parse-number (cadr args)) hex file max-bytes stop-address)] + [(string=? (car args) "--max-bytes") + (when (null? (cdr args)) (error 'jasm "--max-bytes requires a value")) + (loop (cddr args) arch syntax address hex file (parse-number (cadr args)) stop-address)] + [(string=? (car args) "--stop-address") + (when (null? (cdr args)) (error 'jasm "--stop-address requires a value")) + (loop (cddr args) arch syntax address hex file max-bytes (parse-number (cadr args)))] [(string=? (car args) "--hex") (when (null? (cdr args)) (error 'jasm "--hex requires a value")) - (loop (cddr args) arch syntax address (cadr args))] + (loop (cddr args) arch syntax address (cadr args) file max-bytes stop-address)] + [(string=? (car args) "--file") + (when (null? (cdr args)) (error 'jasm "--file requires a value")) + (loop (cddr args) arch syntax address hex (cadr args) max-bytes stop-address)] [else (error 'jasm "unknown raw option" (car args))]))) (define (parse-object args) --- a/tests/test-jasm.ss +++ b/tests/test-jasm.ss @@ -199,6 +199,20 @@ (instruction-text insn))) '(#t x86-64 1 "pushq\t%rbp")) +(test "disassemble max bytes" + (map instruction-text + (disassemble-bytevector 'x86-64 + (hex-string->bytevector "55 c3 90") + '((syntax . intel) (max-bytes . 2)))) + '("push\trbp" "ret")) + +(test "disassemble stop address" + (map instruction-address + (disassemble-bytevector 'x86-64 + (hex-string->bytevector "55 c3 90") + '((address . #x1000) (stop-address . #x1001)))) + '(4096)) + (test "x86 push att" (one-text 'x86-64 "55") "pushq\t%rbp") new file mode 100644 --- /dev/null +++ b/tests/write-raw-bytes.ss @@ -0,0 +1,19 @@ +#!chezscheme + +(import (chezscheme)) + +(define (write-bytevector-file path bv) + (when (file-exists? path) + (delete-file path)) + (let ([port (open-file-output-port path (file-options no-fail))]) + (put-bytevector port bv) + (close-port port))) + +(let ([args (command-line-arguments)]) + (unless (= (length args) 1) + (error 'write-raw-bytes "usage: write-raw-bytes <path>")) + (let ([bv (make-bytevector 3 0)]) + (bytevector-u8-set! bv 0 #x55) + (bytevector-u8-set! bv 1 #xc3) + (bytevector-u8-set! bv 2 #x90) + (write-bytevector-file (car args) bv)))