145 lines
6.8 KiB
Markdown
145 lines
6.8 KiB
Markdown
# gasm-devkit
|
|
|
|
Developer tooling for **GAsm** — Go's built-in Plan 9 assembler.
|
|
|
|
Go ships an assembler but no tooling for it. There is no syntax highlighting,
|
|
no autocomplete, no linter, no static analyser, no formatter, no standalone
|
|
assembler and no debugger for `.s` files. Developers write assembly blind,
|
|
validate it by benchmark, and debug it by print statement.
|
|
|
|
gasm-devkit is the missing toolkit. It is a single, self-contained binary —
|
|
`gasm` — that brings proper developer tooling to Plan 9 assembly:
|
|
|
|
```
|
|
gasm tokens dump the lexical token stream
|
|
gasm parse parse and report syntax errors
|
|
gasm fmt canonicalise formatting (gofmt for assembly)
|
|
gasm lint static checks
|
|
gasm lsp language server (completion, hover, symbols, diagnostics, highlighting)
|
|
gasm asm standalone assembler
|
|
gasm verify dynamic analysis & verification
|
|
gasm debug source-level debugger
|
|
gasm diff compare machine code of two .s files
|
|
gasm profile show basic-block structure of functions
|
|
```
|
|
|
|
## Architecture support
|
|
|
|
gasm-devkit targets every architecture Go's assembler speaks. The instruction
|
|
tables are **generated from the Go toolchain's own assembler source**
|
|
(`cmd/internal/obj/<arch>`), so gasm-devkit recognises *every* mnemonic the
|
|
real assembler accepts — not a hand-maintained subset that drifts and rots.
|
|
|
|
| Architecture | GOARCH | File suffix | Instructions recognised |
|
|
|--------------|-------------|----------------|------------------------------------|
|
|
| AMD64 | `amd64` | `_amd64.s` | 1600 + common opcodes + traditional aliases |
|
|
| ARM64 | `arm64` | `_arm64.s` | 538 + common opcodes |
|
|
| RISC-V | `riscv64` | `_riscv64.s` | 961 + common opcodes |
|
|
| LoongArch | `loong64` | `_loong64.s` | 799 + common opcodes |
|
|
|
|
"Common opcodes" are the instructions shared by every architecture (`RET`,
|
|
`JMP`, `NOP`, `CALL`, `TEXT`, `FUNCDATA`, `PCDATA`, …). AMD64 additionally
|
|
carries the traditional conditional-jump spellings (`JZ`, `JNZ`, `JA`, `JC`,
|
|
…) that the assembler accepts as aliases. Regenerating the tables is one
|
|
command — `just gen` — and requires only a Go installation; the committed
|
|
output has no runtime dependency on the toolchain.
|
|
|
|
## Supported Platforms
|
|
|
|
The toolkit runs on Linux. All four Linux architectures are supported as
|
|
hosts — amd64, arm64, riscv64 and loong64 — and the release matrix
|
|
cross-compiles the same four targets.
|
|
|
|
**FreeBSD support is planned for a future release.**
|
|
|
|
## Principles
|
|
|
|
- **Pure Go and GAsm only.** No C, no cgo, no external toolchains, no native
|
|
binaries, no JavaScript runtimes. The parser is hand-written; there is no
|
|
parser generator.
|
|
- **Self-contained.** The toolkit's production code depends only on the
|
|
standard library; one binary, no runtime data files. The single module
|
|
dependency, `golang.org/x/arch`, is used **only in tests** to validate the
|
|
instruction encoder by round-trip decoding — it is never linked into the
|
|
`gasm` binary.
|
|
- **Linux-only.** Runs natively on amd64, arm64, riscv64 and loong64 Linux
|
|
hosts; the release matrix cross-compiles the same four targets. Latest
|
|
stable Go only.
|
|
- **No vendor lock-in.** The integration surface is the Language Server
|
|
Protocol and a command-line interface — both open standards. No cloud
|
|
service, no proprietary API, no dependence on any one editor's internals.
|
|
- **Complete and verifiable.** Instruction coverage is generated from the
|
|
assembler's own source and regenerated on demand, so it cannot silently fall
|
|
behind the toolchain.
|
|
|
|
## Components
|
|
|
|
| Package | Purpose |
|
|
|---------|---------|
|
|
| `token` | Lexical token kinds and source positions. |
|
|
| `lexer` | Hand-written scanner for Plan 9 assembly. |
|
|
| `ast` | The abstract syntax tree. |
|
|
| `parser` | Line-oriented, error-tolerant parser producing the AST. |
|
|
| `arch` | amd64, arm64, riscv64 and loong64 register files and instruction tables. |
|
|
| `lint` | Conservative static checks. |
|
|
| `format` | A canonical formatter — `gofmt` for assembly. |
|
|
| `asm` | The standalone assembler: amd64, RISC-V and LoongArch encoders, linker, object-file emitters (ELF, GOOBJ). |
|
|
| `verify` | JIT execution substrate for dynamic analysis, combined ABI+fuzz differential testing. |
|
|
| `debug` | Interactive ptrace debugger with GPR/YMM register display and named buffer allocation. |
|
|
| `lsp` | Language Server Protocol server. |
|
|
| `cmd/gasm` | The `gasm` binary tying it all together. |
|
|
| `_gen` | The generator that rebuilds the instruction tables from the Go toolchain. |
|
|
|
|
See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the design rationale and
|
|
data flow, and [`docs/DECISIONS.md`](docs/DECISIONS.md) for design decisions
|
|
deliberately postponed (with the analysis needed to pick them up again).
|
|
|
|
## Quick start
|
|
|
|
```sh
|
|
just install # download dependencies (there are none)
|
|
just build # go vet + gofmt check — zero errors, zero warnings
|
|
just test # full suite, race detector, 80 % coverage gate
|
|
just fmt # gofmt the tree
|
|
just gen # regenerate the instruction tables from the Go toolchain
|
|
```
|
|
|
|
Install the binary and use it:
|
|
|
|
```sh
|
|
just install-bin # installs gasm into $GOBIN
|
|
|
|
gasm --help # overview of commands and flags
|
|
gasm tokens kernel_amd64.s # dump the token stream
|
|
gasm parse kernel_amd64.s # parse, report syntax errors
|
|
gasm fmt -w kernel_amd64.s # canonicalise in place
|
|
gasm fmt # reformat every .s below here, like go fmt
|
|
gasm lint *.s # static checks
|
|
gasm asm --format elf -o k.o k.s # assemble to a linkable ELF object
|
|
gasm verify kernel_amd64.s # JIT-load and report functions
|
|
gasm verify --ground-truth k.s # byte-for-byte vs go tool asm
|
|
gasm verify --call decodeBlockAVX2 --buf src:64:hex...,dst:256:zero k.s
|
|
gasm debug --func name k.s # interactive debugger
|
|
gasm diff a.s b.s # compare machine code byte-for-byte
|
|
gasm diff --map wideCopyAVX2=wideCopyAVX512 avx2.s avx512.s
|
|
gasm profile k.s # show basic-block structure
|
|
```
|
|
|
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development workflow,
|
|
[docs/CLI.md](docs/CLI.md) for the command reference, and
|
|
[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for setup and recipes.
|
|
|
|
## Editor integration
|
|
|
|
`gasm lsp` speaks the Language Server Protocol over standard input/output, so
|
|
any LSP-capable editor can use it — point your editor's LSP client at the
|
|
binary and associate it with `.s` files. Syntax highlighting is delivered as
|
|
**LSP semantic tokens**, so no editor-specific grammar is required. The server
|
|
infers the target architecture from the file-name suffix
|
|
(`_amd64.s` / `_arm64.s` / `_riscv64.s` / `_loong64.s`).
|
|
|
|
## License
|
|
|
|
BSD-3-Clause — see [LICENSE](LICENSE).
|
|
Copyright © 2026 [Petr Balvín](https://petrbalvin.org)
|