diff --git a/CHANGELOG.md b/CHANGELOG.md index d33a354..06ee596 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,62 @@ and this project adheres to [Conventional Commits](https://www.conventionalcommi - +## [0.33.0] — 2026-09-14 + +### Added + +- **Stack-split guards in `gasm asm`.** Every framed function now gets + the morestack prologue check and the trailing morestack block + (`CALL runtime.morestack_noctxt`), byte-identical to the toolchain's + `stacksplit` output on all four architectures: the small, medium and + big frame classes, auto-NOSPLIT leaves, the materialised constants of + large frames (arm64 `R27`, riscv64 `X31`, loong64 `R30`) and the + arm64 extrasize rule. Assembled objects are therefore linkable for + split functions, not only `NOSPLIT` leaves. +- **`gasm dis`.** Standalone disassembly through `golang.org/x/arch`: + a `.s` file is assembled and listed per `TEXT` function with local + labels at their real offsets, or raw bytes from a file or stdin are + disassembled linearly (`-a` selects the architecture). The debugger + shares the same decoder instead of carrying its own. +- **`gasm fmt -l` and `-d`.** Check mode lists files whose formatting + differs; diff mode prints a unified diff from the project's own + LCS-based differ, with GNU header semantics. +- **LSP cross-file navigation.** Go-to-definition and find references + fall back from local labels to the `TEXT` functions of every open + document, and rename follows the same cross-file matching. +- **Large frame offsets on riscv64 and arm64.** Frame-relative loads + and stores beyond the signed 12-bit immediate range materialise the + address through the toolchain temp register (riscv64 `X31`, arm64 + `R27`) instead of silently truncating the offset (riscv64) or + rejecting the instruction (arm64); arm64 frame sizes now add the + toolchain's extrasize exactly (+8 when the frame leaves an alignment + gap, +16 when it is already aligned). +- **Tail calls `JMP sym(SB)`** on all four architectures (amd64 `E9`, + arm64 `B`, riscv64 `JAL X0`, loong64 `B`) with the call relocation. +- **Live oracle-parity tests.** Kernel files covering every guard + class, large-offset pattern and tail call are assembled by gasm and + by the installed `go tool asm` and compared byte-for-byte on all four + architectures, alongside the existing pinned-byte tests. + +### Fixed + +- The v0.32.0 review findings: the parser rejects malformed `TEXT` + frames and parses signed frame sizes; amd64 frame adjustments above + 127 bytes encode with imm32; arm64 and loong64 relocation encodings + match the toolchain; the linter guards unnamed `TEXT` directives and + refreshes its textflag table; the LSP recovers from handler panics + and decodes client URIs; watchpoint slot state moved into the debug + session; dead verify code removed; em and en dashes replaced across + sources. +- `gasm asm --format goobj`: internal calls to `TEXT` symbols of the + same file resolve on every architecture (the reference check accepted + only the amd64 call kind). +- `gasm asm --format elf` on loong64: branch relocations now map to + `R_LARCH_B26` instead of falling into `R_LARCH_PCALA_HI20`. +- arm64 large-prologue `ADD`/`SUB` use the extended-register encoding + the toolchain picks, and the morestack block saves the link register + with the toolchain's `OR` form on loong64. + ## [0.32.0] — 2026-08-31 ### Added diff --git a/README.md b/README.md index 5df3c8c..85f198a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ developer tooling to Plan 9 assembly on amd64, arm64, riscv64 and loong64. directly. - **Formatter.** `gasm fmt` canonicalises indentation, operand spacing, per-function mnemonic alignment and blank-line layout: `gofmt` for assembly, - operating recursively on directories the way `go fmt` does. + operating recursively on directories the way `go fmt` does. `-l` lists + files whose formatting differs and `-d` prints a unified diff. - **Linter.** `gasm lint` runs 18 conservative static checks, among them `undefined-label`, `abi-argsize` (declared frame vs the `// func` signature), `register-clobber` (Go ABI register liveness over the control-flow graph), @@ -24,7 +25,11 @@ developer tooling to Plan 9 assembly on amd64, arm64, riscv64 and loong64. - **Standalone assembler.** `gasm asm` encodes all four architectures without the Go toolchain and writes raw images, linkable ELF objects (with DWARF5 debug sections) or the Go toolchain's own GOOBJ format, which `go build` - consumes in place of the toolchain's output. + consumes in place of the toolchain's output. Framed functions get the + stack-split guard and the morestack block, byte-identical to the + toolchain's, so split functions link too. +- **Disassembler.** `gasm dis` lists a `.s` file's functions at their real + offsets after assembling, or disassembles raw bytes from a file or stdin. - **Dynamic verification.** `gasm verify` JIT-loads assembled functions into executable memory: smoke calls, ABI checks (sentinel registers, red-zone canary), differential fuzzing against the `go tool asm` build, and @@ -36,7 +41,8 @@ developer tooling to Plan 9 assembly on amd64, arm64, riscv64 and loong64. push and pull diagnostics, semantic-token highlighting, go-to-definition, find references, rename, formatting, inlay hints, code actions, signature help, document highlights, workspace symbol search, #include document - links and folding ranges over stdio. + links and folding ranges over stdio; definition, references and rename + work across every open document. - **Comparators and audits.** `gasm diff` compares the machine code of two assembly files byte-for-byte, `gasm profile` shows basic-block structure, `gasm audit-instructions` diffs the encoder against the installed toolchain, @@ -102,9 +108,13 @@ gasm verify --call add --args a=2,b=3 hello_amd64.s # JIT-call it with argumen ```sh gasm fmt # reformat every .s below here, like go fmt gasm fmt -w kernel_amd64.s # canonicalise one file in place +gasm fmt -l *.s # list files whose formatting differs +gasm fmt -d kernel_amd64.s # print a unified diff instead gasm lint *.s # static checks gasm asm --format elf -o k.o k.s # assemble to a linkable ELF object gasm asm --format goobj -p pkg/path -o k.o k.s # Go object, consumed by go build +gasm dis k.s # assemble, then list each function +gasm dis -a amd64 - < dump.bin # disassemble raw bytes from stdin gasm verify --ground-truth k.s # byte-for-byte vs go tool asm gasm verify --fuzz k.s # differential fuzz vs the go tool asm build gasm debug --func name k.s # interactive debugger diff --git a/cmd/gasm/main.go b/cmd/gasm/main.go index 1a8d710..d1bfe49 100644 --- a/cmd/gasm/main.go +++ b/cmd/gasm/main.go @@ -38,7 +38,7 @@ import ( // version is the release version, stamped at build time via // -ldflags "-X main.version=…" (defaulting to the current release). -var version = "0.32.0" +var version = "0.33.0" func main() { if len(os.Args) < 2 { diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index f16daf9..5da1378 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -237,6 +237,18 @@ STP+SUB for large frames) and SB/global symbol references (ADRP+ADD pairs with R_ADDRARM64 relocations). Like the other encoders it is validated byte-for-byte against `GOARCH=arm64 go tool asm`. +On top of the per-architecture encoders, every framed function carries the +**stack-split guard**: the prologue check against `g.stackguard0` (small, +medium and large frame classes, the medium and large classes materialising +their offset through the architecture's temporary register and the large +class adding the SP-underflow branch) and the trailing morestack block +(save the link register, `CALL runtime.morestack_noctxt`, jump back to the +function entry). The auto-NOSPLIT rule, the frame classes, the large-frame +prologue and epilogue forms and the tail calls match the toolchain's +`stacksplit` and `preprocess` output byte for byte; a parity suite +assembles kernel files with gasm and the installed `go tool asm` and diffs +the bytes on all four architectures. + On top of the encoder, `Assemble` walks a parsed `TEXT` body, converts each operand to an encoder operand, and lays the instructions out so local labels resolve to relative jump offsets: jumps start in the short (rel8) form and diff --git a/docs/CLI.md b/docs/CLI.md index 1e8aa7f..b72aa16 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -22,7 +22,7 @@ one token per line. FILE may be `-` to read standard input. Parse FILE and report syntax errors on stderr. On success, prints how many declarations and TEXT functions the file contains. -## `gasm fmt [-w] [path...]` +## `gasm fmt [-w|-l|-d] [path...]` Canonicalise the formatting of Plan 9 assembly sources: indentation, operand spacing, per-function mnemonic alignment, and blank-line layout. @@ -30,6 +30,8 @@ operand spacing, per-function mnemonic alignment, and blank-line layout. | Flag | Description | |------|-------------| | `-w` | Write result to the source file (default: print to stdout) | +| `-l` | List files whose formatting differs, one per line; write nothing | +| `-d` | Print a unified diff of the canonical formatting instead | With no arguments, or with a directory argument, every `.s` file below it is reformatted in place and the names of changed files are listed @@ -62,6 +64,21 @@ Assemble FILE to machine code (amd64, arm64, riscv64, loong64). | `-p` | Package path (required for `--format goobj`) | | `-o` | Write output to file (default: hex dump to stdout) | +## `gasm dis [-a arch] ` + +Disassemble machine code to instruction text (via `golang.org/x/arch`). + +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. The architecture comes from the file name suffix, or from `-a`. +With any other file, or `-` for standard input, the bytes are +disassembled linearly and `-a` selects the architecture (amd64, arm64, +riscv64 or loong64). + +| Flag | Description | +|------|-------------| +| `-a` | Architecture for raw input without a `_arch.s` name | + ## `gasm verify [flags] ` Assemble FILE, map it into executable memory, and run dynamic checks. diff --git a/justfile b/justfile index d8fe937..bf282b7 100644 --- a/justfile +++ b/justfile @@ -3,7 +3,7 @@ # gasm-devkit — developer tooling for Go's Plan 9 assembler (GAsm). -version := "0.32.0" +version := "0.33.0" default: @just --list