Add pure Jerboa jasm plan
ober
3286d83acf96bd5372c868f7381c45fe413f7bc3
new file mode 100644 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.so +*.wpo +*.wp.so +*-main.c +petite_boot.h +scheme_boot.h +program_boot.h new file mode 100644 --- /dev/null +++ b/LICENSE @@ -0,0 +1,6 @@ +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +Copyright 2026 jasm contributors + +Licensed under the Apache License, Version 2.0 with LLVM Exceptions. See +https://llvm.org/LICENSE.txt for the license text. new file mode 100644 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +JERBOA_HOME ?= /Users/user/mine/jerboa +SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme +LIBDIRS := $(CURDIR)/lib +BINARY := jasm + +.PHONY: test check run binary test-binary clean + +test: + $(SCHEME) --libdirs $(LIBDIRS) --script tests/test-jasm.ss + +check: test + +run: + $(SCHEME) --libdirs $(LIBDIRS) --script main.ss --help + +binary: test + JERBOA_HOME=$(JERBOA_HOME) \ + SCHEME=$(SCHEME) \ + BINARY_LIBDIRS=$(LIBDIRS) \ + $(JERBOA_HOME)/support/build-binary.sh main.ss $(BINARY) + +test-binary: binary + test -x ./$(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' + +clean: + rm -f $(BINARY) $(BINARY).wp.so $(BINARY)-main.c petite_boot.h scheme_boot.h program_boot.h + find . \( -name '*.so' -o -name '*.wpo' \) -print0 | xargs -0 rm -f new file mode 100644 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# jasm + +`jasm` is a pure Jerboa binary-to-assembly library and CLI. + +The goal is to grow toward `llvm-objdump`-style decoding and output parity +without linking to LLVM, shelling out to LLVM tools, using FFI, or requiring +external libraries at runtime. + +## Current Scope + +- Pure Jerboa decoder core. +- Seed x86-64 decoder for common prologue/control-flow instructions. +- Seed AArch64 decoder for common integer/load/store/return instructions. +- A Jerboa CLI for raw byte disassembly. + +Object-file readers and broader ISA tables should be added in Jerboa modules. + +## Run + +```sh +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' +``` + +## Library API + +```scheme +(import (jasm disasm)) + +(disassemble->string 'x86-64 (hex-string->bytevector "55 48 89 e5") + '((syntax . intel) (address . 0))) +``` new file mode 100644 --- /dev/null +++ b/docs/plan.md @@ -0,0 +1,711 @@ +# jasm Implementation Plan + +`jasm` is a pure Jerboa binary-to-assembly library and executable. It must not +use FFI, dynamic libraries, external disassembler libraries, or shell out to +installed tools for decoding. LLVM source and documentation may be used only as +reference material and parity targets. + +## Goals + +- Produce a real `jasm` binary, not a script. +- Provide a reusable Jerboa library API for Jerboa's future `disassemble` + facility. +- Decode raw bytevectors into structured instruction records. +- Format decoded instructions as assembly text. +- Read object files and disassemble executable sections. +- Grow toward `llvm-objdump` output and option parity where practical. +- Keep all decoding tables, parsers, and formatters implemented in Jerboa. + +## Non-Goals + +- No FFI. +- No linking to LLVM, binutils, Capstone, or any other external decoder. +- No runtime dependency on installed tools. +- No copying GPL implementation code or tables. +- No guessing instruction bytes from live procedures. Jerboa must provide exact + bytes before this library can disassemble procedure bodies. + +## Current State + +- `./jasm` builds as a native binary. +- `(jasm disasm)` exposes: + - `hex-string->bytevector` + - `decode-instruction` + - `disassemble-bytevector` + - `disassemble->string` +- 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. +- Tests cover the initial decoder slice. + +## Core Architecture + +### Instruction Model + +Define a stable instruction record with fields for: + +- architecture +- address +- size +- raw bytes +- mnemonic +- operands +- formatted text +- instruction class +- branch target, when known +- memory reference metadata, when known +- validity status: `valid`, `unknown`, `reserved`, `unallocated`, `incomplete` +- annotations/comments + +The current alist shape is acceptable for the seed version, but should become a +proper record once field usage stabilizes. + +### Decode Pipeline + +The pipeline should be: + +1. Input source: bytevector or object-file section. +2. Architecture selection. +3. Decoder dispatch by architecture. +4. Instruction record output. +5. Formatter output. +6. CLI/object-file presentation. + +Decoder functions should return structured data first. Text formatting should be +separate so Jerboa callers can consume instructions without reparsing strings. + +### Architecture Registry + +Add a registry module: + +- `(jasm arch registry)` +- `register-architecture!` +- `lookup-architecture` +- `supported-architectures` +- architecture aliases, for example `x86_64 -> x86-64`, `arm64 -> aarch64` +- default syntax and feature profiles per architecture + +Each architecture should provide: + +- decoder entry +- formatter defaults +- valid feature flags +- endianness behavior +- minimum and maximum instruction length +- object-file architecture identifiers + +## Raw Byte Disassembly + +Raw mode is the Jerboa `disassemble` integration point. + +Required API: + +```scheme +(disassemble-bytevector arch bytes options) +(disassemble->string arch bytes options) +(decode-instruction arch bytes offset address options) +``` + +Options should include: + +- `address` +- `syntax` +- `features` +- `cpu` +- `max-bytes` +- `stop-address` +- `show-bytes?` +- `show-addresses?` +- `unknown-policy`: emit `.byte`, error, or skip +- `symbol-resolver` +- `relocation-resolver` + +## CLI Surface + +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 -d file.o +jasm -D file.o +jasm --headers file.o +jasm --section=.text file.o +jasm --syms file.o +jasm --reloc file.o +``` + +Initial commands: + +- `raw` +- `object` +- `headers` +- `sections` +- `symbols` +- `relocs` + +Common options: + +- `--arch` +- `--triple` +- `--syntax` +- `--cpu` +- `--features` +- `--address` +- `--start-address` +- `--stop-address` +- `--section` +- `--show-bytes` +- `--no-show-bytes` +- `--show-addresses` +- `--no-addresses` +- `--print-imm-hex` +- `--demangle`, later +- `--wide` +- `--json`, for tooling + +## Object File Support + +### Object Model + +Introduce `(jasm object)` with records for: + +- object file +- architecture +- format +- endian +- sections +- symbols +- relocations +- segments/program headers +- entry point +- source filename + +### ELF + +Implement ELF first because it is regular and common. + +Required: + +- ELF32 and ELF64 headers. +- Little-endian first, then big-endian. +- Section header table. +- Program header table. +- String tables. +- Symbol tables: `.symtab`, `.dynsym`. +- Relocations: + - `SHT_REL` + - `SHT_RELA` +- Executable section discovery. +- Section filtering. +- Address range filtering. + +Architectures to map early: + +- x86-64 +- i386 +- AArch64 +- ARM +- MIPS 32/64, big-endian and little-endian +- RISC-V 32/64 +- PowerPC 32/64 +- WebAssembly, if using ELF containers where relevant + +### Mach-O + +Implement after ELF. + +Required: + +- 64-bit Mach-O first. +- Universal/fat binaries. +- Load commands. +- Segments and sections. +- Symbol table. +- String table. +- Dysymtab. +- Relocations. +- ARM64 and x86-64 architecture mapping. + +### COFF/PE + +Implement after ELF/Mach-O. + +Required: + +- COFF file header. +- Optional header. +- Section table. +- Symbol table. +- String table. +- Relocations. +- PE image sections. + +### Archives + +Support Unix archives: + +- global header +- member headers +- long filenames +- symbol table members +- nested object iteration + +## x86/x86-64 Decoder Plan + +### Scope + +Grow from seed instructions to a table-driven decoder. Avoid a giant hand-coded +`cond` for the full ISA. + +### Required Decode Layers + +- Prefix parsing: + - legacy prefixes + - segment overrides + - operand-size override + - address-size override + - REP/REPE/REPNE + - LOCK +- REX prefix. +- VEX prefix. +- EVEX prefix. +- XOP prefix, if needed. +- Opcode maps: + - one-byte map + - `0F` + - `0F 38` + - `0F 3A` + - VEX maps + - EVEX maps +- ModR/M. +- SIB. +- displacement decoding. +- immediate decoding. +- relative branch target decoding. +- operand-size resolution. +- address-size resolution. + +### x86 Output Syntax + +Support: + +- AT&T syntax. +- Intel syntax. +- register naming. +- immediate formatting. +- memory operand formatting. +- branch target formatting. + +### x86 Milestones + +1. Integer baseline: + - `mov`, `lea`, `push`, `pop` + - `add`, `sub`, `cmp`, `test` + - `and`, `or`, `xor` + - `inc`, `dec`, `neg`, `not` + - `call`, `ret`, `jmp`, `jcc` + - `nop`, `int3`, `syscall` +2. Common addressing: + - ModR/M all modes. + - SIB all modes. + - RIP-relative addressing. +3. Stack/frame patterns. +4. SSE baseline. +5. AVX/VEX. +6. AVX-512/EVEX. +7. privileged/system instructions. + +## AArch64 Decoder Plan + +### Required Decode Groups + +- Data processing immediate. +- Data processing register. +- Loads/stores. +- Branches. +- Exception generation. +- System instructions. +- Floating point and SIMD. +- Atomics. +- SVE/SME later. + +### AArch64 Milestones + +1. Baseline scalar: + - `mov`, `movz`, `movn`, `movk` + - `add`, `sub`, `cmp`, `cmn` + - logical immediates + - `ldr`, `str`, `ldp`, `stp` + - `b`, `bl`, `br`, `blr`, `ret` + - `cbz`, `cbnz`, `tbz`, `tbnz` +2. PC-relative: + - `adr` + - `adrp` + - literal loads +3. Bitfield/extract aliases. +4. SIMD/floating point baseline. +5. atomics. +6. SVE and SME. + +## ARM/Thumb Plan + +Add after AArch64 baseline is stable. + +Required: + +- ARM state decoder. +- Thumb and Thumb-2 decoder. +- IT blocks. +- condition codes. +- ARM register aliases. +- mixed ARM/Thumb object-file mapping symbols. + +## RISC-V Plan + +RISC-V is a good third architecture because its encoding is regular. + +Required: + +- RV32 and RV64. +- Base integer ISA. +- compressed `C` extension. +- `M`, `A`, `F`, `D`. +- vector extension later. +- feature-driven decode by extension set. + +## MIPS and MIPS64 Plan + +MIPS and MIPS64 should be first-class pure Jerboa decoders. They need explicit +endianness handling because MIPS object files and raw byte streams appear in +both big-endian and little-endian forms. + +### Architecture Names + +Support aliases: + +- `mips` +- `mipsel` +- `mips64` +- `mips64el` +- `mips32` +- `mips32el` + +Canonical architecture names: + +- `mips` +- `mipsel` +- `mips64` +- `mips64el` + +### Required Decode Layers + +- 32-bit fixed-width instruction fetch. +- big-endian and little-endian word readers. +- opcode field dispatch. +- R-type function dispatch. +- REGIMM dispatch. +- coprocessor dispatch. +- SPECIAL2 and SPECIAL3 dispatch. +- immediate sign-extension. +- branch target calculation. +- jump target calculation. +- load/store offset formatting. +- register-name profiles: + - numeric: `$0` ... + - ABI: `$zero`, `$at`, `$v0`, `$a0`, `$t0`, `$s0`, etc. +- MIPS64-specific GPR operations. +- delay-slot awareness metadata. + +### MIPS32 Baseline + +Initial decoder coverage: + +- `nop` +- `addu`, `subu` +- `addiu` +- `and`, `or`, `xor`, `nor` +- `andi`, `ori`, `xori`, `lui` +- `sll`, `srl`, `sra` +- `slt`, `sltu` +- `lw`, `sw`, `lb`, `lbu`, `lh`, `lhu`, `sb`, `sh` +- `beq`, `bne`, `blez`, `bgtz` +- `j`, `jal`, `jr`, `jalr` +- `syscall`, `break` + +### MIPS64 Baseline + +MIPS64 builds on MIPS32 and adds: + +- 64-bit register operation formatting. +- `daddu`, `dsubu` +- `daddiu` +- `ld`, `sd` +- `dsll`, `dsrl`, `dsra` +- `dsll32`, `dsrl32`, `dsra32` +- 64-bit load/store and branch output tests. + +### MIPS Object Integration + +ELF support must map: + +- `EM_MIPS` +- ELF class 32/64 +- data encoding big/little endian +- ABI flags where available +- `.MIPS.abiflags` +- MIPS relocation records +- microMIPS/MIPS16 markers later + +### MIPS Milestones + +1. Raw big-endian MIPS32 seed decoder. +2. Raw little-endian MIPS32 seed decoder. +3. Raw big-endian MIPS64 seed decoder. +4. Raw little-endian MIPS64 seed decoder. +5. ABI register-name formatter. +6. ELF `EM_MIPS` detection and `.text` disassembly. +7. relocation and symbol annotations. +8. microMIPS and MIPS16 investigation. + +## Other Architecture Roadmap + +Add architecture modules in this rough order: + +1. x86-64 +2. AArch64 +3. MIPS/MIPS64 +4. RISC-V +5. ARM/Thumb +6. WebAssembly +7. PowerPC +8. SystemZ +9. BPF +10. LoongArch +11. SPARC +12. MSP430/AVR and other embedded targets + +Each addition needs: + +- architecture alias registration +- raw decode tests +- object-file mapping +- formatter tests +- parity samples against LLVM output where licensing permits comparison by + generated output, not copied source. + +## Formatting + +Create `(jasm format)`: + +- instruction line formatting +- address column +- bytes column +- labels/symbols +- reloc annotations +- comments +- architecture-specific syntax hooks +- JSON output + +Formatter options: + +- `show-addresses?` +- `show-bytes?` +- `bytes-width` +- `symbolic?` +- `print-imm-hex?` +- `syntax` +- `color?` + +## Symbol and Relocation Handling + +A disassembler without symbols is useful for raw procedure bytes, but object +output needs symbolic context. + +Required: + +- symbol lookup by address. +- nearest symbol before address. +- local/global symbol visibility. +- function boundary detection. +- relocation lookup by section offset. +- inline relocation comments. +- branch target labels. + +## Jerboa Integration + +Jerboa's future `(std debug disassemble)` should call this library once it has +exact code bytes. + +Required exported API shape: + +```scheme +(procedure-bytes->disassembly arch bytes metadata options) +``` + +Expected metadata: + +- procedure name +- source location +- machine type +- entry address +- exact byte length +- relocation information, if available + +`jasm` should not read live process memory. Jerboa owns byte extraction. + +## Testing Strategy + +### Unit Tests + +- one instruction per opcode pattern. +- invalid/truncated instruction cases. +- immediate sign-extension cases. +- branch target calculation. +- memory operand formatting. +- syntax variants. + +### Golden Tests + +Maintain fixture files: + +- raw byte fixtures +- expected instruction records +- expected formatted output + +### Parity Tests + +Where an LLVM tool is available on the developer machine, use it only as an +optional test oracle: + +- generate comparison output +- normalize addresses/spacing +- compare text + +These tests must be optional and not required for normal `make test`. + +### Object Fixtures + +Add small checked-in binary fixtures only when licensing/provenance is clear. +Prefer generating fixtures from Jerboa or tiny assembly sources during tests. + +## Performance Plan + +- Keep byte access allocation-free in inner decode loops. +- Decode to records first; format only when requested. +- Use vectors for opcode tables. +- Avoid string building during decode except for early seed implementation. +- Add benchmarks for large `.text` sections. + +## Error Handling + +Unknown bytes should not crash disassembly. + +Policies: + +- raw mode default: emit `.byte`. +- strict mode: raise decode error. +- object mode default: emit `.byte` or `.word` and continue. +- truncated input: emit incomplete record and stop or strict error. + +## Repository Layout + +Planned layout: + +```text +main.ss +lib/jasm/disasm.ss +lib/jasm/arch/registry.ss +lib/jasm/arch/x86.ss +lib/jasm/arch/aarch64.ss +lib/jasm/object/elf.ss +lib/jasm/object/macho.ss +lib/jasm/object/coff.ss +lib/jasm/format.ss +tests/test-jasm.ss +tests/fixtures/ +docs/plan.md +``` + +## Milestones + +### Milestone 1: Correct Project Shape + +- Real `jasm` binary. +- Pure Jerboa raw-byte disassembly. +- Seed x86-64 and AArch64 support. +- Basic tests. + +Status: started. + +### Milestone 2: Decoder Records and Formatting Split + +- Replace text-first instruction records with structured records. +- Add formatter module. +- Keep current string API as compatibility wrapper. + +### Milestone 3: x86-64 Baseline Decoder + +- Prefix parser. +- ModR/M and SIB parser. +- Integer baseline. +- AT&T and Intel syntax. +- Common compiler output decodes cleanly. + +### Milestone 4: AArch64 Baseline Decoder + +- Table/group decoder. +- Scalar baseline. +- branch/load/store coverage. +- common compiler output decodes cleanly. + +### Milestone 5: ELF Reader + +- ELF64 little-endian first. +- section table and symbols. +- disassemble `.text`. +- `jasm -d file.o`. + +### Milestone 6: Mach-O Reader + +- Mach-O 64-bit. +- arm64 and x86-64 objects. +- universal binaries. + +### Milestone 7: RISC-V Baseline + +- RV32/RV64 base integer. +- compressed extension. +- ELF integration. + +### Milestone 8: MIPS/MIPS64 Baseline + +- MIPS32 and MIPS64 canonical architecture aliases. +- big-endian and little-endian raw-byte decoders. +- baseline integer/load/store/branch/jump coverage. +- ABI register-name output. +- ELF `EM_MIPS` integration. + +### Milestone 9: CLI Parity Pass + +- `llvm-objdump`-like option names. +- headers/sections/symbols/relocations. +- address ranges and section filters. + +### Milestone 10: Jerboa Disassemble Integration + +- stable bytevector API. +- metadata-aware report formatting. +- tests with synthetic procedure byte fixtures. + +## Immediate Next Steps + +1. Split current `(jasm disasm)` into architecture modules. +2. Replace instruction alists with records. +3. Add x86 prefix, ModR/M, and SIB parsers. +4. Add AArch64 decode-group dispatch. +5. Add MIPS/MIPS64 raw decoders for both endian modes. +6. Add an ELF64 little-endian reader skeleton. +7. Expand tests before adding more opcodes. new file mode 100755 Binary files /dev/null and b/jasm differ new file mode 100644 --- /dev/null +++ b/lib/jasm/disasm.ss @@ -0,0 +1,368 @@ +#!chezscheme + +(library (jasm disasm) + (export + supported-architectures + hex-string->bytevector + disassemble-bytevector + disassemble->string + decode-instruction + instruction? + instruction-arch + instruction-address + instruction-size + instruction-text + instruction-bytes) + + (import (chezscheme)) + + (define supported-architectures '(x86-64 aarch64)) + + (define (instruction? x) + (and (pair? x) (eq? (car x) 'instruction))) + + (define (make-instruction arch address size text bytes) + (list 'instruction + (cons 'arch arch) + (cons 'address address) + (cons 'size size) + (cons 'text text) + (cons 'bytes bytes))) + + (define (instruction-ref insn key) + (let ([cell (assq key (cdr insn))]) + (if cell (cdr cell) #f))) + + (define (instruction-arch insn) (instruction-ref insn 'arch)) + (define (instruction-address insn) (instruction-ref insn 'address)) + (define (instruction-size insn) (instruction-ref insn 'size)) + (define (instruction-text insn) (instruction-ref insn 'text)) + (define (instruction-bytes insn) (instruction-ref insn 'bytes)) + + (define (option-ref options key default) + (let ([cell (assq key options)]) + (if cell (cdr cell) default))) + + (define (hex-digit c) + (cond + [(and (char>=? c #\0) (char<=? c #\9)) + (- (char->integer c) (char->integer #\0))] + [(and (char>=? c #\a) (char<=? c #\f)) + (+ 10 (- (char->integer c) (char->integer #\a)))] + [(and (char>=? c #\A) (char<=? c #\F)) + (+ 10 (- (char->integer c) (char->integer #\A)))] + [else #f])) + + (define (hex-skip? c) + (or (char-whitespace? c) + (char=? c #\,) + (char=? c #\:) + (char=? c #\_))) + + (define (clean-hex-string s) + (let ([n (string-length s)]) + (let loop ([i 0] [out '()]) + (cond + [(= i n) (list->string (reverse out))] + [(and (< (+ i 1) n) + (char=? (string-ref s i) #\0) + (or (char=? (string-ref s (+ i 1)) #\x) + (char=? (string-ref s (+ i 1)) #\X))) + (loop (+ i 2) out)] + [(hex-skip? (string-ref s i)) + (loop (+ i 1) out)] + [else + (loop (+ i 1) (cons (string-ref s i) out))])))) + + (define (hex-string->bytevector s) + (let* ([clean (clean-hex-string s)] + [n (string-length clean)]) + (when (or (= n 0) (not (= (modulo n 2) 0))) + (error 'hex-string->bytevector "hex string must contain an even number of digits" s)) + (let ([bv (make-bytevector (quotient n 2) 0)]) + (let loop ([i 0] [j 0]) + (if (= i n) + bv + (let ([hi (hex-digit (string-ref clean i))] + [lo (hex-digit (string-ref clean (+ i 1)))]) + (unless (and hi lo) + (error 'hex-string->bytevector "invalid hex digit" s)) + (bytevector-u8-set! bv j (+ (* hi 16) lo)) + (loop (+ i 2) (+ j 1)))))))) + + (define (bv-slice bv start len) + (let ([out (make-bytevector len 0)]) + (bytevector-copy! bv start out 0 len) + out)) + + (define (byte->hex2 b) + (let ([digits "0123456789abcdef"]) + (string (string-ref digits (quotient b 16)) + (string-ref digits (modulo b 16))))) + + (define (ascii-downcase c) + (if (and (char>=? c #\A) (char<=? c #\Z)) + (integer->char (+ (char->integer #\a) + (- (char->integer c) (char->integer #\A)))) + c)) + + (define (number->hex n) + (list->string (map ascii-downcase + (string->list (number->string n 16))))) + + (define (hex-number n) + (string-append "0x" (number->hex n))) + + (define (signed8 b) + (if (>= b 128) (- b 256) b)) + + (define (signed32/u32 x) + (if (>= x #x80000000) (- x #x100000000) x)) + + (define (bv-u8 bv offset) + (bytevector-u8-ref bv offset)) + + (define (bv-u32-le bv offset) + (+ (bv-u8 bv offset) + (* (bv-u8 bv (+ offset 1)) #x100) + (* (bv-u8 bv (+ offset 2)) #x10000) + (* (bv-u8 bv (+ offset 3)) #x1000000))) + + (define (bits n hi lo) + (modulo (quotient n (expt 2 lo)) (expt 2 (+ 1 (- hi lo))))) + + (define x86-reg64 + '#("rax" "rcx" "rdx" "rbx" "rsp" "rbp" "rsi" "rdi" + "r8" "r9" "r10" "r11" "r12" "r13" "r14" "r15")) + + (define x86-reg32 + '#("eax" "ecx" "edx" "ebx" "esp" "ebp" "esi" "edi" + "r8d" "r9d" "r10d" "r11d" "r12d" "r13d" "r14d" "r15d")) + + (define (x86-reg n bits) + (vector-ref (if (= bits 64) x86-reg64 x86-reg32) n)) + + (define (x86-att-reg name) + (string-append "%" name)) + + ;; Chez rejects accidental comma identifiers less clearly than a reader + ;; error here would. Keep this tiny helper separate so formatting is obvious. + (define (x86-intel-binop mnemonic dst src) + (string-append mnemonic "\t" dst ", " src)) + + (define (x86-att-binop mnemonic suffix dst src) + (string-append mnemonic suffix "\t" (x86-att-reg src) ", " (x86-att-reg dst))) + + (define (x86-format-binop mnemonic suffix syntax dst src) + (if (eq? syntax 'att) + (x86-att-binop mnemonic suffix dst src) + (x86-intel-binop mnemonic dst src))) + + (define (x86-format-immop mnemonic suffix syntax dst imm) + (let ([imm-text (if (>= imm 10) (hex-number imm) (number->string imm))]) + (if (eq? syntax 'att) + (string-append mnemonic suffix "\t$" imm-text ", " (x86-att-reg dst)) + (string-append mnemonic "\t" dst ", " imm-text)))) + + (define (x86-unknown bv offset address) + (make-instruction 'x86-64 address 1 + (string-append ".byte\t0x" (byte->hex2 (bv-u8 bv offset))) + (bv-slice bv offset 1))) + + (define (decode-x86-64 bv offset address options) + (let* ([len (bytevector-length bv)] + [syntax (option-ref options 'syntax 'att)]) + (if (>= offset len) + #f + (let* ([b0 (bv-u8 bv offset)] + [has-rex? (and (>= b0 #x40) (<= b0 #x4f))] + [rex (if has-rex? b0 0)] + [rex-w? (not (= (modulo (quotient rex 8) 2) 0))] + [rex-r (if (= (modulo (quotient rex 4) 2) 0) 0 8)] + [rex-b (if (= (modulo rex 2) 0) 0 8)] + [opoff (if has-rex? (+ offset 1) offset)]) + (if (>= opoff len) + (x86-unknown bv offset address) + (let ([op (bv-u8 bv opoff)]) + (cond + [(and (>= op #x50) (<= op #x57)) + (let* ([reg (x86-reg (+ (- op #x50) rex-b) 64)] + [text (if (eq? syntax 'att) + (string-append "pushq\t" (x86-att-reg reg)) + (string-append "push\t" reg))] + [size (+ 1 (if has-rex? 1 0))]) + (make-instruction 'x86-64 address size text + (bv-slice bv offset size)))] + [(= op #xc3) + (let ([size (+ 1 (if has-rex? 1 0))])