From ea3428550d3ff55fd569640fc01e5aaefe37bc18 Mon Sep 17 00:00:00 2001 From: aarzilli Date: Wed, 17 Oct 2018 16:55:53 +0200 Subject: [PATCH] proc/native,proc/core: deduplicate linux register handling code The linux version of proc/native and proc/core contained largely overlapping implementations of the register handling code, deduplicate it by moving it into proc/linutil. --- pkg/proc/core/core.go | 68 +--- pkg/proc/core/linux_amd64_core.go | 236 +------------- pkg/proc/dwarf_expr_test.go | 24 +- pkg/proc/linutil/regs.go | 397 +++++++++++++++++++++++ pkg/proc/native/ptrace_linux.go | 8 +- pkg/proc/native/registers_linux_amd64.go | 289 +---------------- pkg/proc/native/threads_linux.go | 11 +- pkg/proc/registers.go | 96 ------ 8 files changed, 447 insertions(+), 682 deletions(-) create mode 100644 pkg/proc/linutil/regs.go diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go index 60043ee0..35577610 100644 --- a/pkg/proc/core/core.go +++ b/pkg/proc/core/core.go @@ -8,6 +8,7 @@ import ( "sync" "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/linutil" ) // A SplicedMemory represents a memory space formed from multiple regions, @@ -154,8 +155,8 @@ type Process struct { // Thread represents a thread in the core file being debugged. type Thread struct { + regs linutil.AMD64Registers th *LinuxPrStatus - fpregs []proc.Register p *Process common proc.CommonThread } @@ -273,11 +274,12 @@ func (t *Thread) ThreadID() int { // Registers returns the current value of the registers for this thread. func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) { - r := &Registers{&t.th.Reg, nil} + var r linutil.AMD64Registers + r.Regs = t.regs.Regs if floatingPoint { - r.fpregs = t.fpregs + r.Fpregs = t.regs.Fpregs } - return r, nil + return &r, nil } // RestoreRegisters will only return an error for core files, @@ -466,61 +468,3 @@ func (p *Process) FindThread(threadID int) (proc.Thread, bool) { t, ok := p.core.Threads[threadID] return t, ok } - -// Registers represents the CPU registers. -type Registers struct { - *LinuxCoreRegisters - fpregs []proc.Register -} - -// Slice will return a slice containing all registers and their values. -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 -} - -// Copy will return a copy of the registers that is guarenteed -// not to change. -func (r *Registers) Copy() proc.Registers { - return r -} diff --git a/pkg/proc/core/linux_amd64_core.go b/pkg/proc/core/linux_amd64_core.go index 482a8a67..1deac38e 100644 --- a/pkg/proc/core/linux_amd64_core.go +++ b/pkg/proc/core/linux_amd64_core.go @@ -8,44 +8,10 @@ import ( "io" "os" - "golang.org/x/arch/x86/x86asm" - "github.com/derekparker/delve/pkg/proc" "github.com/derekparker/delve/pkg/proc/linutil" ) -// Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on -// all systems. -type LinuxCoreRegisters struct { - R15 uint64 - R14 uint64 - R13 uint64 - R12 uint64 - Rbp uint64 - Rbx uint64 - R11 uint64 - R10 uint64 - R9 uint64 - R8 uint64 - Rax uint64 - Rcx uint64 - Rdx uint64 - Rsi uint64 - Rdi uint64 - Orig_rax uint64 - Rip uint64 - Cs uint64 - Eflags uint64 - Rsp uint64 - Ss uint64 - Fs_base uint64 - Gs_base uint64 - Ds uint64 - Es uint64 - Fs uint64 - Gs uint64 -} - // Copied from golang.org/x/sys/unix.Timeval since it's not available on all // systems. type LinuxCoreTimeval struct { @@ -62,198 +28,6 @@ const NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAV // NT_AUXV is the note type for notes containing a copy of the Auxv array const NT_AUXV elf.NType = 0x6 -// PC returns the value of RIP. -func (r *LinuxCoreRegisters) PC() uint64 { - return r.Rip -} - -// SP returns the value of RSP. -func (r *LinuxCoreRegisters) SP() uint64 { - return r.Rsp -} - -// BP returns the value of RBP. -func (r *LinuxCoreRegisters) BP() uint64 { - return r.Rbp -} - -// CX returns the value of RCX. -func (r *LinuxCoreRegisters) CX() uint64 { - return r.Rcx -} - -// TLS returns the location of the thread local storate, -// which will be the value of Fs_base. -func (r *LinuxCoreRegisters) TLS() uint64 { - return r.Fs_base -} - -// GAddr returns the address of the G struct. Always returns 0 -// and false for core files. -func (r *LinuxCoreRegisters) GAddr() (uint64, bool) { - return 0, false -} - -// Get returns the value of the register requested via the -// register number, returning an error if that register -// could not be found. -func (r *LinuxCoreRegisters) Get(n int) (uint64, error) { - reg := x86asm.Reg(n) - const ( - mask8 = 0x000f - mask16 = 0x00ff - mask32 = 0xffff - ) - - switch reg { - // 8-bit - case x86asm.AL: - return r.Rax & mask8, nil - case x86asm.CL: - return r.Rcx & mask8, nil - case x86asm.DL: - return r.Rdx & mask8, nil - case x86asm.BL: - return r.Rbx & mask8, nil - case x86asm.AH: - return (r.Rax >> 8) & mask8, nil - case x86asm.CH: - return (r.Rcx >> 8) & mask8, nil - case x86asm.DH: - return (r.Rdx >> 8) & mask8, nil - case x86asm.BH: - return (r.Rbx >> 8) & mask8, nil - case x86asm.SPB: - return r.Rsp & mask8, nil - case x86asm.BPB: - return r.Rbp & mask8, nil - case x86asm.SIB: - return r.Rsi & mask8, nil - case x86asm.DIB: - return r.Rdi & mask8, nil - case x86asm.R8B: - return r.R8 & mask8, nil - case x86asm.R9B: - return r.R9 & mask8, nil - case x86asm.R10B: - return r.R10 & mask8, nil - case x86asm.R11B: - return r.R11 & mask8, nil - case x86asm.R12B: - return r.R12 & mask8, nil - case x86asm.R13B: - return r.R13 & mask8, nil - case x86asm.R14B: - return r.R14 & mask8, nil - case x86asm.R15B: - return r.R15 & mask8, nil - - // 16-bit - case x86asm.AX: - return r.Rax & mask16, nil - case x86asm.CX: - return r.Rcx & mask16, nil - case x86asm.DX: - return r.Rdx & mask16, nil - case x86asm.BX: - return r.Rbx & mask16, nil - case x86asm.SP: - return r.Rsp & mask16, nil - case x86asm.BP: - return r.Rbp & mask16, nil - case x86asm.SI: - return r.Rsi & mask16, nil - case x86asm.DI: - return r.Rdi & mask16, nil - case x86asm.R8W: - return r.R8 & mask16, nil - case x86asm.R9W: - return r.R9 & mask16, nil - case x86asm.R10W: - return r.R10 & mask16, nil - case x86asm.R11W: - return r.R11 & mask16, nil - case x86asm.R12W: - return r.R12 & mask16, nil - case x86asm.R13W: - return r.R13 & mask16, nil - case x86asm.R14W: - return r.R14 & mask16, nil - case x86asm.R15W: - return r.R15 & mask16, nil - - // 32-bit - case x86asm.EAX: - return r.Rax & mask32, nil - case x86asm.ECX: - return r.Rcx & mask32, nil - case x86asm.EDX: - return r.Rdx & mask32, nil - case x86asm.EBX: - return r.Rbx & mask32, nil - case x86asm.ESP: - return r.Rsp & mask32, nil - case x86asm.EBP: - return r.Rbp & mask32, nil - case x86asm.ESI: - return r.Rsi & mask32, nil - case x86asm.EDI: - return r.Rdi & mask32, nil - case x86asm.R8L: - return r.R8 & mask32, nil - case x86asm.R9L: - return r.R9 & mask32, nil - case x86asm.R10L: - return r.R10 & mask32, nil - case x86asm.R11L: - return r.R11 & mask32, nil - case x86asm.R12L: - return r.R12 & mask32, nil - case x86asm.R13L: - return r.R13 & mask32, nil - case x86asm.R14L: - return r.R14 & mask32, nil - case x86asm.R15L: - return r.R15 & mask32, nil - - // 64-bit - case x86asm.RAX: - return r.Rax, nil - case x86asm.RCX: - return r.Rcx, nil - case x86asm.RDX: - return r.Rdx, nil - case x86asm.RBX: - return r.Rbx, nil - case x86asm.RSP: - return r.Rsp, nil - case x86asm.RBP: - return r.Rbp, nil - case x86asm.RSI: - return r.Rsi, nil - case x86asm.RDI: - return r.Rdi, nil - case x86asm.R8: - return r.R8, nil - case x86asm.R9: - return r.R9, nil - case x86asm.R10: - return r.R10, nil - case x86asm.R11: - return r.R11, nil - case x86asm.R12: - return r.R12, nil - case x86asm.R13: - return r.R13, nil - case x86asm.R14: - return r.R14, nil - case x86asm.R15: - return r.R15, nil - } - - return 0, proc.ErrUnknownRegister -} - // 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/, @@ -299,11 +73,11 @@ func readCore(corePath, exePath string) (*Core, error) { switch note.Type { case elf.NT_PRSTATUS: t := note.Desc.(*LinuxPrStatus) - lastThread = &Thread{t, nil, nil, proc.CommonThread{}} + lastThread = &Thread{linutil.AMD64Registers{Regs: &t.Reg}, t, nil, proc.CommonThread{}} core.Threads[int(t.Pid)] = lastThread case NT_X86_XSTATE: if lastThread != nil { - lastThread.fpregs = note.Desc.(*proc.LinuxX86Xstate).Decode() + lastThread.regs.Fpregs = note.Desc.(*linutil.AMD64Xstate).Decode() } case elf.NT_PRPSINFO: core.Pid = int(note.Desc.(*LinuxPrPsInfo).Pid) @@ -415,8 +189,8 @@ func readNote(r io.ReadSeeker) (*Note, error) { } note.Desc = data case NT_X86_XSTATE: - var fpregs proc.LinuxX86Xstate - if err := proc.LinuxX86XstateRead(desc, true, &fpregs); err != nil { + var fpregs linutil.AMD64Xstate + if err := linutil.AMD64XstateRead(desc, true, &fpregs); err != nil { return nil, err } note.Desc = &fpregs @@ -516,7 +290,7 @@ type LinuxPrStatus struct { Sighold uint64 Pid, Ppid, Pgrp, Sid int32 Utime, Stime, CUtime, CStime LinuxCoreTimeval - Reg LinuxCoreRegisters + Reg linutil.AMD64PtraceRegs Fpvalid int32 } diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go index e3a62150..6f02369d 100644 --- a/pkg/proc/dwarf_expr_test.go +++ b/pkg/proc/dwarf_expr_test.go @@ -16,7 +16,7 @@ import ( "github.com/derekparker/delve/pkg/dwarf/godwarf" "github.com/derekparker/delve/pkg/dwarf/op" "github.com/derekparker/delve/pkg/proc" - "github.com/derekparker/delve/pkg/proc/core" + "github.com/derekparker/delve/pkg/proc/linutil" ) const defaultCFA = 0xc420051d00 @@ -88,7 +88,7 @@ func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegist return scope } -func dwarfRegisters(regs *core.Registers) op.DwarfRegisters { +func dwarfRegisters(regs *linutil.AMD64Registers) op.DwarfRegisters { a := proc.AMD64Arch("linux") dwarfRegs := a.RegistersToDwarfRegisters(regs, 0) dwarfRegs.CFA = defaultCFA @@ -119,9 +119,9 @@ func TestDwarfExprRegisters(t *testing.T) { mainfn := bi.LookupFunc["main.main"] mem := newFakeMemory(defaultCFA, uint64(0), uint64(testCases["b"]), uint16(testCases["pair.v"])) - regs := core.Registers{LinuxCoreRegisters: &core.LinuxCoreRegisters{}} - regs.Rax = uint64(testCases["a"]) - regs.Rdx = uint64(testCases["c"]) + regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}} + regs.Regs.Rax = uint64(testCases["a"]) + regs.Regs.Rdx = uint64(testCases["c"]) dwarfExprCheck(t, mem, dwarfRegisters(®s), bi, testCases, mainfn) } @@ -174,12 +174,12 @@ func TestDwarfExprComposite(t *testing.T) { mainfn := bi.LookupFunc["main.main"] mem := newFakeMemory(defaultCFA, uint64(0), uint64(0), uint16(testCases["pair.v"]), []byte(stringVal)) - var regs core.Registers - regs.LinuxCoreRegisters = &core.LinuxCoreRegisters{} - regs.Rax = uint64(len(stringVal)) - regs.Rdx = defaultCFA + 18 - regs.Rcx = uint64(testCases["pair.k"]) - regs.Rbx = uint64(testCases["n"]) + var regs linutil.AMD64Registers + regs.Regs = &linutil.AMD64PtraceRegs{} + regs.Regs.Rax = uint64(len(stringVal)) + regs.Regs.Rdx = defaultCFA + 18 + regs.Regs.Rcx = uint64(testCases["pair.k"]) + regs.Regs.Rbx = uint64(testCases["n"]) scope := dwarfExprCheck(t, mem, dwarfRegisters(®s), bi, testCases, mainfn) @@ -214,7 +214,7 @@ func TestDwarfExprLoclist(t *testing.T) { mainfn := bi.LookupFunc["main.main"] mem := newFakeMemory(defaultCFA, uint16(before), uint16(after)) - regs := core.Registers{LinuxCoreRegisters: &core.LinuxCoreRegisters{}} + regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}} scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: mainfn}, Regs: dwarfRegisters(®s), Mem: mem, Gvar: nil, BinInfo: bi} diff --git a/pkg/proc/linutil/regs.go b/pkg/proc/linutil/regs.go new file mode 100644 index 00000000..34ec80d3 --- /dev/null +++ b/pkg/proc/linutil/regs.go @@ -0,0 +1,397 @@ +package linutil + +import ( + "bytes" + "encoding/binary" + "fmt" + + "golang.org/x/arch/x86/x86asm" + + "github.com/derekparker/delve/pkg/proc" +) + +// AMD64Registers implements the proc.Registers interface for the native/linux +// backend and core/linux backends, on AMD64. +type AMD64Registers struct { + Regs *AMD64PtraceRegs + Fpregs []proc.Register + Fpregset *AMD64Xstate +} + +// AMD64PtraceRegs is the struct used by the linux kernel to return the +// general purpose registers for AMD64 CPUs. +type AMD64PtraceRegs struct { + R15 uint64 + R14 uint64 + R13 uint64 + R12 uint64 + Rbp uint64 + Rbx uint64 + R11 uint64 + R10 uint64 + R9 uint64 + R8 uint64 + Rax uint64 + Rcx uint64 + Rdx uint64 + Rsi uint64 + Rdi uint64 + Orig_rax uint64 + Rip uint64 + Cs uint64 + Eflags uint64 + Rsp uint64 + Ss uint64 + Fs_base uint64 + Gs_base uint64 + Ds uint64 + Es uint64 + Fs uint64 + Gs uint64 +} + +// Slice returns the registers as a list of (name, value) pairs. +func (r *AMD64Registers) Slice() []proc.Register { + var regs = []struct { + k string + v uint64 + }{ + {"Rip", r.Regs.Rip}, + {"Rsp", r.Regs.Rsp}, + {"Rax", r.Regs.Rax}, + {"Rbx", r.Regs.Rbx}, + {"Rcx", r.Regs.Rcx}, + {"Rdx", r.Regs.Rdx}, + {"Rdi", r.Regs.Rdi}, + {"Rsi", r.Regs.Rsi}, + {"Rbp", r.Regs.Rbp}, + {"R8", r.Regs.R8}, + {"R9", r.Regs.R9}, + {"R10", r.Regs.R10}, + {"R11", r.Regs.R11}, + {"R12", r.Regs.R12}, + {"R13", r.Regs.R13}, + {"R14", r.Regs.R14}, + {"R15", r.Regs.R15}, + {"Orig_rax", r.Regs.Orig_rax}, + {"Cs", r.Regs.Cs}, + {"Eflags", r.Regs.Eflags}, + {"Ss", r.Regs.Ss}, + {"Fs_base", r.Regs.Fs_base}, + {"Gs_base", r.Regs.Gs_base}, + {"Ds", r.Regs.Ds}, + {"Es", r.Regs.Es}, + {"Fs", r.Regs.Fs}, + {"Gs", r.Regs.Gs}, + } + out := make([]proc.Register, 0, len(regs)+len(r.Fpregs)) + 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 +} + +// PC returns the value of RIP register. +func (r *AMD64Registers) PC() uint64 { + return r.Regs.Rip +} + +// SP returns the value of RSP register. +func (r *AMD64Registers) SP() uint64 { + return r.Regs.Rsp +} + +func (r *AMD64Registers) BP() uint64 { + return r.Regs.Rbp +} + +// CX returns the value of RCX register. +func (r *AMD64Registers) CX() uint64 { + return r.Regs.Rcx +} + +// TLS returns the address of the thread local storage memory segment. +func (r *AMD64Registers) TLS() uint64 { + return r.Regs.Fs_base +} + +// GAddr returns the address of the G variable if it is known, 0 and false +// otherwise. +func (r *AMD64Registers) GAddr() (uint64, bool) { + return 0, false +} + +// Get returns the value of the n-th register (in x86asm order). +func (r *AMD64Registers) Get(n int) (uint64, error) { + reg := x86asm.Reg(n) + const ( + mask8 = 0x000f + mask16 = 0x00ff + mask32 = 0xffff + ) + + switch reg { + // 8-bit + case x86asm.AL: + return r.Regs.Rax & mask8, nil + case x86asm.CL: + return r.Regs.Rcx & mask8, nil + case x86asm.DL: + return r.Regs.Rdx & mask8, nil + case x86asm.BL: + return r.Regs.Rbx & mask8, nil + case x86asm.AH: + return (r.Regs.Rax >> 8) & mask8, nil + case x86asm.CH: + return (r.Regs.Rcx >> 8) & mask8, nil + case x86asm.DH: + return (r.Regs.Rdx >> 8) & mask8, nil + case x86asm.BH: + return (r.Regs.Rbx >> 8) & mask8, nil + case x86asm.SPB: + return r.Regs.Rsp & mask8, nil + case x86asm.BPB: + return r.Regs.Rbp & mask8, nil + case x86asm.SIB: + return r.Regs.Rsi & mask8, nil + case x86asm.DIB: + return r.Regs.Rdi & mask8, nil + case x86asm.R8B: + return r.Regs.R8 & mask8, nil + case x86asm.R9B: + return r.Regs.R9 & mask8, nil + case x86asm.R10B: + return r.Regs.R10 & mask8, nil + case x86asm.R11B: + return r.Regs.R11 & mask8, nil + case x86asm.R12B: + return r.Regs.R12 & mask8, nil + case x86asm.R13B: + return r.Regs.R13 & mask8, nil + case x86asm.R14B: + return r.Regs.R14 & mask8, nil + case x86asm.R15B: + return r.Regs.R15 & mask8, nil + + // 16-bit + case x86asm.AX: + return r.Regs.Rax & mask16, nil + case x86asm.CX: + return r.Regs.Rcx & mask16, nil + case x86asm.DX: + return r.Regs.Rdx & mask16, nil + case x86asm.BX: + return r.Regs.Rbx & mask16, nil + case x86asm.SP: + return r.Regs.Rsp & mask16, nil + case x86asm.BP: + return r.Regs.Rbp & mask16, nil + case x86asm.SI: + return r.Regs.Rsi & mask16, nil + case x86asm.DI: + return r.Regs.Rdi & mask16, nil + case x86asm.R8W: + return r.Regs.R8 & mask16, nil + case x86asm.R9W: + return r.Regs.R9 & mask16, nil + case x86asm.R10W: + return r.Regs.R10 & mask16, nil + case x86asm.R11W: + return r.Regs.R11 & mask16, nil + case x86asm.R12W: + return r.Regs.R12 & mask16, nil + case x86asm.R13W: + return r.Regs.R13 & mask16, nil + case x86asm.R14W: + return r.Regs.R14 & mask16, nil + case x86asm.R15W: + return r.Regs.R15 & mask16, nil + + // 32-bit + case x86asm.EAX: + return r.Regs.Rax & mask32, nil + case x86asm.ECX: + return r.Regs.Rcx & mask32, nil + case x86asm.EDX: + return r.Regs.Rdx & mask32, nil + case x86asm.EBX: + return r.Regs.Rbx & mask32, nil + case x86asm.ESP: + return r.Regs.Rsp & mask32, nil + case x86asm.EBP: + return r.Regs.Rbp & mask32, nil + case x86asm.ESI: + return r.Regs.Rsi & mask32, nil + case x86asm.EDI: + return r.Regs.Rdi & mask32, nil + case x86asm.R8L: + return r.Regs.R8 & mask32, nil + case x86asm.R9L: + return r.Regs.R9 & mask32, nil + case x86asm.R10L: + return r.Regs.R10 & mask32, nil + case x86asm.R11L: + return r.Regs.R11 & mask32, nil + case x86asm.R12L: + return r.Regs.R12 & mask32, nil + case x86asm.R13L: + return r.Regs.R13 & mask32, nil + case x86asm.R14L: + return r.Regs.R14 & mask32, nil + case x86asm.R15L: + return r.Regs.R15 & mask32, nil + + // 64-bit + case x86asm.RAX: + return r.Regs.Rax, nil + case x86asm.RCX: + return r.Regs.Rcx, nil + case x86asm.RDX: + return r.Regs.Rdx, nil + case x86asm.RBX: + return r.Regs.Rbx, nil + case x86asm.RSP: + return r.Regs.Rsp, nil + case x86asm.RBP: + return r.Regs.Rbp, nil + case x86asm.RSI: + return r.Regs.Rsi, nil + case x86asm.RDI: + return r.Regs.Rdi, nil + case x86asm.R8: + return r.Regs.R8, nil + case x86asm.R9: + return r.Regs.R9, nil + case x86asm.R10: + return r.Regs.R10, nil + case x86asm.R11: + return r.Regs.R11, nil + case x86asm.R12: + return r.Regs.R12, nil + case x86asm.R13: + return r.Regs.R13, nil + case x86asm.R14: + return r.Regs.R14, nil + case x86asm.R15: + return r.Regs.R15, nil + } + + return 0, proc.ErrUnknownRegister +} + +// Copy returns a copy of these registers that is guarenteed not to change. +func (r *AMD64Registers) Copy() proc.Registers { + var rr AMD64Registers + rr.Regs = &AMD64PtraceRegs{} + rr.Fpregset = &AMD64Xstate{} + *(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 +} + +// 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 +} + +// 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 +} + +// Decode decodes an XSAVE area to a list of name/value pairs of registers. +func (xsave *AMD64Xstate) Decode() (regs []proc.Register) { + // x87 registers + regs = proc.AppendWordReg(regs, "CW", xsave.Cwd) + regs = proc.AppendWordReg(regs, "SW", xsave.Swd) + regs = proc.AppendWordReg(regs, "TW", xsave.Ftw) + regs = proc.AppendWordReg(regs, "FOP", xsave.Fop) + regs = proc.AppendQwordReg(regs, "FIP", xsave.Rip) + regs = proc.AppendQwordReg(regs, "FDP", xsave.Rdp) + + for i := 0; i < len(xsave.StSpace); i += 4 { + regs = proc.AppendX87Reg(regs, i/4, uint16(xsave.StSpace[i+2]), uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i])) + } + + // SSE registers + regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(xsave.Mxcsr)) + regs = proc.AppendDwordReg(regs, "MXCSR_MASK", xsave.MxcrMask) + + for i := 0; i < len(xsave.XmmSpace); i += 16 { + regs = proc.AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16]) + if xsave.AvxState { + regs = proc.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 AMD64XstateRead(xstateargs []byte, readLegacy bool, regset *AMD64Xstate) 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.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)]) + + return nil +} diff --git a/pkg/proc/native/ptrace_linux.go b/pkg/proc/native/ptrace_linux.go index c456987e..720d450d 100644 --- a/pkg/proc/native/ptrace_linux.go +++ b/pkg/proc/native/ptrace_linux.go @@ -6,7 +6,7 @@ import ( sys "golang.org/x/sys/unix" - "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/linutil" ) // PtraceAttach executes the sys.PtraceAttach call. @@ -57,8 +57,8 @@ 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 proc.LinuxX86Xstate, err error) { - _, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.PtraceFpRegs)), 0, 0) +func PtraceGetRegset(tid int) (regset linutil.AMD64Xstate, err error) { + _, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.AMD64PtraceFpRegs)), 0, 0) if err == syscall.Errno(0) || err == syscall.ENODEV { // ignore ENODEV, it just means this CPU doesn't have X87 registers (??) err = nil @@ -78,6 +78,6 @@ func PtraceGetRegset(tid int) (regset proc.LinuxX86Xstate, err error) { } regset.Xsave = xstateargs[:iov.Len] - err = proc.LinuxX86XstateRead(regset.Xsave, false, ®set) + err = linutil.AMD64XstateRead(regset.Xsave, false, ®set) return regset, err } diff --git a/pkg/proc/native/registers_linux_amd64.go b/pkg/proc/native/registers_linux_amd64.go index eb3715a2..b30223ad 100644 --- a/pkg/proc/native/registers_linux_amd64.go +++ b/pkg/proc/native/registers_linux_amd64.go @@ -3,102 +3,21 @@ package native import ( "fmt" - "golang.org/x/arch/x86/x86asm" sys "golang.org/x/sys/unix" "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/linutil" ) -// Regs is a wrapper for sys.PtraceRegs. -type Regs struct { - regs *sys.PtraceRegs - fpregs []proc.Register - fpregset *proc.LinuxX86Xstate -} - -func (r *Regs) Slice() []proc.Register { - var regs = []struct { - k string - v uint64 - }{ - {"Rip", r.regs.Rip}, - {"Rsp", r.regs.Rsp}, - {"Rax", r.regs.Rax}, - {"Rbx", r.regs.Rbx}, - {"Rcx", r.regs.Rcx}, - {"Rdx", r.regs.Rdx}, - {"Rdi", r.regs.Rdi}, - {"Rsi", r.regs.Rsi}, - {"Rbp", r.regs.Rbp}, - {"R8", r.regs.R8}, - {"R9", r.regs.R9}, - {"R10", r.regs.R10}, - {"R11", r.regs.R11}, - {"R12", r.regs.R12}, - {"R13", r.regs.R13}, - {"R14", r.regs.R14}, - {"R15", r.regs.R15}, - {"Orig_rax", r.regs.Orig_rax}, - {"Cs", r.regs.Cs}, - {"Eflags", r.regs.Eflags}, - {"Ss", r.regs.Ss}, - {"Fs_base", r.regs.Fs_base}, - {"Gs_base", r.regs.Gs_base}, - {"Ds", r.regs.Ds}, - {"Es", r.regs.Es}, - {"Fs", r.regs.Fs}, - {"Gs", r.regs.Gs}, - } - out := make([]proc.Register, 0, len(regs)+len(r.fpregs)) - 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 -} - -// PC returns the value of RIP register. -func (r *Regs) PC() uint64 { - return r.regs.PC() -} - -// SP returns the value of RSP register. -func (r *Regs) SP() uint64 { - return r.regs.Rsp -} - -func (r *Regs) BP() uint64 { - return r.regs.Rbp -} - -// CX returns the value of RCX register. -func (r *Regs) CX() uint64 { - return r.regs.Rcx -} - -// TLS returns the address of the thread -// local storage memory segment. -func (r *Regs) TLS() uint64 { - return r.regs.Fs_base -} - -func (r *Regs) GAddr() (uint64, bool) { - return 0, false -} - // SetPC sets RIP to the value specified by 'pc'. func (thread *Thread) SetPC(pc uint64) error { ir, err := registers(thread, false) if err != nil { return err } - r := ir.(*Regs) - r.regs.SetPC(pc) - thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) }) + r := ir.(*linutil.AMD64Registers) + r.Regs.Rip = pc + thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) }) return err } @@ -109,9 +28,9 @@ func (thread *Thread) SetSP(sp uint64) (err error) { if err != nil { return err } - r := ir.(*Regs) - r.regs.Rsp = sp - thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) }) + r := ir.(*linutil.AMD64Registers) + r.Regs.Rsp = sp + thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) }) return } @@ -121,183 +40,26 @@ func (thread *Thread) SetDX(dx uint64) (err error) { if err != nil { return err } - r := ir.(*Regs) - r.regs.Rdx = dx - thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) }) + r := ir.(*linutil.AMD64Registers) + r.Regs.Rdx = dx + thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) }) return } -func (r *Regs) Get(n int) (uint64, error) { - reg := x86asm.Reg(n) - const ( - mask8 = 0x000f - mask16 = 0x00ff - mask32 = 0xffff - ) - - switch reg { - // 8-bit - case x86asm.AL: - return r.regs.Rax & mask8, nil - case x86asm.CL: - return r.regs.Rcx & mask8, nil - case x86asm.DL: - return r.regs.Rdx & mask8, nil - case x86asm.BL: - return r.regs.Rbx & mask8, nil - case x86asm.AH: - return (r.regs.Rax >> 8) & mask8, nil - case x86asm.CH: - return (r.regs.Rcx >> 8) & mask8, nil - case x86asm.DH: - return (r.regs.Rdx >> 8) & mask8, nil - case x86asm.BH: - return (r.regs.Rbx >> 8) & mask8, nil - case x86asm.SPB: - return r.regs.Rsp & mask8, nil - case x86asm.BPB: - return r.regs.Rbp & mask8, nil - case x86asm.SIB: - return r.regs.Rsi & mask8, nil - case x86asm.DIB: - return r.regs.Rdi & mask8, nil - case x86asm.R8B: - return r.regs.R8 & mask8, nil - case x86asm.R9B: - return r.regs.R9 & mask8, nil - case x86asm.R10B: - return r.regs.R10 & mask8, nil - case x86asm.R11B: - return r.regs.R11 & mask8, nil - case x86asm.R12B: - return r.regs.R12 & mask8, nil - case x86asm.R13B: - return r.regs.R13 & mask8, nil - case x86asm.R14B: - return r.regs.R14 & mask8, nil - case x86asm.R15B: - return r.regs.R15 & mask8, nil - - // 16-bit - case x86asm.AX: - return r.regs.Rax & mask16, nil - case x86asm.CX: - return r.regs.Rcx & mask16, nil - case x86asm.DX: - return r.regs.Rdx & mask16, nil - case x86asm.BX: - return r.regs.Rbx & mask16, nil - case x86asm.SP: - return r.regs.Rsp & mask16, nil - case x86asm.BP: - return r.regs.Rbp & mask16, nil - case x86asm.SI: - return r.regs.Rsi & mask16, nil - case x86asm.DI: - return r.regs.Rdi & mask16, nil - case x86asm.R8W: - return r.regs.R8 & mask16, nil - case x86asm.R9W: - return r.regs.R9 & mask16, nil - case x86asm.R10W: - return r.regs.R10 & mask16, nil - case x86asm.R11W: - return r.regs.R11 & mask16, nil - case x86asm.R12W: - return r.regs.R12 & mask16, nil - case x86asm.R13W: - return r.regs.R13 & mask16, nil - case x86asm.R14W: - return r.regs.R14 & mask16, nil - case x86asm.R15W: - return r.regs.R15 & mask16, nil - - // 32-bit - case x86asm.EAX: - return r.regs.Rax & mask32, nil - case x86asm.ECX: - return r.regs.Rcx & mask32, nil - case x86asm.EDX: - return r.regs.Rdx & mask32, nil - case x86asm.EBX: - return r.regs.Rbx & mask32, nil - case x86asm.ESP: - return r.regs.Rsp & mask32, nil - case x86asm.EBP: - return r.regs.Rbp & mask32, nil - case x86asm.ESI: - return r.regs.Rsi & mask32, nil - case x86asm.EDI: - return r.regs.Rdi & mask32, nil - case x86asm.R8L: - return r.regs.R8 & mask32, nil - case x86asm.R9L: - return r.regs.R9 & mask32, nil - case x86asm.R10L: - return r.regs.R10 & mask32, nil - case x86asm.R11L: - return r.regs.R11 & mask32, nil - case x86asm.R12L: - return r.regs.R12 & mask32, nil - case x86asm.R13L: - return r.regs.R13 & mask32, nil - case x86asm.R14L: - return r.regs.R14 & mask32, nil - case x86asm.R15L: - return r.regs.R15 & mask32, nil - - // 64-bit - case x86asm.RAX: - return r.regs.Rax, nil - case x86asm.RCX: - return r.regs.Rcx, nil - case x86asm.RDX: - return r.regs.Rdx, nil - case x86asm.RBX: - return r.regs.Rbx, nil - case x86asm.RSP: - return r.regs.Rsp, nil - case x86asm.RBP: - return r.regs.Rbp, nil - case x86asm.RSI: - return r.regs.Rsi, nil - case x86asm.RDI: - return r.regs.Rdi, nil - case x86asm.R8: - return r.regs.R8, nil - case x86asm.R9: - return r.regs.R9, nil - case x86asm.R10: - return r.regs.R10, nil - case x86asm.R11: - return r.regs.R11, nil - case x86asm.R12: - return r.regs.R12, nil - case x86asm.R13: - return r.regs.R13, nil - case x86asm.R14: - return r.regs.R14, nil - case x86asm.R15: - return r.regs.R15, nil - } - - return 0, proc.ErrUnknownRegister -} - func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) { var ( - regs sys.PtraceRegs + regs linutil.AMD64PtraceRegs err error ) - thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, ®s) }) + thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, (*sys.PtraceRegs)(®s)) }) if err != nil { return nil, err } - r := &Regs{®s, nil, nil} + r := &linutil.AMD64Registers{®s, nil, nil} if floatingPoint { - var fpregset proc.LinuxX86Xstate - r.fpregs, fpregset, err = thread.fpRegisters() - r.fpregset = &fpregset + var fpregset linutil.AMD64Xstate + r.Fpregs, fpregset, err = thread.fpRegisters() + r.Fpregset = &fpregset if err != nil { return nil, err } @@ -315,7 +77,7 @@ const ( _XSAVE_SSE_REGION_LEN = 416 ) -func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs proc.LinuxX86Xstate, err error) { +func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs linutil.AMD64Xstate, err error) { thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) }) regs = fpregs.Decode() if err != nil { @@ -323,20 +85,3 @@ func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs proc.LinuxX86X } return } - -// Copy returns a copy of these registers that is -// guarenteed not to change. -func (r *Regs) Copy() proc.Registers { - var rr Regs - rr.regs = &sys.PtraceRegs{} - rr.fpregset = &proc.LinuxX86Xstate{} - *(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 -} diff --git a/pkg/proc/native/threads_linux.go b/pkg/proc/native/threads_linux.go index 6e0da76f..f545b12a 100644 --- a/pkg/proc/native/threads_linux.go +++ b/pkg/proc/native/threads_linux.go @@ -8,6 +8,7 @@ import ( sys "golang.org/x/sys/unix" "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/linutil" ) type WaitStatus sys.WaitStatus @@ -83,21 +84,21 @@ func (t *Thread) Blocked() bool { } func (t *Thread) restoreRegisters(savedRegs proc.Registers) error { - sr := savedRegs.(*Regs) + sr := savedRegs.(*linutil.AMD64Registers) var restoreRegistersErr error t.dbp.execPtraceFunc(func() { - restoreRegistersErr = sys.PtraceSetRegs(t.ID, sr.regs) + restoreRegistersErr = sys.PtraceSetRegs(t.ID, (*sys.PtraceRegs)(sr.Regs)) if restoreRegistersErr != nil { return } - if sr.fpregset.Xsave != nil { - iov := sys.Iovec{Base: &sr.fpregset.Xsave[0], Len: uint64(len(sr.fpregset.Xsave))} + if sr.Fpregset.Xsave != nil { + iov := sys.Iovec{Base: &sr.Fpregset.Xsave[0], Len: uint64(len(sr.Fpregset.Xsave))} _, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0) return } - _, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.fpregset.PtraceFpRegs)), 0, 0) + _, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.Fpregset.AMD64PtraceFpRegs)), 0, 0) return }) if restoreRegistersErr == syscall.Errno(0) { diff --git a/pkg/proc/registers.go b/pkg/proc/registers.go index 1826762e..17ee1323 100644 --- a/pkg/proc/registers.go +++ b/pkg/proc/registers.go @@ -250,99 +250,3 @@ func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string { } 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 -}