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 guarenteed 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 Developer’s // 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 Developer’s 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, ®set.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 }