diff --git a/pkg/proc/fbsdutil/regs.go b/pkg/proc/fbsdutil/regs.go index e8cbddf3..0ca24b1e 100644 --- a/pkg/proc/fbsdutil/regs.go +++ b/pkg/proc/fbsdutil/regs.go @@ -1,6 +1,10 @@ package fbsdutil import ( + "fmt" + + "github.com/go-delve/delve/pkg/dwarf/op" + "github.com/go-delve/delve/pkg/dwarf/regnum" "github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc/amd64util" ) @@ -178,3 +182,71 @@ func (r *AMD64Registers) Copy() (proc.Registers, error) { } return &rr, nil } + +func (r *AMD64Registers) SetReg(regNum uint64, reg *op.DwarfRegister) (bool, error) { + var p *int64 + switch regNum { + case regnum.AMD64_Rax: + p = &r.Regs.Rax + case regnum.AMD64_Rbx: + p = &r.Regs.Rbx + case regnum.AMD64_Rcx: + p = &r.Regs.Rcx + case regnum.AMD64_Rdx: + p = &r.Regs.Rdx + case regnum.AMD64_Rsi: + p = &r.Regs.Rsi + case regnum.AMD64_Rdi: + p = &r.Regs.Rdi + case regnum.AMD64_Rbp: + p = &r.Regs.Rbp + case regnum.AMD64_Rsp: + p = &r.Regs.Rsp + case regnum.AMD64_R8: + p = &r.Regs.R8 + case regnum.AMD64_R9: + p = &r.Regs.R9 + case regnum.AMD64_R10: + p = &r.Regs.R10 + case regnum.AMD64_R11: + p = &r.Regs.R11 + case regnum.AMD64_R12: + p = &r.Regs.R12 + case regnum.AMD64_R13: + p = &r.Regs.R13 + case regnum.AMD64_R14: + p = &r.Regs.R14 + case regnum.AMD64_R15: + p = &r.Regs.R15 + case regnum.AMD64_Rip: + p = &r.Regs.Rip + case regnum.AMD64_Rflags: + p = &r.Regs.Rflags + } + + if p != nil { + if reg.Bytes != nil && len(reg.Bytes) != 8 { + return false, fmt.Errorf("wrong number of bytes for register %s (%d)", regnum.AMD64ToName(regNum), len(reg.Bytes)) + } + *p = int64(reg.Uint64Val) + return false, nil + } + + if r.loadFpRegs != nil { + if err := r.loadFpRegs(r); err != nil { + return false, err + } + r.loadFpRegs = nil + } + + if regNum < regnum.AMD64_XMM0 || regNum > regnum.AMD64_XMM0+15 { + return false, fmt.Errorf("can not set %s", regnum.AMD64ToName(regNum)) + } + + reg.FillBytes() + + if err := r.Fpregset.SetXmmRegister(int(regNum-regnum.AMD64_XMM0), reg.Bytes); err != nil { + return false, err + } + return true, nil +} diff --git a/pkg/proc/native/ptrace_freebsd.go b/pkg/proc/native/ptrace_freebsd.go index f165e47d..fe7c5c37 100644 --- a/pkg/proc/native/ptrace_freebsd.go +++ b/pkg/proc/native/ptrace_freebsd.go @@ -5,16 +5,13 @@ package native //#include // // #include -// #include "ptrace_freebsd_amd64.h" +// import "C" import ( - "syscall" "unsafe" sys "golang.org/x/sys/unix" - - "github.com/go-delve/delve/pkg/proc/amd64util" ) // ptraceAttach executes the sys.PtraceAttach call. @@ -42,9 +39,15 @@ func ptraceSingleStep(id int) error { // Get a list of the thread ids of a process func ptraceGetLwpList(pid int) (tids []int32) { - num_lwps, _ := C.ptrace_get_num_lwps(C.int(pid)) - tids = make([]int32, num_lwps) - n, _ := C.ptrace_get_lwp_list(C.int(pid), (*C.int)(unsafe.Pointer(&tids[0])), C.size_t(num_lwps)) + numLWPS := C.ptrace(C.PT_GETNUMLWPS, C.pid_t(pid), C.caddr_t(unsafe.Pointer(uintptr(0))), C.int(0)) + if numLWPS < 0 { + panic("PT_GETNUMLWPS failed") + } + tids = make([]int32, numLWPS) + n := C.ptrace(C.PT_GETLWPLIST, C.pid_t(pid), C.caddr_t(unsafe.Pointer(&tids[0])), C.int(numLWPS)) + if n < 0 { + panic("PT_GETLWPLIST failed") + } return tids[0:n] } @@ -54,20 +57,6 @@ func ptraceGetLwpInfo(wpid int) (info sys.PtraceLwpInfoStruct, err error) { return info, err } -func ptraceGetRegset(id int) (regset amd64util.AMD64Xstate, err error) { - _, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(id), uintptr(unsafe.Pointer(®set.AMD64PtraceFpRegs)), 0, 0, 0) - if err == syscall.Errno(0) || err == syscall.ENODEV { - var xsave_len C.size_t - xsave, _ := C.ptrace_get_xsave(C.int(id), &xsave_len) - defer C.free(unsafe.Pointer(xsave)) - if xsave != nil { - xsave_sl := C.GoBytes(unsafe.Pointer(xsave), C.int(xsave_len)) - err = amd64util.AMD64XstateRead(xsave_sl, false, ®set) - } - } - return -} - // id may be a PID or an LWPID func ptraceReadData(id int, addr uintptr, data []byte) (n int, err error) { return sys.PtraceIO(sys.PIOD_READ_D, id, addr, data, len(data)) diff --git a/pkg/proc/native/ptrace_freebsd_amd64.c b/pkg/proc/native/ptrace_freebsd_amd64.c deleted file mode 100644 index 40494711..00000000 --- a/pkg/proc/native/ptrace_freebsd_amd64.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -#include "ptrace_freebsd_amd64.h" - -/* Returns the number of kernel threads associated with the traced process. */ -int ptrace_get_num_lwps(int pid) { - int ret; - errno = 0; - ret = ptrace(PT_GETNUMLWPS, (pid_t)pid, 0, 0); - return (ret); -} - -/* - * Fetches the list of LWPs for a given process into tids. Returns the number - * of LWP entries filled in. Sets errno on return. - */ -int ptrace_get_lwp_list(int pid, int *tids, size_t len) { - int ret; - errno = 0; - ret = ptrace(PT_GETLWPLIST, (pid_t)pid, (caddr_t)tids, len); - return (ret); -} - -/* - * Returns a pointer to the X86 XSAVE data, or NULL on failure. Returns the - * length of the buffer in the len argument. Must be freed when no longer in - * use. Modifies errno. - */ -unsigned char* ptrace_get_xsave(int tid, size_t *len) { - static ssize_t xsave_len = 0; - static int getxstate_info_errno = 0; - unsigned char *buf; - int err; - - if (xsave_len == 0) { - /* Haven't tried to set the size yet */ - struct ptrace_xstate_info info; - err = ptrace(PT_GETXSTATE_INFO, (pid_t)tid, - (caddr_t)&info, sizeof(info)); - if (err == 0) - xsave_len = info.xsave_len; - else { - xsave_len = -1; - getxstate_info_errno = errno; - } - } - if (xsave_len < 0) { - /* Not supported on this system */ - errno = getxstate_info_errno; - return (NULL); - } - - buf = malloc(xsave_len); - if (buf == NULL) { - errno; - return (NULL); - } - err = ptrace(PT_GETXSTATE, (pid_t)tid, (caddr_t)buf, xsave_len); - if (err == 0) { - errno = 0; - *len = xsave_len; - return (buf); - } else { - free(buf); - return (NULL); - } -} diff --git a/pkg/proc/native/ptrace_freebsd_amd64.go b/pkg/proc/native/ptrace_freebsd_amd64.go new file mode 100644 index 00000000..462d9aeb --- /dev/null +++ b/pkg/proc/native/ptrace_freebsd_amd64.go @@ -0,0 +1,89 @@ +package native + +/* +#include +#include +*/ +import "C" + +import ( + "fmt" + "unsafe" + + "github.com/go-delve/delve/pkg/proc/amd64util" +) + +var ( + xsaveLen int + xsaveErr error +) + +func ptraceGetXsaveLen(tid int) (int, error) { + var info C.struct_ptrace_xstate_info + ret, err := C.ptrace(C.PT_GETXSTATE_INFO, C.pid_t(tid), C.caddr_t(unsafe.Pointer(&info)), C.int(unsafe.Sizeof(info))) + if ret == 0 { + xsaveLen = int(info.xsave_len) + } else { + xsaveLen, xsaveErr = -1, err + return xsaveLen, fmt.Errorf("failed to get xstate info: %v", err) + } + return xsaveLen, nil +} + +func ptraceXsaveLen(tid int) (int, error) { + if xsaveLen > 0 { + return xsaveLen, nil + } + if xsaveLen < 0 { + return xsaveLen, fmt.Errorf("failed to get xstate info: %v", xsaveErr) + } + return ptraceGetXsaveLen(tid) +} + +// ptraceGetXsave gets the X86 XSAVE data for the given tid. +func ptraceGetXsave(tid int) ([]byte, error) { + len, err := ptraceXsaveLen(tid) + if err != nil { + return nil, err + } + xsaveBuf := make([]byte, len) + ret, err := C.ptrace(C.PT_GETXSTATE, C.pid_t(tid), C.caddr_t(unsafe.Pointer(&xsaveBuf[0])), C.int(len)) + if ret != 0 { + return nil, fmt.Errorf("failed to get xstate: %v", err) + } + return xsaveBuf, nil +} + +// ptraceSetXsave sets the X86 XSAVE data for the given tid. +func ptraceSetXsave(tid int, xsaveBuf []byte) error { + ret, err := C.ptrace(C.PT_SETXSTATE, C.pid_t(tid), C.caddr_t(unsafe.Pointer(&xsaveBuf[0])), C.int(len(xsaveBuf))) + if ret != 0 { + return fmt.Errorf("failed to set xstate: %v", err) + } + return nil +} + +func ptraceGetRegset(id int) (*amd64util.AMD64Xstate, error) { + var regset amd64util.AMD64Xstate + ret, err := C.ptrace(C.PT_GETFPREGS, C.pid_t(id), C.caddr_t(unsafe.Pointer(®set.AMD64PtraceFpRegs)), C.int(0)) + if ret != 0 { + return nil, fmt.Errorf("failed to get FP registers: %v", err) + } + regset.Xsave, err = ptraceGetXsave(id) + if err != nil { + return nil, err + } + err = amd64util.AMD64XstateRead(regset.Xsave, false, ®set) + return ®set, err +} + +func ptraceSetRegset(id int, regset *amd64util.AMD64Xstate) error { + ret, err := C.ptrace(C.PT_SETFPREGS, C.pid_t(id), C.caddr_t(unsafe.Pointer(®set.AMD64PtraceFpRegs)), C.int(0)) + if ret != 0 { + return fmt.Errorf("failed to set FP registers: %v", err) + } + if regset.Xsave != nil { + return ptraceSetXsave(id, regset.Xsave) + } + return nil +} diff --git a/pkg/proc/native/ptrace_freebsd_amd64.h b/pkg/proc/native/ptrace_freebsd_amd64.h deleted file mode 100644 index 8d06d2d0..00000000 --- a/pkg/proc/native/ptrace_freebsd_amd64.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -struct ptrace_lwpinfo; - -unsigned char* ptrace_get_xsave(int tid, size_t *len); -int ptrace_get_lwp_list(int pid, int *tids, size_t len); -int ptrace_get_num_lwps(int pid); diff --git a/pkg/proc/native/registers_freebsd_amd64.go b/pkg/proc/native/registers_freebsd_amd64.go index 6e65c545..2e7449b5 100644 --- a/pkg/proc/native/registers_freebsd_amd64.go +++ b/pkg/proc/native/registers_freebsd_amd64.go @@ -6,7 +6,6 @@ import ( sys "golang.org/x/sys/unix" "github.com/go-delve/delve/pkg/dwarf/op" - "github.com/go-delve/delve/pkg/dwarf/regnum" "github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc/amd64util" "github.com/go-delve/delve/pkg/proc/fbsdutil" @@ -25,52 +24,17 @@ func (thread *nativeThread) setPC(pc uint64) error { } // SetReg changes the value of the specified register. -func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) (err error) { +func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { ir, err := registers(thread) if err != nil { return err } r := ir.(*fbsdutil.AMD64Registers) - switch regNum { - case regnum.AMD64_Rax: - r.Regs.Rax = int64(reg.Uint64Val) - case regnum.AMD64_Rbx: - r.Regs.Rbx = int64(reg.Uint64Val) - case regnum.AMD64_Rcx: - r.Regs.Rcx = int64(reg.Uint64Val) - case regnum.AMD64_Rdx: - r.Regs.Rdx = int64(reg.Uint64Val) - case regnum.AMD64_Rsi: - r.Regs.Rsi = int64(reg.Uint64Val) - case regnum.AMD64_Rdi: - r.Regs.Rdi = int64(reg.Uint64Val) - case regnum.AMD64_Rbp: - r.Regs.Rbp = int64(reg.Uint64Val) - case regnum.AMD64_Rsp: - r.Regs.Rsp = int64(reg.Uint64Val) - case regnum.AMD64_R8: - r.Regs.R8 = int64(reg.Uint64Val) - case regnum.AMD64_R9: - r.Regs.R9 = int64(reg.Uint64Val) - case regnum.AMD64_R10: - r.Regs.R10 = int64(reg.Uint64Val) - case regnum.AMD64_R11: - r.Regs.R11 = int64(reg.Uint64Val) - case regnum.AMD64_R12: - r.Regs.R12 = int64(reg.Uint64Val) - case regnum.AMD64_R13: - r.Regs.R13 = int64(reg.Uint64Val) - case regnum.AMD64_R14: - r.Regs.R14 = int64(reg.Uint64Val) - case regnum.AMD64_R15: - r.Regs.R15 = int64(reg.Uint64Val) - case regnum.AMD64_Rip: - r.Regs.Rip = int64(reg.Uint64Val) - default: - return fmt.Errorf("changing register %d not implemented", regNum) + fpchanged, err := r.SetReg(regNum, reg) + if err != nil { + return err } - thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) }) - return + return setRegisters(thread, r, fpchanged) } func registers(thread *nativeThread) (proc.Registers, error) { @@ -88,18 +52,14 @@ func registers(thread *nativeThread) (proc.Registers, error) { return nil, err } r := fbsdutil.NewAMD64Registers(®s, uint64(fsbase), func(r *fbsdutil.AMD64Registers) error { - var fpregset amd64util.AMD64Xstate var floatLoadError error - r.Fpregs, fpregset, floatLoadError = thread.fpRegisters() - r.Fpregset = &fpregset + r.Fpregs, r.Fpregset, floatLoadError = thread.fpRegisters() return floatLoadError }) return r, nil } -const _NT_X86_XSTATE = 0x202 - -func (thread *nativeThread) fpRegisters() (regs []proc.Register, fpregs amd64util.AMD64Xstate, err error) { +func (thread *nativeThread) fpRegisters() (regs []proc.Register, fpregs *amd64util.AMD64Xstate, err error) { thread.dbp.execPtraceFunc(func() { fpregs, err = ptraceGetRegset(thread.ID) }) if err != nil { err = fmt.Errorf("could not get floating point registers: %v", err.Error()) @@ -107,3 +67,16 @@ func (thread *nativeThread) fpRegisters() (regs []proc.Register, fpregs amd64uti regs = fpregs.Decode() return } + +func setRegisters(thread *nativeThread, r *fbsdutil.AMD64Registers, setFP bool) (err error) { + thread.dbp.execPtraceFunc(func() { + err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) + if err != nil { + return + } + if setFP && r.Fpregset != nil { + err = ptraceSetRegset(thread.ID, r.Fpregset) + } + }) + return +} diff --git a/pkg/proc/native/threads_freebsd.go b/pkg/proc/native/threads_freebsd.go index 9f87e00e..f9163014 100644 --- a/pkg/proc/native/threads_freebsd.go +++ b/pkg/proc/native/threads_freebsd.go @@ -2,11 +2,10 @@ package native // #include import "C" + import ( "fmt" "github.com/go-delve/delve/pkg/proc/fbsdutil" - "syscall" - "unsafe" sys "golang.org/x/sys/unix" @@ -78,26 +77,7 @@ func (t *nativeThread) singleStep() (err error) { func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error { sr := savedRegs.(*fbsdutil.AMD64Registers) - - var restoreRegistersErr error - t.dbp.execPtraceFunc(func() { - restoreRegistersErr = sys.PtraceSetRegs(t.ID, (*sys.Reg)(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))} - _, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGS, uintptr(t.ID), uintptr(unsafe.Pointer(&iov)), 0, 0, 0) - return - } - - _, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(unsafe.Pointer(&sr.Fpregset.AMD64PtraceFpRegs)), 0, 0, 0) - return - }) - if restoreRegistersErr == syscall.Errno(0) { - restoreRegistersErr = nil - } - return restoreRegistersErr + return setRegisters(t, sr, true) } func (t *nativeThread) WriteMemory(addr uint64, data []byte) (written int, err error) { diff --git a/pkg/proc/target.go b/pkg/proc/target.go index dfa7b4de..6837d0ba 100644 --- a/pkg/proc/target.go +++ b/pkg/proc/target.go @@ -268,7 +268,7 @@ func (t *Target) Valid() (bool, error) { // Currently only non-recorded processes running on AMD64 support // function calls. func (t *Target) SupportsFunctionCalls() bool { - return (t.Process.BinInfo().Arch.Name == "amd64" && t.Process.BinInfo().GOOS != "freebsd") || t.Process.BinInfo().Arch.Name == "arm64" + return t.Process.BinInfo().Arch.Name == "amd64" || t.Process.BinInfo().Arch.Name == "arm64" } // ClearCaches clears internal caches that should not survive a restart. diff --git a/pkg/proc/test/support.go b/pkg/proc/test/support.go index e35daa46..e56b9075 100644 --- a/pkg/proc/test/support.go +++ b/pkg/proc/test/support.go @@ -325,10 +325,6 @@ func MustSupportFunctionCalls(t *testing.T, testBackend string) { t.Skip("this backend does not support function calls") } - if runtime.GOOS == "freebsd" { - t.Skip("freebsd backend has problems with changing and restoring XMM registers") - } - if runtime.GOOS == "darwin" && os.Getenv("TRAVIS") == "true" && runtime.GOARCH == "amd64" { t.Skip("function call injection tests are failing on macOS on Travis-CI (see #1802)") }