// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause //go:build freebsd && amd64 package debug import "fmt" func printRegs(regs *Regs, codeBase, funcOff uint64) { fmt.Printf(" RIP = %#016x (func+%#x)\n", regs.RIP, regs.RIP-codeBase-funcOff) fmt.Printf(" RSP = %#016x RBP = %#016x\n", regs.RSP, regs.RBP) fmt.Printf(" RAX = %#016x RBX = %#016x\n", regs.RAX, regs.RBX) fmt.Printf(" RCX = %#016x RDX = %#016x\n", regs.RCX, regs.RDX) fmt.Printf(" RSI = %#016x RDI = %#016x\n", regs.RSI, regs.RDI) fmt.Printf(" R8 = %#016x R9 = %#016x\n", regs.R8, regs.R9) fmt.Printf(" R10 = %#016x R11 = %#016x\n", regs.R10, regs.R11) fmt.Printf(" R12 = %#016x R13 = %#016x\n", regs.R12, regs.R13) fmt.Printf(" R14 = %#016x R15 = %#016x\n", regs.R14, regs.R15) fmt.Printf(" RFLAGS = %#x [%s]\n", regs.RFLAGS, decodeRflags(regs.RFLAGS)) } func printVectorRegs(v *VectorRegs) { fmt.Println("\n Vector registers (YMM):") for i := 0; i < 16; i += 2 { fmt.Printf(" YMM%-2d = ", i) printYMM(v.YMM[i][:]) fmt.Printf(" YMM%-2d = ", i+1) printYMM(v.YMM[i+1][:]) fmt.Println() } } func printYMM(b []byte) { for j := 0; j < 32; j += 4 { v := uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24 fmt.Printf("%08x ", v) } } func decodeRflags(f uint64) string { var flags string if f&1 != 0 { flags += "CF " } if f&(1<<2) != 0 { flags += "PF " } if f&(1<<4) != 0 { flags += "AF " } if f&(1<<6) != 0 { flags += "ZF " } if f&(1<<7) != 0 { flags += "SF " } if f&(1<<8) != 0 { flags += "TF " } if f&(1<<9) != 0 { flags += "IF " } if f&(1<<10) != 0 { flags += "DF " } if f&(1<<11) != 0 { flags += "OF " } if flags == "" { return "none" } return flags[:len(flags)-1] } // SetReg modifies a register value in the debuggee. func (s *Session) SetReg(name string, value uint64) error { regs, err := s.GetRegs() if err != nil { return err } switch name { case "rax", "eax", "ax", "al": regs.RAX = value case "rbx", "ebx", "bx", "bl": regs.RBX = value case "rcx", "ecx", "cx", "cl": regs.RCX = value case "rdx", "edx", "dx", "dl": regs.RDX = value case "rsi", "esi", "si": regs.RSI = value case "rdi", "edi", "di": regs.RDI = value case "rbp", "ebp", "bp": regs.RBP = value case "rsp", "esp", "sp": regs.RSP = value case "r8": regs.R8 = value case "r9": regs.R9 = value case "r10": regs.R10 = value case "r11": regs.R11 = value case "r12": regs.R12 = value case "r13": regs.R13 = value case "r14": regs.R14 = value case "r15": regs.R15 = value case "rip", "eip": regs.RIP = value default: return fmt.Errorf("debug: unknown register %q", name) } return s.SetRegs(®s) } // archReturnAddr reads the return address of the current frame (amd64 // ABI0 convention). A function that contains a CALL (or has a frame) is // assembled with the prologue PUSHQ BP; MOVQ SP, BP, so mid-function the // word at SP is the saved caller BP, a stack address, and the return // address sits further up. Walk the stack from SP and take the first word // that lies in an executable mapping: stack and data words never do, a // return address always does. FreeBSD exposes no mapping list, so the // walk degenerates to the raw entry convention, [SP] before any push. func archReturnAddr(s *Session, regs *Regs) (uint64, error) { return s.Peek(regs.RSP) } // archSPLabel returns the SP register name for display. func archSPLabel() string { return "RSP" }