delve/pkg/proc/linutil/regs_i386_arch.go
2020-08-24 10:19:50 -07:00

305 lines
8.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package linutil
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/go-delve/delve/pkg/proc"
"golang.org/x/arch/x86/x86asm"
)
// I386Registers implements the proc.Registers interface for the native/linux
// backend and core/linux backends, on I386.
type I386Registers struct {
Regs *I386PtraceRegs
Fpregs []proc.Register
Fpregset *I386Xstate
Tls uint64
loadFpRegs func(*I386Registers) error
}
func NewI386Registers(regs *I386PtraceRegs, loadFpRegs func(*I386Registers) error) *I386Registers {
return &I386Registers{Regs: regs, Fpregs: nil, Fpregset: nil, Tls: 0, loadFpRegs: loadFpRegs}
}
// I386PtraceRegs is the struct used by the linux kernel to return the
// general purpose registers for I386 CPUs.
type I386PtraceRegs struct {
Ebx int32
Ecx int32
Edx int32
Esi int32
Edi int32
Ebp int32
Eax int32
Xds int32
Xes int32
Xfs int32
Xgs int32
Orig_eax int32
Eip int32
Xcs int32
Eflags int32
Esp int32
Xss int32
}
// Slice returns the registers as a list of (name, value) pairs.
func (r *I386Registers) Slice(floatingPoint bool) ([]proc.Register, error) {
var regs = []struct {
k string
v int32
}{
{"Ebx", r.Regs.Ebx},
{"Ecx", r.Regs.Ecx},
{"Edx", r.Regs.Edx},
{"Esi", r.Regs.Esi},
{"Edi", r.Regs.Edi},
{"Ebp", r.Regs.Ebp},
{"Eax", r.Regs.Eax},
{"Xds", r.Regs.Xds},
{"Xes", r.Regs.Xes},
{"Xfs", r.Regs.Xfs},
{"Xgs", r.Regs.Xgs},
{"Orig_eax", r.Regs.Orig_eax},
{"Eip", r.Regs.Eip},
{"Xcs", r.Regs.Xcs},
{"Eflags", r.Regs.Eflags},
{"Esp", r.Regs.Esp},
{"Xss", r.Regs.Xss},
}
out := make([]proc.Register, 0, len(regs)+len(r.Fpregs))
for _, reg := range regs {
out = proc.AppendUint64Register(out, reg.k, uint64(uint32(reg.v)))
}
var floatLoadError error
if floatingPoint {
if r.loadFpRegs != nil {
floatLoadError = r.loadFpRegs(r)
r.loadFpRegs = nil
}
out = append(out, r.Fpregs...)
}
return out, floatLoadError
}
// PC returns the value of EIP register.
func (r *I386Registers) PC() uint64 {
return uint64(uint32(r.Regs.Eip))
}
// SP returns the value of ESP register.
func (r *I386Registers) SP() uint64 {
return uint64(uint32(r.Regs.Esp))
}
func (r *I386Registers) BP() uint64 {
return uint64(uint32(r.Regs.Ebp))
}
// CX returns the value of ECX register.
func (r *I386Registers) CX() uint64 {
return uint64(uint32(r.Regs.Ecx))
}
// TLS returns the address of the thread local storage memory segment.
func (r I386Registers) TLS() uint64 {
return r.Tls
}
// GAddr returns the address of the G variable if it is known, 0 and false
// otherwise.
func (r *I386Registers) GAddr() (uint64, bool) {
return 0, false
}
// Get returns the value of the n-th register (in x86asm order).
func (r *I386Registers) Get(n int) (uint64, error) {
reg := x86asm.Reg(n)
const (
mask8 = 0x000000ff
mask16 = 0x0000ffff
)
switch reg {
// 8-bit
case x86asm.AL:
return uint64(r.Regs.Eax) & mask8, nil
case x86asm.CL:
return uint64(r.Regs.Ecx) & mask8, nil
case x86asm.DL:
return uint64(r.Regs.Edx) & mask8, nil
case x86asm.BL:
return uint64(r.Regs.Ebx) & mask8, nil
case x86asm.AH:
return (uint64(r.Regs.Eax) >> 8) & mask8, nil
case x86asm.CH:
return (uint64(r.Regs.Ecx) >> 8) & mask8, nil
case x86asm.DH:
return (uint64(r.Regs.Edx) >> 8) & mask8, nil
case x86asm.BH:
return (uint64(r.Regs.Ebx) >> 8) & mask8, nil
case x86asm.SPB:
return uint64(r.Regs.Esp) & mask8, nil
case x86asm.BPB:
return uint64(r.Regs.Ebp) & mask8, nil
case x86asm.SIB:
return uint64(r.Regs.Esi) & mask8, nil
case x86asm.DIB:
return uint64(r.Regs.Edi) & mask8, nil
// 16-bit
case x86asm.AX:
return uint64(r.Regs.Eax) & mask16, nil
case x86asm.CX:
return uint64(r.Regs.Ecx) & mask16, nil
case x86asm.DX:
return uint64(r.Regs.Edx) & mask16, nil
case x86asm.BX:
return uint64(r.Regs.Ebx) & mask16, nil
case x86asm.SP:
return uint64(r.Regs.Esp) & mask16, nil
case x86asm.BP:
return uint64(r.Regs.Ebp) & mask16, nil
case x86asm.SI:
return uint64(r.Regs.Esi) & mask16, nil
case x86asm.DI:
return uint64(r.Regs.Edi) & mask16, nil
// 32-bit
case x86asm.EAX:
return uint64(uint32(r.Regs.Eax)), nil
case x86asm.ECX:
return uint64(uint32(r.Regs.Ecx)), nil
case x86asm.EDX:
return uint64(uint32(r.Regs.Edx)), nil
case x86asm.EBX:
return uint64(uint32(r.Regs.Ebx)), nil
case x86asm.ESP:
return uint64(uint32(r.Regs.Esp)), nil
case x86asm.EBP:
return uint64(uint32(r.Regs.Ebp)), nil
case x86asm.ESI:
return uint64(uint32(r.Regs.Esi)), nil
case x86asm.EDI:
return uint64(uint32(r.Regs.Edi)), nil
}
return 0, proc.ErrUnknownRegister
}
// Copy returns a copy of these registers that is guaranteed not to change.
func (r *I386Registers) Copy() (proc.Registers, error) {
if r.loadFpRegs != nil {
err := r.loadFpRegs(r)
r.loadFpRegs = nil
if err != nil {
return nil, err
}
}
var rr I386Registers
rr.Regs = &I386PtraceRegs{}
rr.Fpregset = &I386Xstate{}
*(rr.Regs) = *(r.Regs)
if r.Fpregset != nil {
*(rr.Fpregset) = *(r.Fpregset)
}
if r.Fpregs != nil {
rr.Fpregs = make([]proc.Register, len(r.Fpregs))
copy(rr.Fpregs, r.Fpregs)
}
return &rr, nil
}
// I386PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
type I386PtraceFpRegs struct {
Cwd uint16
Swd uint16
Ftw uint16
Fop uint16
Rip uint64
Rdp uint64
Mxcsr uint32
MxcrMask uint32
StSpace [32]uint32
XmmSpace [256]byte
Padding [24]uint32
}
// I386Xstate represents amd64 XSAVE area. See Section 13.1 (and
// following) of Intel® 64 and IA-32 Architectures Software Developers
// Manual, Volume 1: Basic Architecture.
type I386Xstate struct {
I386PtraceFpRegs
Xsave []byte // raw xsave area
AvxState bool // contains AVX state
YmmSpace [256]byte
}
// Decode decodes an XSAVE area to a list of name/value pairs of registers.
func (xsave *I386Xstate) Decode() (regs []proc.Register) {
// x87 registers
regs = proc.AppendUint64Register(regs, "CW", uint64(xsave.Cwd))
regs = proc.AppendUint64Register(regs, "SW", uint64(xsave.Swd))
regs = proc.AppendUint64Register(regs, "TW", uint64(xsave.Ftw))
regs = proc.AppendUint64Register(regs, "FOP", uint64(xsave.Fop))
regs = proc.AppendUint64Register(regs, "FIP", uint64(xsave.Rip))
regs = proc.AppendUint64Register(regs, "FDP", uint64(xsave.Rdp))
for i := 0; i < len(xsave.StSpace); i += 4 {
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i]))
binary.Write(&buf, binary.LittleEndian, uint16(xsave.StSpace[i+2]))
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("ST(%d)", i/4), buf.Bytes())
}
// SSE registers
regs = proc.AppendUint64Register(regs, "MXCSR", uint64(xsave.Mxcsr))
regs = proc.AppendUint64Register(regs, "MXCSR_MASK", uint64(xsave.MxcrMask))
for i := 0; i < len(xsave.XmmSpace); i += 16 {
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16])
if xsave.AvxState {
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("YMM%d", i/16), xsave.YmmSpace[i:i+16])
}
}
return
}
// LinuxX86XstateRead reads a byte array containing an XSAVE area into regset.
// If readLegacy is true regset.PtraceFpRegs will be filled with the
// contents of the legacy region of the XSAVE area.
// See Section 13.1 (and following) of Intel® 64 and IA-32 Architectures
// Software Developers Manual, Volume 1: Basic Architecture.
func I386XstateRead(xstateargs []byte, readLegacy bool, regset *I386Xstate) error {
if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= len(xstateargs) {
return nil
}
if readLegacy {
rdr := bytes.NewReader(xstateargs[:_XSAVE_HEADER_START])
if err := binary.Read(rdr, binary.LittleEndian, &regset.I386PtraceFpRegs); err != nil {
return err
}
}
xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
if xcomp_bv&(1<<63) != 0 {
// compact format not supported
return nil
}
if xstate_bv&(1<<2) == 0 {
// AVX state not present
return nil
}
avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:]
regset.AvxState = true
copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
return nil
}