diff --git a/CHANGELOG.md b/CHANGELOG.md index 697f269..d175f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - **The FreeBSD port of the debugger.** `gasm debug` runs on FreeBSD on - amd64, arm64 and riscv64: the same interactive surface as on Linux — + amd64, arm64 and riscv64 with the same interactive surface as on Linux: breakpoints, hardware watchpoints (x86 debug registers, the arm64 debug - register file), single-stepping, register and memory access — behind the - kernel's own ptrace requests, with tracee memory through `PT_IO` and + register file), single-stepping, register and memory access, all behind + the kernel's own ptrace requests, with tracee memory through `PT_IO` and stop reports through `PT_LWPINFO`. The JIT substrate maps executable memory through `golang.org/x/sys/unix`, so `verify` builds on FreeBSD too. The pipeline compile-gates all three architectures; live @@ -29,6 +29,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 last one in the file, and the `abi-argsize` warning offers to set the TEXT argument area to the size the `// func` signature implies, computed by the new `lint.ExpectedArgSize`. +- **Eleven new lint rules over the directives, the data section and the + sharpest addressing edges.** `missing-argsize` flags a TEXT that + declares no argument area its `// func` signature implies; + `noframe-frame-size` a NOFRAME with a positive frame; + `unnamed-fp-reference` a nameless `0(FP)`, which both assemblers + reject; `hardware-sp-addressing` a negative offset off the hardware SP + rather than the virtual frame; `vex-sse-mixing` a kernel that mixes VEX + and legacy SSE spellings and pays the transition penalty; + `unnamed-result` a `ret+N(FP)` the signature names; `data-width`, + `data-value-overflow`, `data-string-width`, `data-without-globl` and + `data-exceeds-globl` police the DATA width against its value type and + the GLOBL size behind it. `missing-ret` now also flags a function + whose tail can fall off its end even though a RET sits somewhere in the + body, and `invalid-textflag` also reports a flag misplaced between TEXT + and GLOBL. +- **Hover documentation and wider completions in the language server.** + Hovering a directive or pseudo-operation (TEXT, DATA, GLOBL, PCALIGN, + FUNCDATA, PCDATA, the BYTE family) shows its grammar and rules in + preference to the empty instruction-table entry, and completion offers + those names beside the instruction set. The `missing-argsize` warning + carries a quick fix that declares the argument area the signature + implies (`$0` becomes `$0-16`). +- **The GOOBJ link parity gate.** A regression test assembles a kernel + per architecture through `gasm asm --format goobj`, substitutes the + object into a real `go build`'s package archive, proves the archive + carries it byte for byte and re-links with cmd/link on amd64, arm64, + riscv64 and loong64. The binaries run, natively on amd64 and under + qemu-user on arm64 and riscv64, and their output must match the + toolchain-built baseline; loong64 is link-only. The pipeline installs + qemu-user and runs the gate on every push. +- **The amd64 and loong64 encoders close four more corpus files.** The + whole-tree measure moves to 272 of 322 (84.5 %): amd64 gains the + one-operand IMUL, the SSE compare family (CMPPD, CMPPS, CMPSS), RETFL, + the LOOP family, the MMX register bank with its bank-crossing moves, + MOVNTDQ, the CR and DR register moves, PUSH and POP of FS and GS, the + `(TLS)` pseudo-base, the wait and cache controls (CLWB, CLDEMOTE, + TPAUSE, UMONITOR, UMWAIT, RDPID, ENDBR64), indirect branches with the + star spelling (`JMP *(R12)(R13*4)`), `RET sym(SB)` as the tail jump, + the colon shift spelling (`SHLL CX, R11:AX`) and the EVEX and VEX forms + of the rounds, AES key assist, string compares, extracts, blends and + permutes; loong64 gains the acquire and release pair (LLACQ, SCREL, + with the vector widths), the VMOVQ and XVMOVQ lane forms and the BYTE + literal-data escape hatch the other architectures already take. ### Changed @@ -39,8 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 instead of failing it. The headline now reads "assemble for every applicable target": every real-code GOROOT assembly file, the tree without testdata, assembles for all four architectures (250 of 250, - 100 %); over the whole tree including testdata the measure is 271 of - 322 (84.2 %). + 100 %); over the whole tree including testdata the measure is 272 of + 322 (84.5 %). - **The module moves to `sourcedock.dev/petrbalvin/gasm-sdk`.** The repository and the module rename together with the product, now the GAsm Software Development Kit. Fresh installs become @@ -66,6 +109,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 rejected; the toolchain picks the 16-bit lane from the 64-bit bit pattern, so the encoder now does the same, and a zero immediate is rejected where the toolchain rejects it. +- **NOPTR data emits its own symbol kind.** A GLOBL with NOPTR was + emitted as plain SDATA, the kind the linker holds to its Go type + information requirement, so every gasm object carrying runtime-shaped + data (`GLOBL ·x(SB), NOPTR, ...`) died in cmd/link with "missing Go + type information". NOPTR data is now SNOPTRDATA, the toolchain's kind + for pointer-free globals, and RODATA still wins where both flags + appear, exactly as the toolchain chooses. The three end-to-end link + tests that should have caught this substituted the gasm object after + the package archive was already packed, so they passed vacuously; they + now substitute inside the archive, prove the substitution byte for + byte and re-link. +- **Latent encoder divergences against the toolchain, found by the + whole-file differential harness.** On loong64, `MOVx $off(reg)` lost + its base register, SCQ swapped its operand fields, the vector lane + inserts did not scale offsets by the element width and two immediate + opcodes were mistyped; on amd64, NOP with operands encoded 0x90 where + the toolchain emits nothing at all, the VEX gather length bit ignored + the VSIB index width, four permute and extract families were pushed + into EVEX where the toolchain stays VEX, and a reference to a static + symbol no GLOBL defines failed where the toolchain defers it to the + linker as an external relocation. +- **Seven front-end defects found by fuzzing.** The formatter swallowed + the statement after a leading block comment (`/* head */ MOVQ AX, BX` + formatted to the comment alone), dropped trailing tokens after an + `#include` header, and trimmed the trailing whitespace inside a string + literal; the parser peeled only one label of a stacked pair (`a: b:` + parsed differently than it formatted); deeply nested parentheses in an + immediate overflowed the stack through the constant folder, which now + stops at a bounded depth and falls back to the ordinary operand paths; + macro expansion is linear in the invocation count instead of + quadratic; and amplifying macros (the billion-laughs shape) stop at a + work budget sized by the line and the macro table, reported as a + diagnostic instead of running for hours. +- **The debugger handles the edges its tests now reach.** Memory reads + and writes cover exactly the requested bytes, so a request ending in + the last page of a mapping no longer fails on the unmapped page behind + it; disassembly shrinks its instruction window at a mapping's end + instead of failing; a debuggee that dies before signalling readiness + writes a failure notice the launcher reads, so launch fails fast with + the reason instead of after the whole poll (and the poll no longer + waits on the child, which could consume the SIGSTOP park and hang the + launch); amd64 watchpoints acknowledge the sticky DR6 hit bits and + clear the address register on release; the breakpoint listing is + ordered by address so its numbering is stable; the REPL rejects bad + counts, sizes and watchpoint types instead of silently guessing, + reports stops on signals during stepping, and a breakpoint-class trap + that matches no breakpoint and leaves the PC in place surfaces instead + of spinning the continue loop and the coverage run forever. ## [0.35.0] - 2026-09-22 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 76293d8..1e9ae06 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -580,7 +580,7 @@ AST, so neither depends on an encoding. a GOOBJ object references, and `_gen` parses `$GOROOT/src/cmd/internal/obj//anames.go` to rebuild the tables. - **Linux and FreeBSD process interfaces** for the dynamic work: `mmap` and - `mprotect` for the JIT mapping, ptrace for the debugger — with tracee + `mprotect` for the JIT mapping, ptrace for the debugger, with tracee memory through `/proc/pid/mem` on Linux and through `PT_IO` on FreeBSD, and the tracee's stop reports read from `PT_LWPINFO` there. That is why `verify` runs a JIT check only when the host architecture matches the