mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 01:27:16 +08:00
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.
This commit is contained in:
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
|
||||
397
pkg/proc/linutil/regs.go
Normal file
397
pkg/proc/linutil/regs.go
Normal file
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user