2026-08-20 23:57:10 +02:00
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
//go:build linux
|
|
|
|
|
|
|
|
|
|
package debug
|
|
|
|
|
|
|
|
|
|
import (
|
2026-09-19 23:49:19 +02:00
|
|
|
"encoding/binary"
|
2026-08-20 23:57:10 +02:00
|
|
|
"syscall"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// siginfo_t layout (Linux): si_signo, si_errno, si_code, then union.
|
|
|
|
|
// The si_addr field is at offset 16 on all supported architectures.
|
|
|
|
|
type siginfoT struct {
|
|
|
|
|
SiSigno int32
|
|
|
|
|
SiErrno int32
|
|
|
|
|
SiCode int32
|
|
|
|
|
_pad [125]byte
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
trapBRKPT = 1 // software 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:
|
2026-09-19 23:49:19 +02:00
|
|
|
// si_addr sits at struct offset 16 (12 bytes of signo/errno/code
|
|
|
|
|
// plus 4 bytes of union alignment). The siginfo buffer is only
|
|
|
|
|
// 4-byte aligned, so the address is read byte-wise to keep the
|
|
|
|
|
// load aligned on riscv64 and loong64. What si_addr names is
|
|
|
|
|
// architecture-specific (the data address on arm64, the
|
|
|
|
|
// instruction pointer on x86), so the per-architecture
|
|
|
|
|
// archWatchpointAddr resolves it to the watched address.
|
|
|
|
|
addr := binary.LittleEndian.Uint64(info._pad[4:12])
|
|
|
|
|
return StopWatchpoint, archWatchpointAddr(s, addr)
|
2026-08-20 23:57:10 +02:00
|
|
|
default:
|
|
|
|
|
return StopSingleStep, 0
|
|
|
|
|
}
|
|
|
|
|
}
|