103 lines
2.8 KiB
Go
103 lines
2.8 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux && 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]
|
|
}
|
|
|
|
// 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.
|
|
func archReturnAddr(s *Session, regs *Regs) (uint64, error) {
|
|
ranges := execRanges(s.pid)
|
|
for off := uint64(0); off < 512; off += 8 {
|
|
word, err := s.Peek(regs.RSP + off)
|
|
if err != nil {
|
|
break
|
|
}
|
|
for _, r := range ranges {
|
|
if word >= r.lo && word < r.hi {
|
|
return word, nil
|
|
}
|
|
}
|
|
}
|
|
// No mapping available or nothing code-like on the stack: fall back to
|
|
// the raw entry convention, [SP] before any push.
|
|
return s.Peek(regs.RSP)
|
|
}
|
|
|
|
// archSPLabel returns the SP register name for display.
|
|
func archSPLabel() string { return "RSP" }
|