mirror of https://github.com/beego/bee.git
349 lines
9.9 KiB
Go
349 lines
9.9 KiB
Go
package proc
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/binary"
|
||
"errors"
|
||
"fmt"
|
||
"math"
|
||
"os"
|
||
"strings"
|
||
)
|
||
|
||
// Registers is an interface for a generic register type. The
|
||
// interface encapsulates the generic values / actions
|
||
// we need independent of arch. The concrete register types
|
||
// will be different depending on OS/Arch.
|
||
type Registers interface {
|
||
PC() uint64
|
||
SP() uint64
|
||
BP() uint64
|
||
CX() uint64
|
||
TLS() uint64
|
||
// GAddr returns the address of the G variable if it is known, 0 and false otherwise
|
||
GAddr() (uint64, bool)
|
||
Get(int) (uint64, error)
|
||
Slice() []Register
|
||
// Copy returns a copy of the registers that is guaranteed not to change
|
||
// when the registers of the associated thread change.
|
||
Copy() Registers
|
||
}
|
||
|
||
// Register represents a CPU register.
|
||
type Register struct {
|
||
Name string
|
||
Bytes []byte
|
||
Value string
|
||
}
|
||
|
||
// AppendWordReg appends a word (16 bit) register to regs.
|
||
func AppendWordReg(regs []Register, name string, value uint16) []Register {
|
||
var buf bytes.Buffer
|
||
binary.Write(&buf, binary.LittleEndian, value)
|
||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#04x", value)})
|
||
}
|
||
|
||
// AppendDwordReg appends a double word (32 bit) register to regs.
|
||
func AppendDwordReg(regs []Register, name string, value uint32) []Register {
|
||
var buf bytes.Buffer
|
||
binary.Write(&buf, binary.LittleEndian, value)
|
||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#08x", value)})
|
||
}
|
||
|
||
// AppendQwordReg appends a quad word (64 bit) register to regs.
|
||
func AppendQwordReg(regs []Register, name string, value uint64) []Register {
|
||
var buf bytes.Buffer
|
||
binary.Write(&buf, binary.LittleEndian, value)
|
||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#016x", value)})
|
||
}
|
||
|
||
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
|
||
var buf bytes.Buffer
|
||
binary.Write(&buf, binary.LittleEndian, value)
|
||
return append(regs, Register{name, buf.Bytes()[:size], descr.Describe(value, size)})
|
||
}
|
||
|
||
// AppendEflagReg appends EFLAG register to regs.
|
||
func AppendEflagReg(regs []Register, name string, value uint64) []Register {
|
||
return appendFlagReg(regs, name, value, eflagsDescription, 64)
|
||
}
|
||
|
||
// AppendMxcsrReg appends MXCSR register to regs.
|
||
func AppendMxcsrReg(regs []Register, name string, value uint64) []Register {
|
||
return appendFlagReg(regs, name, value, mxcsrDescription, 32)
|
||
}
|
||
|
||
// AppendX87Reg appends a 80 bit float register to regs.
|
||
func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register {
|
||
var f float64
|
||
fset := false
|
||
|
||
const (
|
||
_SIGNBIT = 1 << 15
|
||
_EXP_BIAS = (1 << 14) - 1 // 2^(n-1) - 1 = 16383
|
||
_SPECIALEXP = (1 << 15) - 1 // all bits set
|
||
_HIGHBIT = 1 << 63
|
||
_QUIETBIT = 1 << 62
|
||
)
|
||
|
||
sign := 1.0
|
||
if exponent&_SIGNBIT != 0 {
|
||
sign = -1.0
|
||
}
|
||
exponent &= ^uint16(_SIGNBIT)
|
||
|
||
NaN := math.NaN()
|
||
Inf := math.Inf(+1)
|
||
|
||
switch exponent {
|
||
case 0:
|
||
switch {
|
||
case mantissa == 0:
|
||
f = sign * 0.0
|
||
fset = true
|
||
case mantissa&_HIGHBIT != 0:
|
||
f = NaN
|
||
fset = true
|
||
}
|
||
case _SPECIALEXP:
|
||
switch {
|
||
case mantissa&_HIGHBIT == 0:
|
||
f = sign * Inf
|
||
fset = true
|
||
default:
|
||
f = NaN // signaling NaN
|
||
fset = true
|
||
}
|
||
default:
|
||
if mantissa&_HIGHBIT == 0 {
|
||
f = NaN
|
||
fset = true
|
||
}
|
||
}
|
||
|
||
if !fset {
|
||
significand := float64(mantissa) / (1 << 63)
|
||
f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS))
|
||
}
|
||
|
||
var buf bytes.Buffer
|
||
binary.Write(&buf, binary.LittleEndian, exponent)
|
||
binary.Write(&buf, binary.LittleEndian, mantissa)
|
||
|
||
return append(regs, Register{fmt.Sprintf("ST(%d)", index), buf.Bytes(), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
|
||
}
|
||
|
||
// AppendSSEReg appends a 256 bit SSE register to regs.
|
||
func AppendSSEReg(regs []Register, name string, xmm []byte) []Register {
|
||
buf := bytes.NewReader(xmm)
|
||
|
||
var out bytes.Buffer
|
||
var vi [16]uint8
|
||
for i := range vi {
|
||
binary.Read(buf, binary.LittleEndian, &vi[i])
|
||
}
|
||
|
||
fmt.Fprintf(&out, "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8], vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0])
|
||
|
||
fmt.Fprintf(&out, "\tv2_int={ %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x }", vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0], vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8])
|
||
|
||
fmt.Fprintf(&out, "\tv4_int={ %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x }", vi[3], vi[2], vi[1], vi[0], vi[7], vi[6], vi[5], vi[4], vi[11], vi[10], vi[9], vi[8], vi[15], vi[14], vi[13], vi[12])
|
||
|
||
fmt.Fprintf(&out, "\tv8_int={ %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x }", vi[1], vi[0], vi[3], vi[2], vi[5], vi[4], vi[7], vi[6], vi[9], vi[8], vi[11], vi[10], vi[13], vi[12], vi[15], vi[14])
|
||
|
||
fmt.Fprintf(&out, "\tv16_int={ %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x }", vi[0], vi[1], vi[2], vi[3], vi[4], vi[5], vi[6], vi[7], vi[8], vi[9], vi[10], vi[11], vi[12], vi[13], vi[14], vi[15])
|
||
|
||
buf.Seek(0, os.SEEK_SET)
|
||
var v2 [2]float64
|
||
for i := range v2 {
|
||
binary.Read(buf, binary.LittleEndian, &v2[i])
|
||
}
|
||
fmt.Fprintf(&out, "\tv2_float={ %g %g }", v2[0], v2[1])
|
||
|
||
buf.Seek(0, os.SEEK_SET)
|
||
var v4 [4]float32
|
||
for i := range v4 {
|
||
binary.Read(buf, binary.LittleEndian, &v4[i])
|
||
}
|
||
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
||
|
||
return append(regs, Register{name, xmm, out.String()})
|
||
}
|
||
|
||
// ErrUnknownRegister is returned when the value of an unknown
|
||
// register is requested.
|
||
var ErrUnknownRegister = errors.New("unknown register")
|
||
|
||
type flagRegisterDescr []flagDescr
|
||
type flagDescr struct {
|
||
name string
|
||
mask uint64
|
||
}
|
||
|
||
var mxcsrDescription flagRegisterDescr = []flagDescr{
|
||
{"FZ", 1 << 15},
|
||
{"RZ/RN", 1<<14 | 1<<13},
|
||
{"PM", 1 << 12},
|
||
{"UM", 1 << 11},
|
||
{"OM", 1 << 10},
|
||
{"ZM", 1 << 9},
|
||
{"DM", 1 << 8},
|
||
{"IM", 1 << 7},
|
||
{"DAZ", 1 << 6},
|
||
{"PE", 1 << 5},
|
||
{"UE", 1 << 4},
|
||
{"OE", 1 << 3},
|
||
{"ZE", 1 << 2},
|
||
{"DE", 1 << 1},
|
||
{"IE", 1 << 0},
|
||
}
|
||
|
||
var eflagsDescription flagRegisterDescr = []flagDescr{
|
||
{"CF", 1 << 0},
|
||
{"", 1 << 1},
|
||
{"PF", 1 << 2},
|
||
{"AF", 1 << 4},
|
||
{"ZF", 1 << 6},
|
||
{"SF", 1 << 7},
|
||
{"TF", 1 << 8},
|
||
{"IF", 1 << 9},
|
||
{"DF", 1 << 10},
|
||
{"OF", 1 << 11},
|
||
{"IOPL", 1<<12 | 1<<13},
|
||
{"NT", 1 << 14},
|
||
{"RF", 1 << 16},
|
||
{"VM", 1 << 17},
|
||
{"AC", 1 << 18},
|
||
{"VIF", 1 << 19},
|
||
{"VIP", 1 << 20},
|
||
{"ID", 1 << 21},
|
||
}
|
||
|
||
func (descr flagRegisterDescr) Mask() uint64 {
|
||
var r uint64
|
||
for _, f := range descr {
|
||
r = r | f.mask
|
||
}
|
||
return r
|
||
}
|
||
|
||
func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
|
||
var r []string
|
||
for _, f := range descr {
|
||
if f.name == "" {
|
||
continue
|
||
}
|
||
// rbm is f.mask with only the right-most bit set:
|
||
// 0001 1100 -> 0000 0100
|
||
rbm := f.mask & -f.mask
|
||
if rbm == f.mask {
|
||
if reg&f.mask != 0 {
|
||
r = append(r, f.name)
|
||
}
|
||
} else {
|
||
x := (reg & f.mask) >> uint64(math.Log2(float64(rbm)))
|
||
r = append(r, fmt.Sprintf("%s=%x", f.name, x))
|
||
}
|
||
}
|
||
if reg & ^descr.Mask() != 0 {
|
||
r = append(r, fmt.Sprintf("unknown_flags=%x", reg&^descr.Mask()))
|
||
}
|
||
return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
|
||
}
|
||
|
||
// PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
|
||
type PtraceFpRegs 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
|
||
}
|
||
|
||
// LinuxX86Xstate 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 LinuxX86Xstate struct {
|
||
PtraceFpRegs
|
||
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 *LinuxX86Xstate) Decode() (regs []Register) {
|
||
// x87 registers
|
||
regs = AppendWordReg(regs, "CW", xsave.Cwd)
|
||
regs = AppendWordReg(regs, "SW", xsave.Swd)
|
||
regs = AppendWordReg(regs, "TW", xsave.Ftw)
|
||
regs = AppendWordReg(regs, "FOP", xsave.Fop)
|
||
regs = AppendQwordReg(regs, "FIP", xsave.Rip)
|
||
regs = AppendQwordReg(regs, "FDP", xsave.Rdp)
|
||
|
||
for i := 0; i < len(xsave.StSpace); i += 4 {
|
||
regs = AppendX87Reg(regs, i/4, uint16(xsave.StSpace[i+2]), uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i]))
|
||
}
|
||
|
||
// SSE registers
|
||
regs = AppendMxcsrReg(regs, "MXCSR", uint64(xsave.Mxcsr))
|
||
regs = AppendDwordReg(regs, "MXCSR_MASK", xsave.MxcrMask)
|
||
|
||
for i := 0; i < len(xsave.XmmSpace); i += 16 {
|
||
regs = AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16])
|
||
if xsave.AvxState {
|
||
regs = AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), xsave.YmmSpace[i:i+16])
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
const (
|
||
_XSAVE_HEADER_START = 512
|
||
_XSAVE_HEADER_LEN = 64
|
||
_XSAVE_EXTENDED_REGION_START = 576
|
||
_XSAVE_SSE_REGION_LEN = 416
|
||
)
|
||
|
||
// 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 LinuxX86XstateRead(xstateargs []byte, readLegacy bool, regset *LinuxX86Xstate) 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.PtraceFpRegs); 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
|
||
}
|