parent
809bdca191
commit
d4364d0496
@ -1,6 +1,9 @@
|
||||
package main
|
||||
|
||||
import "runtime"
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func fputestsetup(f64a, f64b, f64c, f64d float64, f32a, f32b, f32c, f32d float32)
|
||||
|
||||
@ -15,5 +18,9 @@ func main() {
|
||||
var f32d float32 = 1.8
|
||||
|
||||
fputestsetup(f64a, f64b, f64c, f64d, f32a, f32b, f32c, f32d)
|
||||
runtime.Breakpoint()
|
||||
if len(os.Args) < 2 || os.Args[1] != "panic" {
|
||||
runtime.Breakpoint()
|
||||
} else {
|
||||
panic("booom!")
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,14 +145,15 @@ type Process struct {
|
||||
bi proc.BinaryInfo
|
||||
core *Core
|
||||
breakpoints map[uint64]*proc.Breakpoint
|
||||
currentThread *LinuxPrStatus
|
||||
currentThread *Thread
|
||||
selectedGoroutine *proc.G
|
||||
allGCache []*proc.G
|
||||
}
|
||||
|
||||
type Thread struct {
|
||||
th *LinuxPrStatus
|
||||
p *Process
|
||||
th *LinuxPrStatus
|
||||
fpregs []proc.Register
|
||||
p *Process
|
||||
}
|
||||
|
||||
var ErrWriteCore = errors.New("can not to core process")
|
||||
@ -169,6 +170,9 @@ func OpenCore(corePath, exePath string) (*Process, error) {
|
||||
breakpoints: make(map[uint64]*proc.Breakpoint),
|
||||
bi: proc.NewBinaryInfo("linux", "amd64"),
|
||||
}
|
||||
for _, thread := range core.Threads {
|
||||
thread.p = p
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
p.bi.LoadBinaryInfo(exePath, &wg)
|
||||
@ -221,8 +225,11 @@ func (t *Thread) ThreadID() int {
|
||||
}
|
||||
|
||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
//TODO(aarzilli): handle floating point registers
|
||||
return &t.th.Reg, nil
|
||||
r := &Registers{&t.th.Reg, nil}
|
||||
if floatingPoint {
|
||||
r.fpregs = t.fpregs
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (t *Thread) Arch() proc.Arch {
|
||||
@ -274,7 +281,7 @@ func (p *Process) ManualStopRequested() bool {
|
||||
}
|
||||
|
||||
func (p *Process) CurrentThread() proc.Thread {
|
||||
return &Thread{p.currentThread, p}
|
||||
return p.currentThread
|
||||
}
|
||||
|
||||
func (p *Process) Detach(bool) error {
|
||||
@ -340,12 +347,62 @@ func (p *Process) SwitchThread(tid int) error {
|
||||
func (p *Process) ThreadList() []proc.Thread {
|
||||
r := make([]proc.Thread, 0, len(p.core.Threads))
|
||||
for _, v := range p.core.Threads {
|
||||
r = append(r, &Thread{v, p})
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
|
||||
t, ok := p.core.Threads[threadID]
|
||||
return &Thread{t, p}, ok
|
||||
return t, ok
|
||||
}
|
||||
|
||||
type Registers struct {
|
||||
*LinuxCoreRegisters
|
||||
fpregs []proc.Register
|
||||
}
|
||||
|
||||
func (r *Registers) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.Rip},
|
||||
{"Rsp", r.Rsp},
|
||||
{"Rax", r.Rax},
|
||||
{"Rbx", r.Rbx},
|
||||
{"Rcx", r.Rcx},
|
||||
{"Rdx", r.Rdx},
|
||||
{"Rdi", r.Rdi},
|
||||
{"Rsi", r.Rsi},
|
||||
{"Rbp", r.Rbp},
|
||||
{"R8", r.R8},
|
||||
{"R9", r.R9},
|
||||
{"R10", r.R10},
|
||||
{"R11", r.R11},
|
||||
{"R12", r.R12},
|
||||
{"R13", r.R13},
|
||||
{"R14", r.R14},
|
||||
{"R15", r.R15},
|
||||
{"Orig_rax", r.Orig_rax},
|
||||
{"Cs", r.Cs},
|
||||
{"Eflags", r.Eflags},
|
||||
{"Ss", r.Ss},
|
||||
{"Fs_base", r.Fs_base},
|
||||
{"Gs_base", r.Gs_base},
|
||||
{"Ds", r.Ds},
|
||||
{"Es", r.Es},
|
||||
{"Fs", r.Fs},
|
||||
{"Gs", r.Gs},
|
||||
}
|
||||
out := make([]proc.Register, 0, len(regs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
@ -125,26 +125,23 @@ func TestSplicedReader(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore(t *testing.T) {
|
||||
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
||||
return
|
||||
}
|
||||
func withCoreFile(t *testing.T, name, args string) *Process {
|
||||
// This is all very fragile and won't work on hosts with non-default core patterns.
|
||||
// Might be better to check in the binary and core?
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fix := test.BuildFixture("panic")
|
||||
bashCmd := fmt.Sprintf("cd %v && ulimit -c unlimited && GOTRACEBACK=crash %v", tempDir, fix.Path)
|
||||
fix := test.BuildFixture(name)
|
||||
bashCmd := fmt.Sprintf("cd %v && ulimit -c unlimited && GOTRACEBACK=crash %v %s", tempDir, fix.Path, args)
|
||||
exec.Command("bash", "-c", bashCmd).Run()
|
||||
cores, err := filepath.Glob(path.Join(tempDir, "core*"))
|
||||
switch {
|
||||
case err != nil || len(cores) > 1:
|
||||
t.Fatalf("Got %v, wanted one file named core* in %v", cores, tempDir)
|
||||
case len(cores) == 0:
|
||||
t.Logf("core file was not produced, could not run test")
|
||||
return
|
||||
t.Skipf("core file was not produced, could not run test")
|
||||
return nil
|
||||
}
|
||||
corePath := cores[0]
|
||||
|
||||
@ -156,6 +153,15 @@ func TestCore(t *testing.T) {
|
||||
t.Errorf("read apport log: %q, %v", apport, err)
|
||||
t.Fatalf("ReadCore() failed: %v", err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func TestCore(t *testing.T) {
|
||||
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
||||
return
|
||||
}
|
||||
p := withCoreFile(t, "panic", "")
|
||||
|
||||
gs, err := proc.GoroutinesInfo(p)
|
||||
if err != nil || len(gs) == 0 {
|
||||
t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err)
|
||||
@ -207,3 +213,80 @@ func TestCore(t *testing.T) {
|
||||
t.Logf("%s = %s", reg.Name, reg.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoreFpRegisters(t *testing.T) {
|
||||
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
||||
return
|
||||
}
|
||||
p := withCoreFile(t, "fputest/", "panic")
|
||||
|
||||
gs, err := proc.GoroutinesInfo(p)
|
||||
if err != nil || len(gs) == 0 {
|
||||
t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err)
|
||||
}
|
||||
|
||||
var regs proc.Registers
|
||||
for _, thread := range p.ThreadList() {
|
||||
frames, err := proc.ThreadStacktrace(thread, 10)
|
||||
if err != nil {
|
||||
t.Errorf("ThreadStacktrace for %x = %v", thread.ThreadID(), err)
|
||||
continue
|
||||
}
|
||||
for i := range frames {
|
||||
if frames[i].Current.Fn == nil {
|
||||
continue
|
||||
}
|
||||
if frames[i].Current.Fn.Name == "runtime.crash" {
|
||||
regs, err = thread.Registers(true)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get registers for thread %x, %v", thread.ThreadID(), err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if regs != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
regtests := []struct{ name, value string }{
|
||||
{"ST(0)", "0x3fffe666660000000000"},
|
||||
{"ST(1)", "0x3fffd9999a0000000000"},
|
||||
{"ST(2)", "0x3fffcccccd0000000000"},
|
||||
{"ST(3)", "0x3fffc000000000000000"},
|
||||
{"ST(4)", "0x3fffb333333333333000"},
|
||||
{"ST(5)", "0x3fffa666666666666800"},
|
||||
{"ST(6)", "0x3fff9999999999999800"},
|
||||
{"ST(7)", "0x3fff8cccccccccccd000"},
|
||||
// Unlike TestClientServer_FpRegisters in service/test/integration2_test
|
||||
// we can not test the value of XMM0, it probably has been reused by
|
||||
// something between the panic and the time we get the core dump.
|
||||
{"XMM1", "0x3ff66666666666663ff4cccccccccccd"},
|
||||
{"XMM2", "0x3fe666663fd9999a3fcccccd3fc00000"},
|
||||
{"XMM3", "0x3ff199999999999a3ff3333333333333"},
|
||||
{"XMM4", "0x3ff4cccccccccccd3ff6666666666666"},
|
||||
{"XMM5", "0x3fcccccd3fc000003fe666663fd9999a"},
|
||||
{"XMM6", "0x4004cccccccccccc4003333333333334"},
|
||||
{"XMM7", "0x40026666666666664002666666666666"},
|
||||
{"XMM8", "0x4059999a404ccccd4059999a404ccccd"},
|
||||
}
|
||||
|
||||
for _, reg := range regs.Slice() {
|
||||
t.Logf("%s = %s", reg.Name, reg.Value)
|
||||
}
|
||||
|
||||
for _, regtest := range regtests {
|
||||
found := false
|
||||
for _, reg := range regs.Slice() {
|
||||
if reg.Name == regtest.name {
|
||||
found = true
|
||||
if !strings.HasPrefix(reg.Value, regtest.value) {
|
||||
t.Fatalf("register %s expected %q got %q", reg.Name, regtest.value, reg.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("register %s not found: %v", regtest.name, regs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +54,8 @@ type LinuxCoreTimeval struct {
|
||||
Usec int64
|
||||
}
|
||||
|
||||
const NT_FILE elf.NType = 0x46494c45 // "FILE".
|
||||
const NT_FILE elf.NType = 0x46494c45 // "FILE".
|
||||
const NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAVE area.
|
||||
|
||||
func (r *LinuxCoreRegisters) PC() uint64 {
|
||||
return r.Rip
|
||||
@ -241,50 +242,6 @@ func (r *LinuxCoreRegisters) SetPC(proc.Thread, uint64) error {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.Rip},
|
||||
{"Rsp", r.Rsp},
|
||||
{"Rax", r.Rax},
|
||||
{"Rbx", r.Rbx},
|
||||
{"Rcx", r.Rcx},
|
||||
{"Rdx", r.Rdx},
|
||||
{"Rdi", r.Rdi},
|
||||
{"Rsi", r.Rsi},
|
||||
{"Rbp", r.Rbp},
|
||||
{"R8", r.R8},
|
||||
{"R9", r.R9},
|
||||
{"R10", r.R10},
|
||||
{"R11", r.R11},
|
||||
{"R12", r.R12},
|
||||
{"R13", r.R13},
|
||||
{"R14", r.R14},
|
||||
{"R15", r.R15},
|
||||
{"Orig_rax", r.Orig_rax},
|
||||
{"Cs", r.Cs},
|
||||
{"Eflags", r.Eflags},
|
||||
{"Ss", r.Ss},
|
||||
{"Fs_base", r.Fs_base},
|
||||
{"Gs_base", r.Gs_base},
|
||||
{"Ds", r.Ds},
|
||||
{"Es", r.Es},
|
||||
{"Fs", r.Fs},
|
||||
{"Gs", r.Gs},
|
||||
}
|
||||
out := make([]proc.Register, 0, len(regs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// readCore reads a core file from corePath corresponding to the executable at
|
||||
// exePath. For details on the Linux ELF core format, see:
|
||||
// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
|
||||
@ -292,7 +249,7 @@ func (r *LinuxCoreRegisters) Slice() []proc.Register {
|
||||
// elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c,
|
||||
// and, if absolutely desperate, readelf.c from the binutils source.
|
||||
func readCore(corePath, exePath string) (*Core, error) {
|
||||
core, err := elf.Open(corePath)
|
||||
coreFile, err := elf.Open(corePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -301,37 +258,42 @@ func readCore(corePath, exePath string) (*Core, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if core.Type != elf.ET_CORE {
|
||||
return nil, fmt.Errorf("%v is not a core file", core)
|
||||
if coreFile.Type != elf.ET_CORE {
|
||||
return nil, fmt.Errorf("%v is not a core file", coreFile)
|
||||
}
|
||||
|
||||
notes, err := readNotes(core)
|
||||
notes, err := readNotes(coreFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
memory := buildMemory(core, exe, notes)
|
||||
memory := buildMemory(coreFile, exe, notes)
|
||||
|
||||
threads := map[int]*LinuxPrStatus{}
|
||||
pid := 0
|
||||
core := &Core{
|
||||
MemoryReader: memory,
|
||||
Threads: map[int]*Thread{},
|
||||
}
|
||||
|
||||
var lastThread *Thread
|
||||
for _, note := range notes {
|
||||
switch note.Type {
|
||||
case elf.NT_PRSTATUS:
|
||||
t := note.Desc.(*LinuxPrStatus)
|
||||
threads[int(t.Pid)] = t
|
||||
lastThread = &Thread{t, nil, nil}
|
||||
core.Threads[int(t.Pid)] = lastThread
|
||||
case NT_X86_XSTATE:
|
||||
if lastThread != nil {
|
||||
lastThread.fpregs = note.Desc.(*proc.LinuxX86Xstate).Decode()
|
||||
}
|
||||
case elf.NT_PRPSINFO:
|
||||
pid = int(note.Desc.(*LinuxPrPsInfo).Pid)
|
||||
core.Pid = int(note.Desc.(*LinuxPrPsInfo).Pid)
|
||||
}
|
||||
}
|
||||
return &Core{
|
||||
MemoryReader: memory,
|
||||
Threads: threads,
|
||||
Pid: pid,
|
||||
}, nil
|
||||
return core, nil
|
||||
}
|
||||
|
||||
type Core struct {
|
||||
proc.MemoryReader
|
||||
Threads map[int]*LinuxPrStatus
|
||||
Threads map[int]*Thread
|
||||
Pid int
|
||||
}
|
||||
|
||||
@ -341,7 +303,7 @@ type Core struct {
|
||||
// - NT_PRPSINFO: Information about a process, including PID and signal. Desc is a LinuxPrPsInfo.
|
||||
// - NT_PRSTATUS: Information about a thread, including base registers, state, etc. Desc is a LinuxPrStatus.
|
||||
// - NT_FPREGSET (Not implemented): x87 floating point registers.
|
||||
// - NT_X86_XSTATE (Not implemented): Other registers, including AVX and such.
|
||||
// - NT_X86_XSTATE: Other registers, including AVX and such.
|
||||
type Note struct {
|
||||
Type elf.NType
|
||||
Name string
|
||||
@ -428,6 +390,12 @@ func readNote(r io.ReadSeeker) (*Note, error) {
|
||||
data.entries = append(data.entries, entry)
|
||||
}
|
||||
note.Desc = data
|
||||
case NT_X86_XSTATE:
|
||||
var fpregs proc.LinuxX86Xstate
|
||||
if err := proc.LinuxX86XstateRead(desc, true, &fpregs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
note.Desc = &fpregs
|
||||
}
|
||||
if err := skipPadding(r, 4); err != nil {
|
||||
return nil, fmt.Errorf("aligning after desc: %v", err)
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// PtraceAttach executes the sys.PtraceAttach call.
|
||||
@ -56,7 +57,7 @@ func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
|
||||
// See amd64_linux_fetch_inferior_registers in gdb/amd64-linux-nat.c.html
|
||||
// and amd64_supply_xsave in gdb/amd64-tdep.c.html
|
||||
// and Section 13.1 (and following) of Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture
|
||||
func PtraceGetRegset(tid int) (regset PtraceXsave, err error) {
|
||||
func PtraceGetRegset(tid int) (regset proc.LinuxX86Xstate, err error) {
|
||||
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.PtraceFpRegs)), 0, 0)
|
||||
if err == syscall.Errno(0) {
|
||||
err = nil
|
||||
@ -71,26 +72,6 @@ func PtraceGetRegset(tid int) (regset PtraceXsave, err error) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= iov.Len {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if xstate_bv&(1<<2) == 0 {
|
||||
// AVX state not present
|
||||
return
|
||||
}
|
||||
|
||||
avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:iov.Len]
|
||||
regset.AvxState = true
|
||||
copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
|
||||
|
||||
return
|
||||
err = proc.LinuxX86XstateRead(xstateargs[:iov.Len], false, ®set)
|
||||
return regset, err
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
@ -273,27 +271,6 @@ func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
type PtraceXsave struct {
|
||||
PtraceFpRegs
|
||||
AvxState bool // contains AVX state
|
||||
YmmSpace [256]byte
|
||||
}
|
||||
|
||||
const (
|
||||
_X86_XSTATE_MAX_SIZE = 2688
|
||||
_NT_X86_XSTATE = 0x202
|
||||
@ -305,31 +282,8 @@ const (
|
||||
)
|
||||
|
||||
func (thread *Thread) fpRegisters() (regs []proc.Register, err error) {
|
||||
var fpregs PtraceXsave
|
||||
var fpregs proc.LinuxX86Xstate
|
||||
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
|
||||
|
||||
// x87 registers
|
||||
regs = proc.AppendWordReg(regs, "CW", fpregs.Cwd)
|
||||
regs = proc.AppendWordReg(regs, "SW", fpregs.Swd)
|
||||
regs = proc.AppendWordReg(regs, "TW", fpregs.Ftw)
|
||||
regs = proc.AppendWordReg(regs, "FOP", fpregs.Fop)
|
||||
regs = proc.AppendQwordReg(regs, "FIP", fpregs.Rip)
|
||||
regs = proc.AppendQwordReg(regs, "FDP", fpregs.Rdp)
|
||||
|
||||
for i := 0; i < len(fpregs.StSpace); i += 4 {
|
||||
regs = proc.AppendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
|
||||
}
|
||||
|
||||
// SSE registers
|
||||
regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(fpregs.Mxcsr))
|
||||
regs = proc.AppendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
|
||||
|
||||
for i := 0; i < len(fpregs.XmmSpace); i += 16 {
|
||||
regs = proc.AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
|
||||
if fpregs.AvxState {
|
||||
regs = proc.AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
|
||||
}
|
||||
}
|
||||
|
||||
regs = fpregs.Decode()
|
||||
return
|
||||
}
|
||||
|
||||
@ -232,3 +232,98 @@ func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
|
||||
}
|
||||
return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user