Files
gasm-sdk/debug/stopinfo_freebsd.go
T
2026-09-25 21:46:40 +02:00

74 lines
2.4 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
//go:build freebsd && (amd64 || arm64 || riscv64)
package debug
import (
"encoding/binary"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
// FreeBSD TRAP_* si_code values (sys/signal.h). A breakpoint (INT3, BRK,
// EBREAK) arrives as TRAP_BRKPT on every supported architecture; TRAP_TRACE
// is shared by the completed single-step and the hardware watchpoint hit,
// so the watchpoint layer disambiguates from the debug registers.
const (
trapBRKPT = 1 // TRAP_BRKPT
trapTRACE = 2 // TRAP_TRACE
)
// StopReason describes why the debuggee stopped.
type StopReason int
const (
StopNone StopReason = iota
StopBreakpoint // software breakpoint hit
StopWatchpoint // hardware watchpoint triggered
StopSingleStep // single-step completed
StopSignal // stopped by a signal
StopExited // process exited
)
// StopInfo returns the reason the debuggee stopped and the faulting address
// (for watchpoints, the watched address that was accessed). FreeBSD has no
// PTRACE_GETSIGINFO; the stop's signal information comes from PT_LWPINFO,
// whose pl_siginfo carries the siginfo the kernel delivered. A ptrace stop
// with no signal behind it (a completed single-step, the initial attach)
// fills no siginfo at all.
func (s *Session) StopInfo() (StopReason, uint64) {
if s.exited {
return StopExited, 0
}
var info unix.PtraceLwpInfoStruct
if err := unix.PtraceLwpInfo(s.pid, &info); err != nil {
return StopNone, 0
}
// The siginfo layout is the FreeBSD siginfo_t: three leading ints
// (signo, errno, code), then the union, 8-byte aligned, whose _fault
// member puts the address at byte offset 16. The read is byte-wise
// because the blob's alignment is not guaranteed.
si := (*[64]byte)(unsafe.Pointer(&info.Siginfo))
signo := int32(binary.LittleEndian.Uint32(si[0:4]))
code := int32(binary.LittleEndian.Uint32(si[8:12]))
switch {
case signo == 0:
// A pure ptrace stop: single-step completion, attach, or the
// events the kernel resolves internally.
return StopSingleStep, 0
case signo != int32(syscall.SIGTRAP):
return StopSignal, uint64(code)
case code == trapBRKPT:
return StopBreakpoint, 0
case code == trapTRACE:
addr := binary.LittleEndian.Uint64(si[16:24])
return archStopTrace(s, addr)
default:
return StopSingleStep, 0
}
}