137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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" }
|