fix(debug): handle mapping edges, stray traps and dying debuggees

Assisted-by: GLM 5.3
This commit is contained in:
2026-10-02 00:40:54 +02:00
parent dd1782c538
commit f57377abb9
19 changed files with 726 additions and 97 deletions
+16 -6
View File
@@ -75,17 +75,27 @@ func (s *Session) setDbRegs(dr *dbreg) error {
// archStopTrace classifies a TRAP_TRACE stop. On amd64 the kernel
// delivers both the completed single-step and the debug-register hit
// through T_TRCTRAP with TRAP_TRACE (sys/amd64/amd64/trap.c), and DR6's
// B0-B3 bits name the watchpoint that fired.
// B0-B3 bits name the watchpoint that fired. The bits are sticky ("the
// processor never clears DR6", Intel SDM vol 3, "Debug Registers"), so
// they are acknowledged here: cleared once read, or the next hit on a
// different slot would still see this slot's bit set and report this
// slot's address again.
func archStopTrace(s *Session, siAddr uint64) (StopReason, uint64) {
dr, err := s.getDbRegs()
if err != nil {
return StopSingleStep, 0
}
if status := dr.Dr[drStatus]; status&0xF != 0 {
for slot := range 4 {
if status&(1<<slot) != 0 && dr.Dr[slot] != 0 {
return StopWatchpoint, dr.Dr[slot]
}
status := dr.Dr[drStatus] & 0xF
if status == 0 {
return StopSingleStep, 0
}
// Best effort: the write-back only fails for a dead debuggee, for
// which no further watchpoint can fire anyway.
dr.Dr[drStatus] &^= 0xF
_ = s.setDbRegs(dr)
for slot := range 4 {
if status&(1<<slot) != 0 && dr.Dr[slot] != 0 {
return StopWatchpoint, dr.Dr[slot]
}
}
return StopSingleStep, 0