diff --git a/cmd/gasm/debug_linux_amd64.go b/cmd/gasm/debug_linux_amd64.go deleted file mode 100644 index a2151c6..0000000 --- a/cmd/gasm/debug_linux_amd64.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) -// SPDX-License-Identifier: BSD-3-Clause - -//go:build linux && amd64 - -package main - -import ( - "fmt" - "os" - "sort" - "strings" - - "sourcedock.dev/petrbalvin/gasm-devkit/debug" - "sourcedock.dev/petrbalvin/gasm-devkit/verify" -) - -func cmdDebug(args []string) int { - fs := newCommand("debug", "gasm debug --func ", ` -Interactive debugger for JIT-assembled amd64 functions. Launches the -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 - x [addr] [len] hex-dump memory (default: current PC, 64 bytes) - labels list function labels and offsets - quit 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") - fs.Parse(args) - - // --- Debuggee mode (internal, spawned by the debugger) --- - if *target { - tmpDir := os.Getenv("GASM_DEBUG_TMP") - if tmpDir == "" || fs.NArg() < 1 || *funcName == "" || *argsFile == "" { - fmt.Fprintln(os.Stderr, "gasm debug --target: internal mode") - return 2 - } - if err := debug.RunTarget(fs.Arg(0), *funcName, *argsFile, tmpDir); err != nil { - fmt.Fprintf(os.Stderr, "gasm debug: %v\n", err) - return 1 - } - return 0 - } - - // --- Debugger mode (interactive REPL) --- - if fs.NArg() < 1 || *funcName == "" { - fmt.Fprintln(os.Stderr, "usage: gasm debug --func ") - return 2 - } - path := fs.Arg(0) - - // Load the kernel to extract function metadata and labels. - k, err := verify.Load(path) - if err != nil { - fmt.Fprintf(os.Stderr, "gasm debug: %v\n", err) - return 1 - } - defer k.Close() - - fl, err := k.Func(*funcName) - if err != nil { - fmt.Fprintf(os.Stderr, "gasm debug: %v\n", err) - return 1 - } - - // Build the label list for the REPL. - var labels []debug.Label - for name, off := range fl.Labels { - labels = append(labels, debug.Label{Name: name, Offset: off}) - } - sort.Slice(labels, func(i, j int) bool { return labels[i].Offset < labels[j].Offset }) - - // Launch the debuggee with the argument block. - var argBlock []byte - var bufAddrs []uint64 - var sess *debug.Session - if *bufSpec != "" { - // Parse the function signature to determine argument layout. - src, err := readSource(path) - if err != nil { - fmt.Fprintf(os.Stderr, "gasm debug: %v\n", err) - return 1 - } - sig, ok := verify.ExtractFuncSig(src, *funcName) - if !ok { - fmt.Fprintf(os.Stderr, "gasm debug: no // func signature found for %s\n", *funcName) - return 1 - } - layout := verify.ArgLayout(sig) - - // Parse the buffer spec to get buffer names. - bufNames := parseBufNames(*bufSpec) - - // Allocate buffers in the debuggee. - argBlock = make([]byte, fl.Args) - sess, bufAddrs, err = debug.LaunchWithBuffers("", path, *funcName, argBlock, *bufSpec) - if err != nil { - fmt.Fprintf(os.Stderr, "gasm debug: %v\n", err) - return 1 - } - - // Construct the argument block with buffer pointers at the correct positions. - bufIdx := 0 - for _, arg := range layout { - if !arg.IsPtr { - continue - } - // Find the buffer that matches this argument. - for i, name := range bufNames { - if i < len(bufAddrs) && (name == arg.Name || strings.HasPrefix(arg.Name, name)) { - addr := bufAddrs[i] - off := arg.Offset - if off+8 <= len(argBlock) { - argBlock[off] = byte(addr) - argBlock[off+1] = byte(addr >> 8) - argBlock[off+2] = byte(addr >> 16) - argBlock[off+3] = byte(addr >> 24) - argBlock[off+4] = byte(addr >> 32) - argBlock[off+5] = byte(addr >> 40) - argBlock[off+6] = byte(addr >> 48) - argBlock[off+7] = byte(addr >> 56) - } - // For slices, also set the length and capacity. - if strings.HasPrefix(arg.Typ, "[]") && off+24 <= len(argBlock) { - // Find the buffer size from the spec. - size := parseBufSize(*bufSpec, name) - // Length at offset+8, capacity at offset+16. - for j := 0; j < 8; j++ { - argBlock[off+8+j] = byte(size >> (j * 8)) - argBlock[off+16+j] = byte(size >> (j * 8)) - } - } - bufIdx++ - break - } - } - } - _ = bufIdx - } else { - argBlock = make([]byte, fl.Args) - sess, err = debug.Launch("", path, *funcName, argBlock) - if err != nil { - fmt.Fprintf(os.Stderr, "gasm debug: %v\n", err) - return 1 - } - } - defer sess.Kill() - - bm := debug.NewBreakpoints(sess) - fmt.Printf("gasm debug: %s in %s (pid %d)\n", *funcName, path, sess.Pid()) - - // Convert the line table for the REPL. - var srcLines []debug.SourceLine - for _, le := range fl.Lines { - srcLines = append(srcLines, debug.SourceLine{Offset: le.Offset, Line: le.Line}) - } - debug.REPL(sess, bm, sess.CodeBase(), fl.Offset, fl.Size, fl.Args, labels, srcLines) - return 0 -} - -// parseBufNames extracts buffer names from a buffer specification. -// Format: name:size:pattern[,name:size:pattern...] -func parseBufNames(spec string) []string { - var names []string - for _, part := range strings.Split(spec, ",") { - fields := strings.SplitN(part, ":", 3) - if len(fields) >= 1 && fields[0] != "" { - names = append(names, fields[0]) - } - } - return names -} - -// parseBufSize extracts the size of a named buffer from a buffer specification. -func parseBufSize(spec, name string) int { - for _, part := range strings.Split(spec, ",") { - fields := strings.SplitN(part, ":", 3) - if len(fields) >= 2 && fields[0] == name { - var size int - fmt.Sscanf(fields[1], "%d", &size) - return size - } - } - return 0 -}