2020-12-14 17:39:01 +00:00
|
|
|
|
package amd64util
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"github.com/go-delve/delve/pkg/proc"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AMD64Xstate 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 AMD64Xstate struct {
|
|
|
|
|
AMD64PtraceFpRegs
|
|
|
|
|
Xsave []byte // raw xsave area
|
|
|
|
|
AvxState bool // contains AVX state
|
|
|
|
|
YmmSpace [256]byte
|
|
|
|
|
Avx512State bool // contains AVX512 state
|
|
|
|
|
ZmmSpace [512]byte
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AMD64PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
|
|
|
|
|
type AMD64PtraceFpRegs 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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decode decodes an XSAVE area to a list of name/value pairs of registers.
|
2023-08-17 21:24:43 +00:00
|
|
|
|
func (xstate *AMD64Xstate) Decode() []proc.Register {
|
2020-12-14 17:39:01 +00:00
|
|
|
|
var regs []proc.Register
|
|
|
|
|
// x87 registers
|
2023-08-17 21:24:43 +00:00
|
|
|
|
regs = proc.AppendUint64Register(regs, "CW", uint64(xstate.Cwd))
|
|
|
|
|
regs = proc.AppendUint64Register(regs, "SW", uint64(xstate.Swd))
|
|
|
|
|
regs = proc.AppendUint64Register(regs, "TW", uint64(xstate.Ftw))
|
|
|
|
|
regs = proc.AppendUint64Register(regs, "FOP", uint64(xstate.Fop))
|
|
|
|
|
regs = proc.AppendUint64Register(regs, "FIP", xstate.Rip)
|
|
|
|
|
regs = proc.AppendUint64Register(regs, "FDP", xstate.Rdp)
|
2020-12-14 17:39:01 +00:00
|
|
|
|
|
2023-08-17 21:24:43 +00:00
|
|
|
|
for i := 0; i < len(xstate.StSpace); i += 4 {
|
2020-12-14 17:39:01 +00:00
|
|
|
|
var buf bytes.Buffer
|
2023-08-17 21:24:43 +00:00
|
|
|
|
binary.Write(&buf, binary.LittleEndian, uint64(xstate.StSpace[i+1])<<32|uint64(xstate.StSpace[i]))
|
|
|
|
|
binary.Write(&buf, binary.LittleEndian, uint16(xstate.StSpace[i+2]))
|
2020-12-14 17:39:01 +00:00
|
|
|
|
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("ST(%d)", i/4), buf.Bytes())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SSE registers
|
2023-08-17 21:24:43 +00:00
|
|
|
|
regs = proc.AppendUint64Register(regs, "MXCSR", uint64(xstate.Mxcsr))
|
|
|
|
|
regs = proc.AppendUint64Register(regs, "MXCSR_MASK", uint64(xstate.MxcrMask))
|
2020-12-14 17:39:01 +00:00
|
|
|
|
|
2023-08-17 21:24:43 +00:00
|
|
|
|
for i := 0; i < len(xstate.XmmSpace); i += 16 {
|
2020-12-14 17:39:01 +00:00
|
|
|
|
n := i / 16
|
2023-08-17 21:24:43 +00:00
|
|
|
|
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("XMM%d", n), xstate.XmmSpace[i:i+16])
|
|
|
|
|
if xstate.AvxState {
|
|
|
|
|
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("YMM%d", n), xstate.YmmSpace[i:i+16])
|
|
|
|
|
if xstate.Avx512State {
|
|
|
|
|
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("ZMM%d", n), xstate.ZmmSpace[n*32:(n+1)*32])
|
2020-12-14 17:39:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return regs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
_XSTATE_MAX_KNOWN_SIZE = 2969
|
|
|
|
|
|
2021-03-04 18:28:28 +00:00
|
|
|
|
_XSAVE_XMM_REGION_START = 160
|
2020-12-14 17:39:01 +00:00
|
|
|
|
_XSAVE_HEADER_START = 512
|
|
|
|
|
_XSAVE_HEADER_LEN = 64
|
|
|
|
|
_XSAVE_EXTENDED_REGION_START = 576
|
|
|
|
|
_XSAVE_SSE_REGION_LEN = 416
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AMD64XstateRead 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.
|
2024-10-21 16:16:57 +00:00
|
|
|
|
// If xstateZMMHi256Offset is zero, it will be guessed.
|
|
|
|
|
func AMD64XstateRead(xstateargs []byte, readLegacy bool, regset *AMD64Xstate, xstateZMMHi256Offset int) error {
|
2020-12-14 17:39:01 +00:00
|
|
|
|
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.AMD64PtraceFpRegs); 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)])
|
|
|
|
|
|
|
|
|
|
if xstate_bv&(1<<6) == 0 {
|
|
|
|
|
// AVX512 state not present
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-21 16:16:57 +00:00
|
|
|
|
if xstateZMMHi256Offset == 0 {
|
|
|
|
|
// Guess ZMM_Hi256 component offset
|
|
|
|
|
// ref: https://github.com/bminor/binutils-gdb/blob/df89bdf0baf106c3b0a9fae53e4e48607a7f3f87/gdb/i387-tdep.c#L916
|
|
|
|
|
if xstate_bv&(1<<9) != 0 && len(xstateargs) == 2440 {
|
|
|
|
|
// AMD CPUs supporting PKRU
|
|
|
|
|
xstateZMMHi256Offset = 896
|
|
|
|
|
} else {
|
|
|
|
|
// Intel CPUs supporting AVX512
|
|
|
|
|
xstateZMMHi256Offset = 1152
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
avx512state := xstateargs[xstateZMMHi256Offset:]
|
2020-12-14 17:39:01 +00:00
|
|
|
|
regset.Avx512State = true
|
|
|
|
|
copy(regset.ZmmSpace[:], avx512state[:len(regset.ZmmSpace)])
|
|
|
|
|
|
|
|
|
|
// TODO(aarzilli): if xstate_bv&(1<<7) is set then xstateargs[1664:2688]
|
|
|
|
|
// contains ZMM16 through ZMM31, those aren't just the higher 256bits, it's
|
|
|
|
|
// the full register so each is 64 bytes (512bits)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2021-03-04 18:28:28 +00:00
|
|
|
|
|
|
|
|
|
func (xstate *AMD64Xstate) SetXmmRegister(n int, value []byte) error {
|
|
|
|
|
if n >= 16 {
|
|
|
|
|
return fmt.Errorf("setting register XMM%d not supported", n)
|
|
|
|
|
}
|
|
|
|
|
if len(value) > 64 {
|
|
|
|
|
return fmt.Errorf("value of register XMM%d too large (%d bytes)", n, len(value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy least significant 16 bytes to Xsave area
|
|
|
|
|
|
|
|
|
|
xmmval := value
|
|
|
|
|
if len(xmmval) > 16 {
|
|
|
|
|
xmmval = xmmval[:16]
|
|
|
|
|
}
|
|
|
|
|
rest := value[len(xmmval):]
|
|
|
|
|
|
|
|
|
|
xmmpos := _XSAVE_XMM_REGION_START + (n * 16)
|
|
|
|
|
if xmmpos >= len(xstate.Xsave) {
|
|
|
|
|
return fmt.Errorf("could not set XMM%d: not in XSAVE area", n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy(xstate.Xsave[xmmpos:], xmmval)
|
|
|
|
|
|
|
|
|
|
if len(rest) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy bytes [16, 32) to Xsave area
|
|
|
|
|
|
|
|
|
|
ymmval := rest
|
|
|
|
|
if len(ymmval) > 16 {
|
|
|
|
|
ymmval = ymmval[:16]
|
|
|
|
|
}
|
|
|
|
|
rest = rest[len(ymmval):]
|
|
|
|
|
|
|
|
|
|
ymmpos := _XSAVE_EXTENDED_REGION_START + (n * 16)
|
|
|
|
|
if ymmpos >= len(xstate.Xsave) {
|
|
|
|
|
return fmt.Errorf("could not set XMM%d: bytes 16..%d not in XSAVE area", n, 16+len(ymmval))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy(xstate.Xsave[ymmpos:], ymmval)
|
|
|
|
|
|
|
|
|
|
if len(rest) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy bytes [32, 64) to Xsave area
|
|
|
|
|
|
|
|
|
|
zmmval := rest
|
2024-10-21 16:16:57 +00:00
|
|
|
|
zmmpos := AMD64XstateZMMHi256Offset() + (n * 32)
|
2021-03-04 18:28:28 +00:00
|
|
|
|
if zmmpos >= len(xstate.Xsave) {
|
|
|
|
|
return fmt.Errorf("could not set XMM%d: bytes 32..%d not in XSAVE area", n, 32+len(zmmval))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copy(xstate.Xsave[zmmpos:], zmmval)
|
|
|
|
|
return nil
|
|
|
|
|
}
|