diff --git a/cmd/gasm/debug_linux.go b/cmd/gasm/debug_linux.go index a7062a1..937b1de 100644 --- a/cmd/gasm/debug_linux.go +++ b/cmd/gasm/debug_linux.go @@ -17,7 +17,7 @@ import ( func cmdDebug(args []string) int { fs := newCommand("debug", "gasm debug --func ", ` -Interactive debugger for JIT-assembled amd64 functions. Launches the +Interactive debugger for JIT-assembled functions. Launches the function in a traced subprocess (ptrace), then provides a REPL for single-stepping, breakpoints, register and memory inspection. diff --git a/cmd/gasm/main.go b/cmd/gasm/main.go index 7377817..927d40e 100644 --- a/cmd/gasm/main.go +++ b/cmd/gasm/main.go @@ -117,9 +117,9 @@ func usage(w io.Writer) { {"parse", "parse and report syntax errors"}, {"fmt", "canonicalise formatting (gofmt for assembly)"}, {"lint", "run static checks"}, - {"asm", "assemble .s files to machine code (amd64, riscv64)"}, - {"verify", "JIT-assemble and run dynamic checks (amd64, riscv64)"}, - {"debug", "interactive source-level debugger (amd64)"}, + {"asm", "assemble .s files to machine code (amd64, arm64, riscv64, loong64)"}, + {"verify", "JIT-assemble and run dynamic checks (amd64, arm64, riscv64, loong64)"}, + {"debug", "interactive source-level debugger (amd64, arm64, riscv64, loong64)"}, {"diff", "compare machine code of two .s files"}, {"profile", "show basic-block structure of functions"}, {"lsp", "run the language server over stdio"}, @@ -1073,7 +1073,7 @@ decoders) that crash on random input but should succeed on valid data. // AArch64: ground-truth only (no JIT on non-ARM64 hosts). return cmdVerifyARM64(path, *groundTruth, *profile) default: - fmt.Fprintln(os.Stderr, "gasm verify: only amd64, riscv64 and loong64 are supported") + fmt.Fprintln(os.Stderr, "gasm verify: unsupported architecture") return 1 } diff --git a/debug/breakpoint.go b/debug/breakpoint.go index 12beadb..ab6b3fd 100644 --- a/debug/breakpoint.go +++ b/debug/breakpoint.go @@ -1,6 +1,8 @@ // Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause +//go:build linux + package debug import "fmt" diff --git a/debug/repl.go b/debug/repl.go index 6872654..375ddf1 100644 --- a/debug/repl.go +++ b/debug/repl.go @@ -94,7 +94,7 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar text, instLen, _ := s.Disassemble(pc) if strings.HasPrefix(strings.ToLower(text), "call") || strings.HasPrefix(strings.ToLower(text), "bl") { afterAddr := pc + uint64(instLen) - bp, err := bm.Set(afterAddr, "(next)") + _, err := bm.Set(afterAddr, "(next)") if err != nil { fmt.Printf("cannot set next breakpoint: %v\n", err) continue @@ -109,7 +109,6 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar } bm.HandleTrap(®s) bm.Clear(afterAddr) - _ = bp } else { if err := s.Step(); err != nil { fmt.Println(err) @@ -130,7 +129,7 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar fmt.Printf("cannot read return address: %v\n", err) continue } - bp, err := bm.Set(retAddr, "(finish)") + _, err = bm.Set(retAddr, "(finish)") if err != nil { fmt.Printf("cannot set finish breakpoint: %v\n", err) continue @@ -147,7 +146,6 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar bm.HandleTrap(®s) } bm.Clear(retAddr) - _ = bp if s.Exited() { fmt.Println("debuggee exited") } else { diff --git a/debug/tracer.go b/debug/tracer.go index 0ffaf54..796106e 100644 --- a/debug/tracer.go +++ b/debug/tracer.go @@ -1,6 +1,8 @@ // Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause +//go:build linux + package debug // tracer abstracts the minimal ptrace operations needed by the breakpoint diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 814dbf3..17c6c6e 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -97,10 +97,11 @@ fixed-arity instructions (`RET`, `NOP`, `JMP`, `CALL`) carry counts at all. Rules are conservative by design — silence beats a false positive. The rules are `unknown-instruction`, `operand-count`, `undefined-label`, -`duplicate-label`, `missing-ret`, `missing-textflag-include`, `abi-argsize` and -`unreachable-code`. Every diagnostic carries a stable code so callers can -disable rules individually, and arch-specific rules switch off entirely when -the target architecture cannot be inferred from the file name. +`duplicate-label`, `missing-ret`, `missing-textflag-include`, `abi-argsize`, +`unreachable-code`, `register-clobber`, `funcdata-pcdata`, `unused-label`, +`invalid-textflag` and `stack-imbalance`. Every diagnostic carries a stable +code so callers can disable rules individually, and arch-specific rules switch +off entirely when the target architecture cannot be inferred from the file name. Two things keep the rules honest on real-world code: @@ -365,7 +366,7 @@ masked), reporting any encoding drift. ### `debug` -The interactive debugger (Phase 4, linux/amd64). It launches the target +The interactive debugger (all four architectures). It launches the target function in a child process that maps the JIT code, calls `PTRACE_TRACEME`, and stops; the parent attaches via ptrace and controls execution. Breakpoints are patched as INT3 bytes through `/proc/pid/mem` diff --git a/docs/CLI.md b/docs/CLI.md index 9da764b..e672f5e 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -52,7 +52,7 @@ Rules: `unknown-instruction`, `operand-count`, `undefined-label`, ## `gasm asm [--format raw|elf|goobj] [-p pkg] [-o out] ` -Assemble FILE (amd64) to machine code. +Assemble FILE to machine code (amd64, arm64, riscv64, loong64). | Flag | Description | |------|-------------| @@ -90,7 +90,7 @@ return values and any output written to the buffers. ## `gasm debug --func [--buf spec] ` -Interactive debugger for JIT-assembled amd64 functions. Requires a +Interactive debugger for JIT-assembled functions (amd64, arm64, riscv64, loong64). Requires a compiled binary on `$PATH` (not `go run`). | Flag | Description | diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index cdf66ae..807106e 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -101,7 +101,7 @@ format/ Canonical formatter lsp/ Language Server Protocol server asm/ Standalone assembler, encoder, object emitters verify/ JIT execution, differential testing, ABI checks -debug/ Interactive ptrace debugger (linux/amd64) +debug/ Interactive ptrace debugger (all four architectures) _gen/ Instruction table generator testdata/ Test fixtures docs/ Architecture, development, CLI reference diff --git a/lint/lint.go b/lint/lint.go index e42605e..eccc867 100644 --- a/lint/lint.go +++ b/lint/lint.go @@ -71,7 +71,6 @@ const ( CodeMissingTextflag = "missing-textflag-include" CodeUnreachable = "unreachable-code" CodeABIArgSize = "abi-argsize" - CodeNosplitFrame = "nosplit-frame" CodeRegisterClobber = "register-clobber" CodeFuncdata = "funcdata-pcdata" CodeUnusedLabel = "unused-label" diff --git a/lsp/server.go b/lsp/server.go index 56e34e8..09fa9c5 100644 --- a/lsp/server.go +++ b/lsp/server.go @@ -133,7 +133,7 @@ func (s *Server) dispatch(msg *rpcMessage) (exit bool) { }, InlayHintProvider: true, }, - ServerInfo: map[string]string{"name": "gasm", "version": "0.1.0"}, + ServerInfo: map[string]string{"name": "gasm", "version": "0.31.1"}, }) case "initialized", "textDocument/didSave":