14 KiB
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.
Synopsis
gasm [global flags] <command> [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
Usage: gasm tokens <file>
Print the lexical token stream of FILE: position, token kind and text, one
token per line. FILE may be - to read standard input.
gasm tokens hello_amd64.s
1:1 # "#"
1:2 IDENT "include"
1:10 STRING "\"textflag.h\""
parse
Usage: gasm parse <file>
Parse FILE and report syntax errors on stderr. On success, print how many declarations and TEXT functions the file contains.
gasm parse hello_amd64.s
hello_amd64.s: OK, 2 declarations, 1 functions
fmt
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.
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:
gasm fmt -d ugly_amd64.s
--- 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
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.
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.
gasm lint kernel_amd64.s
asm
Usage: gasm asm [--format raw|elf|goobj] [-p pkg] [-GOARCH arch] [-o out] <file>
| 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.
gasm asm hello_amd64.s
add: 16 bytes
0000: 48 8b 44 24 08 48 03 44 24 10 48 89 44 24 18 c3
dis
Usage: gasm dis [-a arch] <file>
| 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).
gasm dis hello_amd64.s
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
Usage: gasm verify [-smoke] [-abi] [-fuzz] [-ground-truth] [-profile] [-call] <file.s>
| 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.
gasm verify --ground-truth hello_amd64.s
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
gasm verify --call add --args a=2,b=3 hello_amd64.s
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
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.
REPL commands:
| 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 |
gasm debug --func add --cover hello_amd64.s
diff
Usage: gasm diff [-GOARCH arch] <file1.s> <file2.s>
| 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.
gasm diff hello_amd64.s hello_amd64.s
add: identical (16 bytes)
all functions identical
profile
Usage: gasm profile <file.s>
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.
gasm profile hello_amd64.s
add: 16 bytes, args=24, frame=0 NOSPLIT
basic blocks: 1
audit-instructions
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.
gasm audit-instructions amd64
gasm table (amd64, families excluded): 1542 mnemonics
gasm encodable: 580 go tool asm recognized: 1542
shared: 580
scaffold
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.
gasm scaffold differential kernel_amd64.s > kernel_differential_test.go
lsp
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
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:
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:
gasm asm --format goobj -p example.com/kernel -o kernel.o kernel_amd64.s
Find which labels a failing kernel reaches, headlessly:
gasm debug --func decodeBlockAVX2 --cover --script cmds.txt --timeout 30s kernel_amd64.s