// Copyright (c) 2026 Petr BalvĂ­n (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