// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause //go:build linux && amd64 package debug import ( "fmt" "strings" "sourcedock.dev/petrbalvin/gasm-sdk/arch" "sourcedock.dev/petrbalvin/gasm-sdk/disasm" ) // Disassemble decodes the instruction at the given address in the debuggee's // memory and returns its text representation and length in bytes. An amd64 // instruction is up to 15 bytes long, but the read must not reach past the // end of the mapping: when the full 15-byte window crosses into unmapped // memory the window shrinks, because an instruction at the mapping's end is // by construction no longer than the readable bytes that hold it. func (s *Session) Disassemble(addr uint64) (string, int, error) { var lastErr error for _, n := range []int{15, 8, 4, 2, 1} { mem, err := s.ReadMemory(addr, n) if err != nil { lastErr = err continue } ins, derr := disasm.Decode(arch.AMD64, mem, addr) if derr != nil { return "", 0, derr } return ins.Text, ins.Len, nil } return "", 0, lastErr } // DisassembleN decodes up to n instructions starting at addr and returns // them as a formatted string with addresses and byte offsets. func (s *Session) DisassembleN(addr uint64, n int) string { var result strings.Builder pc := addr for range n { text, length, err := s.Disassemble(pc) if err != nil { result.WriteString(fmt.Sprintf(" %#08x: \n", pc, err)) break } result.WriteString(fmt.Sprintf(" %#08x: %s\n", pc, text)) if length == 0 { length = 1 } pc += uint64(length) } return result.String() } // isCallInsn reports whether disassembled text (x86asm.IntelSyntax) is a // call. The first token must match exactly: a prefix test would also catch // unrelated mnemonics. func isCallInsn(text string) bool { m, _, _ := strings.Cut(text, " ") return strings.ToLower(m) == "call" }