# Command line The reference below is taken from the program's own `--help`. If the two disagree, the program is right and this file is a defect. The same reference is installed as man pages: `just install-man` puts gasm(1) and one page per command into ~/.local/share/man (`MANDIR` overrides), and a test compares each page against the binary so the two cannot drift apart. ## Synopsis ```sh gasm [global flags] [command flags] [arguments] ``` ## Commands | Command | Purpose | |---|---| | `tokens` | print the lexical token stream | | `parse` | parse a file and report syntax errors | | `fmt` | canonicalise the formatting of `.s` files | | `lint` | run the static checks | | `asm` | assemble `.s` files to machine code | | `dis` | disassemble machine code or an assembled file | | `verify` | JIT-assemble and run the dynamic checks | | `debug` | interactive source-level debugger | | `diff` | compare the machine code of two `.s` files | | `profile` | show the basic-block structure of the functions | | `audit-instructions` | diff the encoder against the toolchain's name table | | `scaffold` | generate a differential test skeleton for a kernel | | `lsp` | run the language server over stdio | | `version` | print the version | ## tokens ```text Usage: gasm tokens ``` Print the lexical token stream of FILE: position, token kind and text, one token per line. FILE may be `-` to read standard input. ```sh gasm tokens hello_amd64.s ``` ```text 1:1 # "#" 1:2 IDENT "include" 1:10 STRING "\"textflag.h\"" ``` ## parse ```text Usage: gasm parse ``` Parse FILE and report syntax errors on stderr. On success, print how many declarations and TEXT functions the file contains. ```sh gasm parse hello_amd64.s ``` ```text hello_amd64.s: OK, 2 declarations, 1 functions ``` ## fmt ```text Usage: gasm fmt [-w|-l|-d] [path...] ``` | Flag | Default | Effect | |---|---|---| | `-w` | off | write the result back to the source file | | `-l` | off | list the files whose formatting differs; write nothing | | `-d` | off | print a unified diff of the canonical formatting instead | `-l` and `-d` are mutually exclusive. With no arguments, or with a directory argument, every `.s` file below it is reformatted in place and the names of the changed files are listed, the way `go fmt` does; `.` and `_` directories are skipped. Explicit file arguments print to stdout unless `-w` is given. ```sh gasm fmt -l kernel_amd64.s ``` Empty output means every file is formatted, which is the shape a CI check wants; `-d` shows what would change: ```sh gasm fmt -d ugly_amd64.s ``` ```text --- ugly_amd64.s +++ ugly_amd64.s @@ -2,8 +2,8 @@ // func add(a, b int) int TEXT ·add(SB), NOSPLIT, $0-24 - MOVQ a+0(FP), AX - ADDQ b+8(FP), AX + MOVQ a+0(FP), AX + ADDQ b+8(FP), AX ``` ## lint ```text Usage: gasm lint ``` | Flag | Default | Effect | |---|---|---| | `-disable` | empty | comma-separated rule codes to disable | Diagnostics are printed as `file:line:col: severity: message [code]`. The exit status is non-zero when an error-severity diagnostic is found; warnings (the register-clobber audit, for example) do not affect it. Rules: `unknown-instruction`, `operand-count`, `undefined-label`, `duplicate-label`, `missing-ret`, `missing-textflag-include`, `abi-argsize`, `unreachable-code`, `register-clobber`, `funcdata-pcdata`, `unused-label`, `invalid-textflag`, `stack-imbalance`, `register-width-mismatch`, `abi0-register-args`, `nonportable-register-name`, `unencodable-instruction` and `reserved-register-write`. ```sh gasm lint kernel_amd64.s ``` ## asm ```text Usage: gasm asm [--format raw|elf|goobj] [-p pkg] [-GOARCH arch] [-o out] ``` | Flag | Default | Effect | |---|---|---| | `-format` | `raw` | output format: `raw` (concatenated image), `elf` or `goobj` (Go object) | | `-p` | empty | package path for `--format goobj`, qualifying the exported symbols | | `-GOARCH` | empty | target architecture: `amd64`, `arm64`, `riscv64` or `loong64`; overrides the file-name suffix | | `-o` | empty | write the output to this file instead of a hex dump on stdout | Supported architectures: amd64 (VEX/AVX2 and EVEX/AVX-512 included), arm64, riscv64 (RV64IMAFDC and RVC) and loong64, taken from the file's `_arch.s` suffix or from `-GOARCH`, which is how files whose names carry no recognisable suffix (most of GOROOT's, for example `cpu_x86.s`) are assembled. `raw` concatenates the functions and the data section into one self-consistent image; `elf` emits a relocatable object that links with the system toolchain; `goobj` emits the Go toolchain's own object format, which `cmd/link` consumes directly. ```sh gasm asm hello_amd64.s ``` ```text add: 16 bytes 0000: 48 8b 44 24 08 48 03 44 24 10 48 89 44 24 18 c3 ``` ## dis ```text Usage: gasm dis [-a arch] ``` | Flag | Default | Effect | |---|---|---| | `-a` | empty | architecture for raw input without a `_arch.s` name | With a `.s` file the file is assembled first and the listing follows the real layout: one block per `TEXT` function, local labels printed at their offsets. With any other file, or `-` for standard input, the bytes are disassembled linearly and `-a` selects the architecture (amd64, arm64, riscv64 or loong64). ```sh gasm dis hello_amd64.s ``` ```text add: 16 bytes 0000: 48 8b 44 24 08 mov rax, qword ptr [rsp+0x8] 0005: 48 03 44 24 10 add rax, qword ptr [rsp+0x10] 000a: 48 89 44 24 18 mov qword ptr [rsp+0x18], rax 000f: c3 ret ``` ## verify ```text Usage: gasm verify [-smoke] [-abi] [-fuzz] [-ground-truth] [-profile] [-call] ``` | Flag | Default | Effect | |---|---|---| | `--ground-truth` | off | compare the machine code byte-for-byte against `go tool asm` | | `--fuzz` | off | differential fuzz against the `go tool asm` build | | `-n` | 1000 | fuzz iterations per function | | `--abi` | off | ABI-checking calls: sentinel registers and a red-zone canary | | `--abi-n` | 100 | ABI check iterations with varied inputs | | `--profile` | off | list the basic-block structure per function | | `--smoke` | off | call each NOSPLIT function with zeroed arguments | | `--call` | empty | invoke a single function with `--buf` instead of the sweeps | | `--buf` | empty | buffer spec for `--call`: `name:size:pattern[,name:size:pattern]` | | `--args` | empty | scalar args for `--call`: `name=value[,name=value]` (decimal or `0x` hex) | | `--repeat` | 1 | number of times to repeat a `--call` invocation | | `--save-corpus` | empty | with `--fuzz`: write each failing input to this directory as replayable JSON | | `--replay` | empty | re-run saved corpus entries, one child process per entry | The JIT checks run when the host matches the file's architecture; the toolchain comparison works everywhere. `--fuzz`, `--smoke` and `--abi` run each function in its own child process, so a partial function that faults on random input is reported as `CRASH` instead of ending the sweep; `--call` with `--buf` invokes such a function with valid data. loong64 stays on the ground-truth path until hardware validation. ```sh gasm verify --ground-truth hello_amd64.s ``` ```text hello_amd64.s: 1 functions JIT-loaded add: MATCH (16 bytes) ground truth: 1/1 functions byte-identical add: 16 bytes, args=24, frame=0 NOSPLIT ``` ```sh gasm verify --call add --args a=2,b=3 hello_amd64.s ``` ```text add: 16 bytes, args=24 signature: func add(a int, b int) int scalars: a = 2 b = 3 args before: 02 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (24 bytes) args after: 02 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 (24 bytes) call 1: OK ``` ## debug ```text Usage: gasm debug --func ``` | Flag | Default | Effect | |---|---|---| | `-func` | empty | the function to debug, required | | `-buf` | empty | buffer spec: `name:size:pattern[,name:size:pattern]` (zero, ones, seq or hex) | | `-args` | empty | file containing the ABI0 argument block | | `-script` | empty | run REPL commands from a file, one per line, and exit; `-` reads stdin | | `-timeout` | 0 | kill the debuggee after this duration, for headless `-script` runs | | `-cover` | off | run to completion with a breakpoint on every instruction and report which executed | The debugger spawns the debuggee from the `gasm` binary on `$PATH`, so install it first with `just install`; `go run` does not work for the traced child. Requires Linux (ptrace) and all four architectures are supported. REPL commands: | Command | Effect | |---|---| | `break [if ]` | set a breakpoint, optionally conditional | | `delete ` | remove a breakpoint | | `info break` | list the breakpoints | | `step [n]`, `s` | single-step n instructions | | `next`, `n` | step over a CALL | | `finish`, `fin` | run until the function returns | | `continue`, `c` | run until a breakpoint, watchpoint or exit | | `disas [n]`, `u` | disassemble n instructions at the PC | | `regs` | print the general-purpose and vector/FP registers | | `where` | show the source line and the nearest label at the PC | | `stack` | show the stack near RSP, the return address and the ABI0 args | | `bt`, `backtrace` | backtrace: the current frame and the return address | | `x [addr] [len]` | hex-dump memory | | `w ` | write bytes to memory | | `set ` | set a register | | `watch [r\|w] [size]` | set a hardware watchpoint, write by default | | `unwatch []` | clear one watchpoint or all of them | | `labels`, `l` | list the function's labels and offsets | | `help`, `h`, `?` | show the command help | | `quit`, `q` | kill the debuggee and exit | ```sh gasm debug --func add --cover hello_amd64.s ``` ## diff ```text Usage: gasm diff [-GOARCH arch] ``` | Flag | Default | Effect | |---|---|---| | `-GOARCH` | empty | target architecture for both files, overriding the file-name suffixes | | `-map` | empty | comma-separated `old=new` pairs to match functions with different names | Functions are paired by exact name unless `--map` says otherwise, so `--map wideCopyAVX2=wideCopyAVX512` pairs two variants regardless of suffix. The exit status is non-zero when anything differs. ```sh gasm diff hello_amd64.s hello_amd64.s ``` ```text add: identical (16 bytes) all functions identical ``` ## profile ```text Usage: gasm profile ``` Show the basic-block structure of each function: its labels, their offsets and the block boundaries. This is the static structure; for runtime execution counts use `gasm debug --cover`, and for input coverage `gasm verify --fuzz`. ```sh gasm profile hello_amd64.s ``` ```text add: 16 bytes, args=24, frame=0 NOSPLIT basic blocks: 1 ``` ## audit-instructions ```text Usage: gasm audit-instructions [--corpus [dir]] [amd64|arm64|riscv64|loong64] ``` Compare the gasm encoder for the given architecture (default amd64) against the installed `go tool asm` and print the diff: superset encodings (gasm-only spellings, shippable via `gasm asm --format goobj`), known-but-unencodable names (the encoder backlog) and go-only names (feature gaps). The Go side is probed black-box with a battery of operand shapes per mnemonic, so the audit tracks whatever toolchain `go env GOROOT` provides. On non-amd64 architectures the backlog is an over-approximation: a name counts as encodable only when a probe shape assembles cleanly, so a name whose real forms the battery misses lands in the backlog. ```sh gasm audit-instructions amd64 ``` ```text gasm table (amd64, families excluded): 1542 mnemonics gasm encodable: 580 go tool asm recognized: 1542 shared: 580 ``` With `--corpus` the audit changes shape: it assembles every `.s` file under DIR (default `GOROOT/src`) with the gasm encoder only, no toolchain probing. A file whose name carries a recognisable `_arch` suffix is attempted for that architecture; a file without one is attempted for all four, exactly as a `GOARCH` build would compile it. The report gives the headline number (files that assemble for every target architecture), the per-architecture pass rates and the most common failure reasons with one representative file each, which drive the encodability backlog by frequency rather than by table order. A run over GOROOT takes under a second. ```sh gasm audit-instructions --corpus gasm audit-instructions --corpus "$(go env GOROOT)/src/crypto" ``` ```text corpus /usr/local/go/src: 627 files (365 generic, attempted for all architectures) assemble for every target architecture: 108 (17.2%) amd64: 77/464 attempted 148 instruction not encodable e.g. /usr/local/go/src/cmd/asm/internal/asm/testdata/386enc.s ... ``` ## scaffold ```text Usage: gasm scaffold differential ``` Print a differential test skeleton for every `// func` signature in FILE. The generated test seeds random states, drives the kernel and a portable reference (`Portable`), and compares the outputs byte-for-byte. Write the reference bodies, place the file in the kernel's package, and run it in CI. ```sh gasm scaffold differential kernel_amd64.s > kernel_differential_test.go ``` ## lsp ```text Usage: gasm lsp ``` Run the language server over standard input/output, JSON-RPC 2.0 with `Content-Length` framing. Point an LSP-capable editor at the binary and associate it with `.s` files; the target architecture is inferred from the file-name suffix (`_amd64.s`, `_arm64.s`, `_riscv64.s`, `_loong64.s`). Provides: completion, hover, document symbols, push and pull diagnostics, semantic tokens, go-to-definition, find references, rename, document formatting, inlay hints, code actions, signature help, document highlights, workspace symbol search, #include document links, and folding ranges for function bodies. Definition, references and rename work across every open document. ## version ```text Usage: gasm version ``` Print the version the toolchain recorded for the build, the same string as `gasm --version`: the tag on a tagged checkout, a pseudo-version naming the commit below one, with `+dirty` appended on a dirty tree and `(devel)` outside version control. ## Global flags | Flag | Default | Effect | |---|---|---| | `-h`, `--help` | off | print the usage | | `-V`, `--version` | off | print the version | ## Exit codes | Code | Meaning | |---|---| | `0` | success | | `1` | a failure the program detected: a parse or assembly error, an error-severity lint diagnostic, a mismatch in `verify`, a file that cannot be read | | `2` | the arguments were wrong: a missing or extra argument, an unknown command or format, an invalid `--map` pair | ## Examples Assemble a kernel, check it, and run it: ```sh gasm lint kernel_amd64.s gasm fmt -l kernel_amd64.s gasm asm -o kernel.bin kernel_amd64.s gasm verify --ground-truth kernel_amd64.s ``` Link the kernel into a Go program through the toolchain's own object format: ```sh gasm asm --format goobj -p example.com/kernel -o kernel.o kernel_amd64.s ``` Find which labels a failing kernel reaches, headlessly: ```sh gasm debug --func decodeBlockAVX2 --cover --script cmds.txt --timeout 30s kernel_amd64.s ```