From 6d7f10f13e6e244f73cd56182526fdea946aa349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Sun, 30 Aug 2026 11:00:40 +0200 Subject: [PATCH] refactor(cmd): re-enter child modes via environment instead of hidden flags Assisted-by: GLM 5.3 Flash --- cmd/gasm/debug_linux.go | 33 ++++++++++++++++++++++++--------- cmd/gasm/main.go | 35 ++++++++++++++++++++--------------- debug/ptrace_linux.go | 4 ++-- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/cmd/gasm/debug_linux.go b/cmd/gasm/debug_linux.go index fbc2251..94e8e15 100644 --- a/cmd/gasm/debug_linux.go +++ b/cmd/gasm/debug_linux.go @@ -24,15 +24,30 @@ function in a traced subprocess (ptrace), then provides a REPL for single-stepping, breakpoints, register and memory inspection. REPL commands: - break set a breakpoint at a label or absolute address - step [n] single-step n instructions (default 1) - continue run until next breakpoint or exit - regs print general-purpose registers + break [if ] + set a breakpoint, optionally conditional on a + register comparison (reg-reg or reg-immediate) + delete remove a breakpoint + info break list all breakpoints + step [n], s single-step n instructions (default 1) + 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 PC + regs print general-purpose and vector registers + where show source line and nearest label at PC + stack show stack near RSP (return address + ABI0 args) + bt, backtrace backtrace (current frame + return address) x [addr] [len] hex-dump memory (default: current PC, 64 bytes) - labels list function labels and offsets - quit kill the debuggee and exit + w write bytes to memory + set set a register + watch [r|w] [size] + set a hardware watchpoint (write by default) + unwatch [] clear one watchpoint, or all without an argument + labels, l list function labels and offsets + help, h, ? show command help + quit, q kill the debuggee and exit `) - target := fs.Bool("target", false, "") // hidden: debuggee subprocess mode funcName := fs.String("func", "", "function to debug") argsFile := fs.String("args", "", "file containing the ABI0 argument block") bufSpec := fs.String("buf", "", "buffer specification: name:size:pattern[,name:size:pattern...] where pattern is zero, ones, seq, or hex") @@ -42,10 +57,10 @@ REPL commands: fs.Parse(args) // --- Debuggee mode (internal, spawned by the debugger) --- - if *target { + if os.Getenv("GASM_DEBUG_TARGET") != "" { tmpDir := os.Getenv("GASM_DEBUG_TMP") if tmpDir == "" || fs.NArg() < 1 || *funcName == "" || *argsFile == "" { - fmt.Fprintln(os.Stderr, "gasm debug --target: internal mode") + fmt.Fprintln(os.Stderr, "gasm debug: internal debuggee mode") return 2 } if err := debug.RunTarget(fs.Arg(0), *funcName, *argsFile, tmpDir); err != nil { diff --git a/cmd/gasm/main.go b/cmd/gasm/main.go index aa58341..a7d817d 100644 --- a/cmd/gasm/main.go +++ b/cmd/gasm/main.go @@ -1054,8 +1054,6 @@ decoders) that crash on random input but should succeed on valid data. groundTruth := set.Bool("ground-truth", false, "compare machine code byte-for-byte against go tool asm") fuzz := set.Bool("fuzz", false, "differential fuzz: JIT both gasm and go-tool-asm versions, compare outputs") fuzzN := set.Int("n", 1000, "number of fuzz iterations per function") - fuzzOne := set.String("fuzz-one", "", "") // hidden: fuzz a single function (subprocess mode) - sweepOne := set.String("sweep-one", "", "") // hidden: smoke/abi a single function (subprocess mode) call := set.String("call", "", "call a single function with -buf instead of the sweeps") bufSpec := set.String("buf", "", "buffer spec for -call: name:size:pattern[,name:size:pattern] (zero, ones, seq, or hex)") scalarSpec := set.String("args", "", "scalar args for -call: name=value[,name=value] (decimal or 0x hex)") @@ -1100,8 +1098,11 @@ decoders) that crash on random input but should succeed on valid data. return cmdVerifyCall(k, path, *call, *bufSpec, *scalarSpec, *repeat) } - // Subprocess mode: fuzz a single function and exit. - if *fuzzOne != "" { + // Subprocess mode: fuzz a single function and exit. The parent selects + // the function through the environment, so no internal flag leaks into + // the -h output. + fuzzOne := os.Getenv("GASM_VERIFY_FUZZ_ONE") + if fuzzOne != "" { gt, err := verify.GroundTruth(path) if err != nil { fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err) @@ -1113,17 +1114,17 @@ decoders) that crash on random input but should succeed on valid data. return 1 } sigs := verify.ExtractSignatures(src) - sig, ok := sigs[*fuzzOne] + sig, ok := sigs[fuzzOne] if !ok { - fmt.Printf("%s: no signature\n", *fuzzOne) + fmt.Printf("%s: no signature\n", fuzzOne) return 0 } - goCode, ok := gt[*fuzzOne] + goCode, ok := gt[fuzzOne] if !ok { - fmt.Printf("%s: not in go tool asm\n", *fuzzOne) + fmt.Printf("%s: not in go tool asm\n", fuzzOne) return 0 } - res := k.FuzzFunc(*fuzzOne, sig, goCode, *fuzzN, 42) + res := k.FuzzFunc(fuzzOne, sig, goCode, *fuzzN, 42) fmt.Printf("%s\n", res) if !res.OK() { return 1 @@ -1134,13 +1135,15 @@ decoders) that crash on random input but should succeed on valid data. // Subprocess mode: run the smoke/abi checks for a single function and // exit with the accumulated status. The parent interprets a clean exit // as success, a non-zero exit as failure and death-by-signal as a crash. - if *sweepOne != "" { - fl, err := k.Func(*sweepOne) + // Like the fuzz mode, the parent selects the function through the + // environment instead of an internal flag. + if sweepOne := os.Getenv("GASM_VERIFY_SWEEP_ONE"); sweepOne != "" { + fl, err := k.Func(sweepOne) if err != nil || !fl.NoSplit { - fmt.Fprintf(os.Stderr, "gasm verify: %s: %v\n", *sweepOne, err) + fmt.Fprintf(os.Stderr, "gasm verify: %s: %v\n", sweepOne, err) return 1 } - msgs, failed := runSweepChecks(k, path, *sweepOne, fl, *smoke, *abi, *abiN) + msgs, failed := runSweepChecks(k, path, sweepOne, fl, *smoke, *abi, *abiN) for _, m := range msgs { fmt.Println(m) } @@ -1324,7 +1327,8 @@ func fuzzInSubprocess(path, funcName string, n int) string { if err != nil { return fmt.Sprintf("%s: cannot find self: %v", funcName, err) } - cmd := exec.Command(self, "verify", "--fuzz-one="+funcName, "-n", strconv.Itoa(n), path) + cmd := exec.Command(self, "verify", "-n", strconv.Itoa(n), path) + cmd.Env = append(os.Environ(), "GASM_VERIFY_FUZZ_ONE="+funcName) out, err := cmd.CombinedOutput() if err != nil { // Check if the child was killed by a signal. @@ -1363,7 +1367,7 @@ func sweepInSubprocess(path, funcName string, smoke, abi bool, abiN int) (string if err != nil { return fmt.Sprintf(" smoke/abi: FAIL — cannot find self: %v", err), true } - args := []string{"verify", "--sweep-one=" + funcName} + args := []string{"verify"} if smoke { args = append(args, "-smoke") } @@ -1372,6 +1376,7 @@ func sweepInSubprocess(path, funcName string, smoke, abi bool, abiN int) (string } args = append(args, path) cmd := exec.Command(self, args...) + cmd.Env = append(os.Environ(), "GASM_VERIFY_SWEEP_ONE="+funcName) out, err := cmd.CombinedOutput() if err != nil { if exitErr, ok := err.(*exec.ExitError); ok { diff --git a/debug/ptrace_linux.go b/debug/ptrace_linux.go index e9cb95d..a643deb 100644 --- a/debug/ptrace_linux.go +++ b/debug/ptrace_linux.go @@ -58,8 +58,8 @@ func LaunchWithBuffers(gasmBin, asmPath, funcName string, args []byte, bufSpec s } } - cmd := exec.Command(self, "debug", "--target", "--func", funcName, "--args", argsFile, asmPath) - cmd.Env = append(os.Environ(), "GASM_DEBUG_TMP="+tmpDir) + cmd := exec.Command(self, "debug", "--func", funcName, "--args", argsFile, asmPath) + cmd.Env = append(os.Environ(), "GASM_DEBUG_TARGET=1", "GASM_DEBUG_TMP="+tmpDir) cmd.Stdout = nil cmd.Stderr = os.Stderr cmd.SysProcAttr = &syscall.SysProcAttr{}