// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause //go:build linux && amd64 package debug import ( "fmt" "syscall" "unsafe" ) // StopReason describes why the debuggee stopped. type StopReason int const ( StopNone StopReason = iota StopBreakpoint // INT3 breakpoint hit StopWatchpoint // hardware watchpoint triggered StopSingleStep // single-step completed StopSignal // stopped by a signal StopExited // process exited ) // siginfo_t layout (Linux amd64): si_signo, si_errno, si_code, then union. type siginfoT struct { SiSigno int32 SiErrno int32 SiCode int32 _pad [125]byte } const ( trapBRKPT = 1 // INT3 breakpoint trapHWBRKPT = 4 // hardware watchpoint ) // StopInfo returns the reason the debuggee stopped and the faulting address // (for watchpoints, the watched address that was accessed). func (s *Session) StopInfo() (StopReason, uint64) { if s.exited { return StopExited, 0 } var info siginfoT _, _, errno := syscall.Syscall6( syscall.SYS_PTRACE, uintptr(syscall.PTRACE_GETSIGINFO), uintptr(s.pid), 0, uintptr(unsafe.Pointer(&info)), 0, 0, ) if errno != 0 { return StopNone, 0 } if info.SiSigno != int32(syscall.SIGTRAP) { return StopSignal, uint64(info.SiCode) } switch info.SiCode { case trapBRKPT: return StopBreakpoint, 0 case trapHWBRKPT: // The faulting address is in si_addr (offset 16 in siginfo_t on amd64). addr := *(*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(&info)) + 16)) return StopWatchpoint, addr default: return StopSingleStep, 0 } } // SetReg modifies a register value in the debuggee. func (s *Session) SetReg(name string, value uint64) error { regs, err := s.GetRegs() if err != nil { return err } switch name { case "rax", "eax", "ax", "al": regs.RAX = value case "rbx", "ebx", "bx", "bl": regs.RBX = value case "rcx", "ecx", "cx", "cl": regs.RCX = value case "rdx", "edx", "dx", "dl": regs.RDX = value case "rsi", "esi", "si": regs.RSI = value case "rdi", "edi", "di": regs.RDI = value case "rbp", "ebp", "bp": regs.RBP = value case "rsp", "esp", "sp": regs.RSP = value case "r8": regs.R8 = value case "r9": regs.R9 = value case "r10": regs.R10 = value case "r11": regs.R11 = value case "r12": regs.R12 = value case "r13": regs.R13 = value case "r14": regs.R14 = value case "r15": regs.R15 = value case "rip", "eip": regs.RIP = value default: return fmt.Errorf("debug: unknown register %q", name) } return s.SetRegs(®s) }