delve/pkg/proc/linutil/regs_riscv64_arch.go
2024-10-11 12:34:25 -07:00

387 lines
8.6 KiB
Go

package linutil
import (
"encoding/binary"
"fmt"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc"
"golang.org/x/arch/riscv64/riscv64asm"
)
// RISCV64Registers implements the proc.Registers interface.
type RISCV64Registers struct {
Regs *RISCV64PtraceRegs // general-purpose registers & pc
iscgo bool
tp_tls uint64
Fpregs []proc.Register // Formatted floating-point registers
Fpregset []byte // holding all floating-point register values
loadFpRegs func(*RISCV64Registers) error
}
func NewRISCV64Registers(regs *RISCV64PtraceRegs, iscgo bool, tp_tls uint64,
loadFpRegs func(*RISCV64Registers) error) *RISCV64Registers {
return &RISCV64Registers{
Regs: regs,
iscgo: iscgo,
tp_tls: tp_tls,
loadFpRegs: loadFpRegs,
}
}
// RISCV64PtraceRegs is the struct used by the linux kernel to return the
// general purpose registers for RISC-V CPUs.
type RISCV64PtraceRegs struct {
Pc uint64
Ra uint64
Sp uint64
Gp uint64
Tp uint64
T0 uint64
T1 uint64
T2 uint64
S0 uint64
S1 uint64
A0 uint64
A1 uint64
A2 uint64
A3 uint64
A4 uint64
A5 uint64
A6 uint64
A7 uint64
S2 uint64
S3 uint64
S4 uint64
S5 uint64
S6 uint64
S7 uint64
S8 uint64
S9 uint64
S10 uint64
S11 uint64
T3 uint64
T4 uint64
T5 uint64
T6 uint64
}
// Slice returns the registers as a list of (name, value) pairs.
func (r *RISCV64Registers) Slice(floatingPoint bool) ([]proc.Register, error) {
var regs64 = []struct {
k string
v uint64
}{
{"X1", r.Regs.Ra},
{"X2", r.Regs.Sp},
{"X3", r.Regs.Gp},
{"X4", r.Regs.Tp},
{"X5", r.Regs.T0},
{"X6", r.Regs.T1},
{"X7", r.Regs.T2},
{"X8", r.Regs.S0},
{"X9", r.Regs.S1},
{"X10", r.Regs.A0},
{"X11", r.Regs.A1},
{"X12", r.Regs.A2},
{"X13", r.Regs.A3},
{"X14", r.Regs.A4},
{"X15", r.Regs.A5},
{"X16", r.Regs.A6},
{"X17", r.Regs.A7},
{"X18", r.Regs.S2},
{"X19", r.Regs.S3},
{"X20", r.Regs.S4},
{"X21", r.Regs.S5},
{"X22", r.Regs.S6},
{"X23", r.Regs.S7},
{"X24", r.Regs.S8},
{"X25", r.Regs.S9},
{"X26", r.Regs.S10},
{"X27", r.Regs.S11},
{"X28", r.Regs.T3},
{"X29", r.Regs.T4},
{"X30", r.Regs.T5},
{"X31", r.Regs.T6},
{"PC", r.Regs.Pc},
}
out := make([]proc.Register, 0, (len(regs64) + len(r.Fpregs)))
for _, reg := range regs64 {
out = proc.AppendUint64Register(out, reg.k, reg.v)
}
var floatLoadError error
if floatingPoint {
if r.loadFpRegs != nil {
floatLoadError = r.loadFpRegs(r)
r.loadFpRegs = nil
}
out = append(out, r.Fpregs...)
}
return out, floatLoadError
}
// PC returns the value of PC register.
func (r *RISCV64Registers) PC() uint64 {
// PC Register
return r.Regs.Pc
}
// SP returns the value of SP register.
func (r *RISCV64Registers) SP() uint64 {
// Stack pointer
return r.Regs.Sp
}
// BP returns the value of FP register
func (r *RISCV64Registers) BP() uint64 {
// unused FP register
return 0
}
// TLS returns the address of the thread local storage memory segment.
func (r *RISCV64Registers) TLS() uint64 {
// TODO: calling cgo may overwrite $x27, read it from the kernel
if !r.iscgo {
return 0
}
return r.tp_tls
}
// GAddr returns the address of the G variable if it is known, 0 and false otherwise.
func (r *RISCV64Registers) GAddr() (uint64, bool) {
// Defined in $GOROOT/cmd/internal/obj/riscv/cpu.go.
return r.Regs.S11, !r.iscgo
}
// LR returns the link register.
func (r *RISCV64Registers) LR() uint64 {
return r.Regs.Ra
}
// Copy returns a copy of these registers that is guaranteed not to change.
func (r *RISCV64Registers) Copy() (proc.Registers, error) {
if r.loadFpRegs != nil {
err := r.loadFpRegs(r)
r.loadFpRegs = nil
if err != nil {
return nil, err
}
}
var rr RISCV64Registers
rr.Regs = &RISCV64PtraceRegs{}
*(rr.Regs) = *(r.Regs)
if r.Fpregs != nil {
rr.Fpregs = make([]proc.Register, len(r.Fpregs))
copy(rr.Fpregs, r.Fpregs)
}
if r.Fpregset != nil {
rr.Fpregset = make([]byte, len(r.Fpregset))
copy(rr.Fpregset, r.Fpregset)
}
return &rr, nil
}
func (r *RISCV64Registers) GetReg(regNum uint64) (uint64, error) {
reg := riscv64asm.Reg(regNum)
if reg <= riscv64asm.X31 {
switch regNum {
case regnum.RISCV64_LR:
return uint64(r.Regs.Ra), nil
case regnum.RISCV64_SP:
return uint64(r.Regs.Sp), nil
case regnum.RISCV64_GP:
return uint64(r.Regs.Gp), nil
case regnum.RISCV64_TP:
return uint64(r.Regs.Tp), nil
case regnum.RISCV64_T0:
return uint64(r.Regs.T0), nil
case regnum.RISCV64_T1:
return uint64(r.Regs.T1), nil
case regnum.RISCV64_T2:
return uint64(r.Regs.T2), nil
case regnum.RISCV64_S0:
return uint64(r.Regs.S0), nil
case regnum.RISCV64_S1:
return uint64(r.Regs.S1), nil
case regnum.RISCV64_A0:
return uint64(r.Regs.A0), nil
case regnum.RISCV64_A1:
return uint64(r.Regs.A1), nil
case regnum.RISCV64_A2:
return uint64(r.Regs.A2), nil
case regnum.RISCV64_A3:
return uint64(r.Regs.A3), nil
case regnum.RISCV64_A4:
return uint64(r.Regs.A4), nil
case regnum.RISCV64_A5:
return uint64(r.Regs.A5), nil
case regnum.RISCV64_A6:
return uint64(r.Regs.A6), nil
case regnum.RISCV64_A7:
return uint64(r.Regs.A7), nil
case regnum.RISCV64_S2:
return uint64(r.Regs.S2), nil
case regnum.RISCV64_S3:
return uint64(r.Regs.S3), nil
case regnum.RISCV64_S4:
return uint64(r.Regs.S4), nil
case regnum.RISCV64_S5:
return uint64(r.Regs.S5), nil
case regnum.RISCV64_S6:
return uint64(r.Regs.S6), nil
case regnum.RISCV64_S7:
return uint64(r.Regs.S7), nil
case regnum.RISCV64_S8:
return uint64(r.Regs.S8), nil
case regnum.RISCV64_S9:
return uint64(r.Regs.S9), nil
case regnum.RISCV64_S10:
return uint64(r.Regs.S10), nil
case regnum.RISCV64_S11:
return uint64(r.Regs.S11), nil
case regnum.RISCV64_T3:
return uint64(r.Regs.T3), nil
case regnum.RISCV64_T4:
return uint64(r.Regs.T4), nil
case regnum.RISCV64_T5:
return uint64(r.Regs.T5), nil
case regnum.RISCV64_T6:
return uint64(r.Regs.T6), nil
}
}
return 0, proc.ErrUnknownRegister
}
func (r *RISCV64Registers) SetReg(regNum uint64, reg *op.DwarfRegister) (fpchanged bool, err error) {
var p *uint64
switch regNum {
case regnum.RISCV64_LR:
p = &r.Regs.Ra
case regnum.RISCV64_SP:
p = &r.Regs.Sp
case regnum.RISCV64_GP:
p = &r.Regs.Gp
case regnum.RISCV64_TP:
p = &r.Regs.Tp
case regnum.RISCV64_T0:
p = &r.Regs.T0
case regnum.RISCV64_T1:
p = &r.Regs.T1
case regnum.RISCV64_T2:
p = &r.Regs.T2
case regnum.RISCV64_S0:
p = &r.Regs.S0
case regnum.RISCV64_S1:
p = &r.Regs.S1
case regnum.RISCV64_A0:
p = &r.Regs.A0
case regnum.RISCV64_A1:
p = &r.Regs.A1
case regnum.RISCV64_A2:
p = &r.Regs.A2
case regnum.RISCV64_A3:
p = &r.Regs.A3
case regnum.RISCV64_A4:
p = &r.Regs.A4
case regnum.RISCV64_A5:
p = &r.Regs.A5
case regnum.RISCV64_A6:
p = &r.Regs.A6
case regnum.RISCV64_A7:
p = &r.Regs.A7
case regnum.RISCV64_S2:
p = &r.Regs.S2
case regnum.RISCV64_S3:
p = &r.Regs.S3
case regnum.RISCV64_S4:
p = &r.Regs.S4
case regnum.RISCV64_S5:
p = &r.Regs.S5
case regnum.RISCV64_S6:
p = &r.Regs.S6
case regnum.RISCV64_S7:
p = &r.Regs.S7
case regnum.RISCV64_S8:
p = &r.Regs.S8
case regnum.RISCV64_S9:
p = &r.Regs.S9
case regnum.RISCV64_S10:
p = &r.Regs.S10
case regnum.RISCV64_S11:
p = &r.Regs.S11
case regnum.RISCV64_T3:
p = &r.Regs.T3
case regnum.RISCV64_T4:
p = &r.Regs.T4
case regnum.RISCV64_T5:
p = &r.Regs.T5
case regnum.RISCV64_T6:
p = &r.Regs.T6
case regnum.RISCV64_PC:
p = &r.Regs.Pc
}
if p != nil {
*p = reg.Uint64Val
return false, nil
}
switch {
case regNum >= regnum.RISCV64_F0 && regNum <= regnum.RISCV64_F31:
if r.loadFpRegs != nil {
err := r.loadFpRegs(r)
r.loadFpRegs = nil
if err != nil {
return false, err
}
}
i := regNum - regnum.RISCV64_F0
reg.FillBytes()
copy(r.Fpregset[8*i:], reg.Bytes)
return true, nil
default:
return false, fmt.Errorf("changing register %d not implemented", regNum)
}
}
// RISCV64PtraceFpRegs is refer to the definition of struct __riscv_d_ext_state in the kernel ptrace.h
type RISCV64PtraceFpRegs struct {
Fregs []byte
Fcsr uint32
}
const _RISCV64_FPREGSET_LENGTH = (32 * 8)
func (fpregs *RISCV64PtraceFpRegs) Decode() (regs []proc.Register) {
for i := 0; i < len(fpregs.Fregs); i += 8 {
name := fmt.Sprintf("F%d", (i / 8))
value := fpregs.Fregs[i : i+8]
regs = proc.AppendBytesRegister(regs, name, value)
}
fcsrBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(fcsrBytes, uint32(fpregs.Fcsr))
regs = proc.AppendBytesRegister(regs, "FCSR", fcsrBytes)
return
}
func (fpregs *RISCV64PtraceFpRegs) Byte() []byte {
fpregs.Fregs = make([]byte, _RISCV64_FPREGSET_LENGTH)
return fpregs.Fregs[:]
}