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 && riscv64
|
|
|
|
|
|
|
|
|
|
package debug
|
|
|
|
|
|
|
|
|
|
// Regs holds the full general-purpose register set of a traced process
|
|
|
|
|
// (the Linux riscv64 user_regs_struct layout).
|
|
|
|
|
type Regs struct {
|
|
|
|
|
PC uint64
|
|
|
|
|
Ra uint64 // x1 (return address)
|
|
|
|
|
Sp uint64 // x2
|
|
|
|
|
Gp uint64 // x3
|
|
|
|
|
Tp uint64 // x4
|
|
|
|
|
T0 uint64 // x5
|
|
|
|
|
T1 uint64 // x6
|
|
|
|
|
T2 uint64 // x7
|
|
|
|
|
S0 uint64 // x8 (frame pointer)
|
|
|
|
|
S1 uint64 // x9
|
|
|
|
|
A0 uint64 // x10
|
|
|
|
|
A1 uint64 // x11
|
|
|
|
|
A2 uint64 // x12
|
|
|
|
|
A3 uint64 // x13
|
|
|
|
|
A4 uint64 // x14
|
|
|
|
|
A5 uint64 // x15
|
|
|
|
|
A6 uint64 // x16
|
|
|
|
|
A7 uint64 // x17
|
|
|
|
|
S2 uint64 // x18
|
|
|
|
|
S3 uint64 // x19
|
|
|
|
|
S4 uint64 // x20
|
|
|
|
|
S5 uint64 // x21
|
|
|
|
|
S6 uint64 // x22
|
|
|
|
|
S7 uint64 // x23
|
|
|
|
|
S8 uint64 // x24
|
|
|
|
|
S9 uint64 // x25
|
|
|
|
|
S10 uint64 // x26
|
|
|
|
|
S11 uint64 // x27
|
|
|
|
|
T3 uint64 // x28
|
|
|
|
|
T4 uint64 // x29
|
|
|
|
|
T5 uint64 // x30
|
|
|
|
|
T6 uint64 // x31
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetPC returns the program counter.
|
|
|
|
|
func (r *Regs) GetPC() uint64 { return r.PC }
|
|
|
|
|
|
|
|
|
|
// SetPC sets the program counter.
|
|
|
|
|
func (r *Regs) SetPC(pc uint64) { r.PC = pc }
|
|
|
|
|
|
|
|
|
|
// GetSP returns the stack pointer.
|
|
|
|
|
func (r *Regs) GetSP() uint64 { return r.Sp }
|
|
|
|
|
|
|
|
|
|
// RegValue returns the value of the named register, or false if unknown.
|
|
|
|
|
func (r *Regs) RegValue(name string) (uint64, bool) {
|
|
|
|
|
switch name {
|
|
|
|
|
case "pc":
|
|
|
|
|
return r.PC, true
|
|
|
|
|
case "ra", "x1":
|
|
|
|
|
return r.Ra, true
|
|
|
|
|
case "sp", "x2":
|
|
|
|
|
return r.Sp, true
|
|
|
|
|
case "gp", "x3":
|
|
|
|
|
return r.Gp, true
|
|
|
|
|
case "tp", "x4":
|
|
|
|
|
return r.Tp, true
|
|
|
|
|
case "t0", "x5":
|
|
|
|
|
return r.T0, true
|
|
|
|
|
case "t1", "x6":
|
|
|
|
|
return r.T1, true
|
|
|
|
|
case "t2", "x7":
|
|
|
|
|
return r.T2, true
|
|
|
|
|
case "s0", "fp", "x8":
|
|
|
|
|
return r.S0, true
|
|
|
|
|
case "s1", "x9":
|
|
|
|
|
return r.S1, true
|
|
|
|
|
case "a0", "x10":
|
|
|
|
|
return r.A0, true
|
|
|
|
|
case "a1", "x11":
|
|
|
|
|
return r.A1, true
|
|
|
|
|
case "a2", "x12":
|
|
|
|
|
return r.A2, true
|
|
|
|
|
case "a3", "x13":
|
|
|
|
|
return r.A3, true
|
|
|
|
|
case "a4", "x14":
|
|
|
|
|
return r.A4, true
|
|
|
|
|
case "a5", "x15":
|
|
|
|
|
return r.A5, true
|
|
|
|
|
case "a6", "x16":
|
|
|
|
|
return r.A6, true
|
|
|
|
|
case "a7", "x17":
|
|
|
|
|
return r.A7, true
|
|
|
|
|
case "s2", "x18":
|
|
|
|
|
return r.S2, true
|
|
|
|
|
case "s3", "x19":
|
|
|
|
|
return r.S3, true
|
|
|
|
|
case "s4", "x20":
|
|
|
|
|
return r.S4, true
|
|
|
|
|
case "s5", "x21":
|
|
|
|
|
return r.S5, true
|
|
|
|
|
case "s6", "x22":
|
|
|
|
|
return r.S6, true
|
|
|
|
|
case "s7", "x23":
|
|
|
|
|
return r.S7, true
|
|
|
|
|
case "s8", "x24":
|
|
|
|
|
return r.S8, true
|
|
|
|
|
case "s9", "x25":
|
|
|
|
|
return r.S9, true
|
|
|
|
|
case "s10", "x26":
|
|
|
|
|
return r.S10, true
|
|
|
|
|
case "s11", "x27":
|
|
|
|
|
return r.S11, true
|
|
|
|
|
case "t3", "x28":
|
|
|
|
|
return r.T3, true
|
|
|
|
|
case "t4", "x29":
|
|
|
|
|
return r.T4, true
|
|
|
|
|
case "t5", "x30":
|
|
|
|
|
return r.T5, true
|
|
|
|
|
case "t6", "x31":
|
|
|
|
|
return r.T6, true
|
|
|
|
|
default:
|
|
|
|
|
return 0, false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// breakpointInsn is the software breakpoint instruction (EBREAK).
|
|
|
|
|
var breakpointInsn = []byte{0x73, 0x00, 0x10, 0x00} // ebreak
|
|
|
|
|
|
2026-09-19 23:49:19 +02:00
|
|
|
// breakpointPCAdjust is how far PC is past the breakpoint instruction after
|
|
|
|
|
// a trap: 0, because the kernel delivers the EBREAK SIGTRAP with sepc still
|
|
|
|
|
// on the ebreak. handle_break passes regs->epc straight to
|
|
|
|
|
// force_sig_fault(SIGTRAP, TRAP_BRKPT, ...) and only the kernel-internal
|
|
|
|
|
// WARN/CFI paths advance epc (arch/riscv/kernel/traps.c).
|
|
|
|
|
const breakpointPCAdjust = 0
|