
Changes implementations of proc.Registers interface and the op.DwarfRegisters struct so that floating point registers can be loaded only when they are needed. Removes the floatingPoint parameter from proc.Thread.Registers. This accomplishes three things: 1. it simplifies the proc.Thread.Registers interface 2. it makes it impossible to accidentally create a broken set of saved registers or of op.DwarfRegisters by accidentally calling Registers(false) 3. it improves general performance of Delve by avoiding to load floating point registers as much as possible Floating point registers are loaded under two circumstances: 1. When the Slice method is called with floatingPoint == true 2. When the Copy method is called Benchmark before: BenchmarkConditionalBreakpoints-4 1 4327350142 ns/op Benchmark after: BenchmarkConditionalBreakpoints-4 1 3852642917 ns/op Updates #1549
305 lines
8.2 KiB
Go
305 lines
8.2 KiB
Go
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
|
||
}
|