Files
gasm-sdk/debug/ptrace_linux_loong64.go
T
petrbalvin b5d6f1b46a fix(debug): target the traced thread and keep the kill from ever blocking
The Go runtime can migrate the debuggee's target-mode goroutine off the
process leader before PTRACE_TRACEME, which left the trace relation on a
thread the session never addressed: its stops starved the waits on the
leader, and a kill sequence that resumed nothing and then blocked in
Wait4 hung the whole package.  The debuggee now reports the traced thread
in the launch handshake and parks with a thread-directed stop, every
ptrace request and wait addresses that thread, a SIGURG arriving on a
single-step resumes it as a single-step again instead of letting the
tracee run uncontrolled, a resume rejected with ESRCH lifts a group-stop
with SIGCONT and retries once, and Kill resumes, kills and reaps through
non-blocking waits so it returns for a tracee in any state.

Assisted-by: GLM 5.3
2026-10-07 13:46:18 +02:00

98 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 linux && loong64
package debug
import (
"fmt"
"syscall"
"unsafe"
)
// GetRegs reads the general-purpose registers of the stopped debuggee.
func (s *Session) GetRegs() (Regs, error) {
var regs Regs
_, _, errno := syscall.Syscall6(
syscall.SYS_PTRACE,
uintptr(syscall.PTRACE_GETREGS),
uintptr(s.tid),
0,
uintptr(unsafe.Pointer(&regs)),
0, 0,
)
if errno != 0 {
return regs, fmt.Errorf("debug: PTRACE_GETREGS: %w", errno)
}
return regs, nil
}
// SetRegs writes the general-purpose registers of the stopped debuggee.
func (s *Session) SetRegs(regs *Regs) error {
_, _, errno := syscall.Syscall6(
syscall.SYS_PTRACE,
uintptr(syscall.PTRACE_SETREGS),
uintptr(s.tid),
0,
uintptr(unsafe.Pointer(regs)),
0, 0,
)
if errno != 0 {
return fmt.Errorf("debug: PTRACE_SETREGS: %w", errno)
}
return nil
}
// ntPrFPREG is the NT_PRFPREG note type (ELF NT ver): the FP register set.
const ntPrFPREG = 0x2
// FPRegs holds the LoongArch FP register state, matching the kernel's
// user_fp_struct layout (32 64-bit FP registers, the fcc condition flags,
// and fcsr).
type FPRegs struct {
F [32]uint64 // F0-F31 (64-bit FP registers)
FCC uint64 // eight per-register condition flags, packed
FCSR uint32
}
// GetFPRegs retrieves the FP register state via PTRACE_GETREGSET with
// NT_PRFPREG (this architecture has no PTRACE_GETFPREGS request).
func (s *Session) GetFPRegs() (FPRegs, error) {
var fp FPRegs
iovec := syscall.Iovec{
Base: (*byte)(unsafe.Pointer(&fp)),
Len: uint64(unsafe.Sizeof(fp)),
}
_, _, errno := syscall.Syscall6(
syscall.SYS_PTRACE,
uintptr(syscall.PTRACE_GETREGSET),
uintptr(s.tid),
uintptr(ntPrFPREG),
uintptr(unsafe.Pointer(&iovec)),
0, 0,
)
if errno != 0 {
return fp, fmt.Errorf("debug: PTRACE_GETREGSET (NT_PRFPREG): %w", errno)
}
return fp, nil
}
// VectorRegs holds the FP register state shown by the regs command
// (the scalar FP subset: 32 64-bit registers, fcc and fcsr; the LSX/LASX
// vector files are not read yet).
type VectorRegs struct {
F [32]uint64
FCC uint64
FCSR uint32
}
// GetVectorRegs retrieves the FP registers.
func (s *Session) GetVectorRegs() (VectorRegs, error) {
fp, err := s.GetFPRegs()
if err != nil {
return VectorRegs{}, err
}
return VectorRegs{F: fp.F, FCC: fp.FCC, FCSR: fp.FCSR}, nil
}