feat(debug): port the debugger to FreeBSD
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build freebsd && riscv64
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// GetRegs reads the general-purpose registers of the stopped debuggee and
|
||||
// converts the FreeBSD struct reg into the portable layout. Sstatus rides
|
||||
// the kernel's struct but the portable surface carries the GPRs and PC.
|
||||
func (s *Session) GetRegs() (Regs, error) {
|
||||
var ur unix.Reg
|
||||
if err := unix.PtraceGetRegs(s.pid, &ur); err != nil {
|
||||
return Regs{}, fmt.Errorf("debug: PT_GETREGS: %w", err)
|
||||
}
|
||||
return Regs{
|
||||
PC: ur.Sepc,
|
||||
Ra: ur.Ra,
|
||||
Sp: ur.Sp,
|
||||
Gp: ur.Gp,
|
||||
Tp: ur.Tp,
|
||||
T0: ur.T[0],
|
||||
T1: ur.T[1],
|
||||
T2: ur.T[2],
|
||||
S0: ur.S[0],
|
||||
S1: ur.S[1],
|
||||
A0: ur.A[0],
|
||||
A1: ur.A[1],
|
||||
A2: ur.A[2],
|
||||
A3: ur.A[3],
|
||||
A4: ur.A[4],
|
||||
A5: ur.A[5],
|
||||
A6: ur.A[6],
|
||||
A7: ur.A[7],
|
||||
S2: ur.S[2],
|
||||
S3: ur.S[3],
|
||||
S4: ur.S[4],
|
||||
S5: ur.S[5],
|
||||
S6: ur.S[6],
|
||||
S7: ur.S[7],
|
||||
S8: ur.S[8],
|
||||
S9: ur.S[9],
|
||||
S10: ur.S[10],
|
||||
S11: ur.S[11],
|
||||
T3: ur.T[3],
|
||||
T4: ur.T[4],
|
||||
T5: ur.T[5],
|
||||
T6: ur.T[6],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetRegs writes the general-purpose registers of the stopped debuggee.
|
||||
// Read-modify-write keeps sstatus, which the kernel owns, intact.
|
||||
func (s *Session) SetRegs(regs *Regs) error {
|
||||
var ur unix.Reg
|
||||
if err := unix.PtraceGetRegs(s.pid, &ur); err != nil {
|
||||
return fmt.Errorf("debug: PT_GETREGS: %w", err)
|
||||
}
|
||||
ur.Sepc = regs.PC
|
||||
ur.Ra = regs.Ra
|
||||
ur.Sp = regs.Sp
|
||||
ur.Gp = regs.Gp
|
||||
ur.Tp = regs.Tp
|
||||
ur.T = [7]uint64{regs.T0, regs.T1, regs.T2, regs.T3, regs.T4, regs.T5, regs.T6}
|
||||
ur.S = [12]uint64{regs.S0, regs.S1, regs.S2, regs.S3, regs.S4, regs.S5,
|
||||
regs.S6, regs.S7, regs.S8, regs.S9, regs.S10, regs.S11}
|
||||
ur.A = [8]uint64{regs.A0, regs.A1, regs.A2, regs.A3, regs.A4, regs.A5, regs.A6, regs.A7}
|
||||
return unix.PtraceSetRegs(s.pid, &ur)
|
||||
}
|
||||
|
||||
// FPRegs holds the RISC-V FP register state (32 64-bit FP registers plus
|
||||
// fcsr).
|
||||
type FPRegs struct {
|
||||
F [32]uint64 // F0-F31 (64-bit FP registers)
|
||||
FCSR uint32
|
||||
}
|
||||
|
||||
// GetFPRegs retrieves the FP register state via PT_GETFPREGS. The FreeBSD
|
||||
// struct fpreg carries each 64-bit FP register in a 128-bit slot (fp_x is
|
||||
// the flat [64]-word area the x/sys type renders as [32][2]); the low word
|
||||
// holds the register, and FCSR rides the tail.
|
||||
func (s *Session) GetFPRegs() (FPRegs, error) {
|
||||
var fp FPRegs
|
||||
var fr unix.FpReg
|
||||
if err := unix.PtraceGetFpRegs(s.pid, &fr); err != nil {
|
||||
return fp, fmt.Errorf("debug: PT_GETFPREGS: %w", err)
|
||||
}
|
||||
for i := range 32 {
|
||||
fp.F[i] = fr.X[i][0]
|
||||
}
|
||||
fp.FCSR = uint32(fr.Fcsr)
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
// VectorRegs holds the FP register state shown by the regs command
|
||||
// (riscv64 has 32 64-bit FP registers and fcsr).
|
||||
type VectorRegs struct {
|
||||
F [32]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, FCSR: fp.FCSR}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user