141 lines
3.0 KiB
Go
141 lines
3.0 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux && arm64
|
|
|
|
package debug
|
|
|
|
// Regs holds the full general-purpose register set of a traced process
|
|
// (the Linux arm64 user_pt_regs layout).
|
|
type Regs struct {
|
|
X0 uint64
|
|
X1 uint64
|
|
X2 uint64
|
|
X3 uint64
|
|
X4 uint64
|
|
X5 uint64
|
|
X6 uint64
|
|
X7 uint64
|
|
X8 uint64
|
|
X9 uint64
|
|
X10 uint64
|
|
X11 uint64
|
|
X12 uint64
|
|
X13 uint64
|
|
X14 uint64
|
|
X15 uint64
|
|
X16 uint64
|
|
X17 uint64
|
|
X18 uint64
|
|
X19 uint64
|
|
X20 uint64
|
|
X21 uint64
|
|
X22 uint64
|
|
X23 uint64
|
|
X24 uint64
|
|
X25 uint64
|
|
X26 uint64
|
|
X27 uint64
|
|
X28 uint64
|
|
X29 uint64 // FP (frame pointer)
|
|
X30 uint64 // LR (link register)
|
|
SP uint64
|
|
PC uint64
|
|
PSTATE uint64
|
|
}
|
|
|
|
// PC 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 "x0":
|
|
return r.X0, true
|
|
case "x1":
|
|
return r.X1, true
|
|
case "x2":
|
|
return r.X2, true
|
|
case "x3":
|
|
return r.X3, true
|
|
case "x4":
|
|
return r.X4, true
|
|
case "x5":
|
|
return r.X5, true
|
|
case "x6":
|
|
return r.X6, true
|
|
case "x7":
|
|
return r.X7, true
|
|
case "x8":
|
|
return r.X8, true
|
|
case "x9":
|
|
return r.X9, true
|
|
case "x10":
|
|
return r.X10, true
|
|
case "x11":
|
|
return r.X11, true
|
|
case "x12":
|
|
return r.X12, true
|
|
case "x13":
|
|
return r.X13, true
|
|
case "x14":
|
|
return r.X14, true
|
|
case "x15":
|
|
return r.X15, true
|
|
case "x16":
|
|
return r.X16, true
|
|
case "x17":
|
|
return r.X17, true
|
|
case "x18":
|
|
return r.X18, true
|
|
case "x19":
|
|
return r.X19, true
|
|
case "x20":
|
|
return r.X20, true
|
|
case "x21":
|
|
return r.X21, true
|
|
case "x22":
|
|
return r.X22, true
|
|
case "x23":
|
|
return r.X23, true
|
|
case "x24":
|
|
return r.X24, true
|
|
case "x25":
|
|
return r.X25, true
|
|
case "x26":
|
|
return r.X26, true
|
|
case "x27":
|
|
return r.X27, true
|
|
case "x28":
|
|
return r.X28, true
|
|
case "x29", "fp":
|
|
return r.X29, true
|
|
case "x30", "lr":
|
|
return r.X30, true
|
|
case "sp":
|
|
return r.SP, true
|
|
case "pc":
|
|
return r.PC, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
// breakpointInsn is the software breakpoint instruction (BRK #0).
|
|
var breakpointInsn = []byte{0x00, 0x00, 0x20, 0xD4} // BRK #0
|
|
|
|
// breakpointPCAdjust is how far PC is past the breakpoint instruction after
|
|
// a trap: 0, because the arm64 kernel delivers the BRK SIGTRAP with the PC
|
|
// still on the BRK. do_el0_brk64 calls send_user_sigtrap, which uses
|
|
// instruction_pointer(regs) unmodified (arch/arm64/kernel/debug-monitors.c);
|
|
// only the kernel-internal skip paths advance the PC. GDB history agrees:
|
|
// decr_pc_after_break on aarch64 Linux is 0 (the +4 variant was a QEMU bug,
|
|
// sourceware PR 17280).
|
|
const breakpointPCAdjust = 0
|