2026-09-17 20:33:18 +02:00
# Command line
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
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.
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
## Synopsis
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm [ global flags] <command> [ command flags] [ arguments]
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
## Commands
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
| 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 |
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
## tokens
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```text
Usage: gasm tokens <file>
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
Print the lexical token stream of FILE: position, token kind and text, one
token per line. FILE may be `-` to read standard input.
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm tokens hello_amd64.s
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```text
1:1 # "#"
1:2 IDENT "include"
1:10 STRING "\"textflag.h\""
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
## parse
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```text
Usage: gasm parse <file>
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
Parse FILE and report syntax errors on stderr. On success, print how many
declarations and TEXT functions the file contains.
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm parse hello_amd64.s
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```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 <file...>
```
| 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.
2026-08-01 05:22:00 +02:00
Rules: `unknown-instruction` , `operand-count` , `undefined-label` ,
`duplicate-label` , `missing-ret` , `missing-textflag-include` ,
`abi-argsize` , `unreachable-code` , `register-clobber` ,
2026-08-30 10:40:25 +02:00
`funcdata-pcdata` , `unused-label` , `invalid-textflag` ,
`stack-imbalance` , `register-width-mismatch` , `abi0-register-args` ,
2026-09-17 20:33:18 +02:00
`nonportable-register-name` , `unencodable-instruction` and
`reserved-register-write` .
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm lint kernel_amd64.s
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
## asm
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```text
Usage: gasm asm [--format raw|elf|goobj] [-p pkg] [-o out] <file>
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
| 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 |
| `-o` | empty | write the output to this file instead of a hex dump on stdout |
2026-09-14 23:36:19 +02:00
2026-09-17 20:33:18 +02:00
Supported architectures: amd64 (VEX/AVX2 and EVEX/AVX-512 included), arm64,
riscv64 (RV64IMAFDC and RVC) and loong64, selected from the file's `_arch.s`
suffix. `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.
2026-09-14 23:36:19 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm asm hello_amd64.s
```
2026-09-14 23:36:19 +02:00
2026-09-17 20:33:18 +02:00
```text
add: 16 bytes
0000: 48 8b 44 24 08 48 03 44 24 10 48 89 44 24 18 c3
```
2026-09-14 23:36:19 +02:00
2026-09-17 20:33:18 +02:00
## dis
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```text
Usage: gasm dis [-a arch] <file>
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
| Flag | Default | Effect |
|---|---|---|
| `-a` | empty | architecture for raw input without a `_arch.s` name |
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
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).
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm dis hello_amd64.s
```
2026-08-05 21:15:05 +02:00
2026-09-17 20:33:18 +02:00
```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
```
2026-08-30 21:23:43 +02:00
2026-09-17 20:33:18 +02:00
## verify
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```text
Usage: gasm verify [-smoke] [-abi] [-fuzz] [-ground-truth] [-profile] [-call] <file.s>
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
| 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 <file.s> --func <name>
```
| 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.
2026-08-01 05:22:00 +02:00
REPL commands:
2026-09-17 20:33:18 +02:00
| Command | Effect |
|---|---|
| `break <label\|addr> [if <reg> <op> <val>]` | set a breakpoint, optionally conditional |
| `delete <label\|addr>` | 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 <addr> <val...>` | write bytes to memory |
| `set <reg> <value>` | set a register |
| `watch <addr> [r\|w] [size]` | set a hardware watchpoint, write by default |
| `unwatch [<slot>]` | 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 |
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm debug --func add --cover hello_amd64.s
```
2026-08-05 21:15:05 +02:00
2026-09-17 20:33:18 +02:00
## diff
2026-08-05 21:15:05 +02:00
2026-09-17 20:33:18 +02:00
```text
Usage: gasm diff <file1.s> <file2.s>
```
2026-08-05 21:15:05 +02:00
2026-09-17 20:33:18 +02:00
| Flag | Default | Effect |
|---|---|---|
| `-map` | empty | comma-separated `old=new` pairs to match functions with different names |
2026-08-05 21:15:05 +02:00
2026-09-17 20:33:18 +02:00
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.
2026-08-05 21:15:05 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm diff hello_amd64.s hello_amd64.s
```
2026-08-05 21:15:05 +02:00
2026-09-17 20:33:18 +02:00
```text
add: identical (16 bytes)
all functions identical
```
2026-08-30 10:40:25 +02:00
2026-09-17 20:33:18 +02:00
## profile
2026-08-30 10:40:25 +02:00
2026-09-17 20:33:18 +02:00
```text
Usage: gasm profile <file.s>
```
2026-08-30 10:40:25 +02:00
2026-09-17 20:33:18 +02:00
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` .
2026-08-30 10:40:25 +02:00
2026-09-17 20:33:18 +02:00
```sh
gasm profile hello_amd64.s
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
```text
add: 16 bytes, args=24, frame=0 NOSPLIT
basic blocks: 1
```
2026-08-01 05:22:00 +02:00
2026-09-17 20:33:18 +02:00
## audit-instructions
```text
Usage: gasm audit-instructions [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
```
## scaffold
```text
Usage: gasm scaffold differential <file.s>
```
Print a differential test skeleton for every `// func` signature in FILE. The
generated test seeds random states, drives the kernel and a portable reference
(`<name>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
```