Skip to content

Commit

Permalink
pkg/proc: fix and improve freebsd register handling (#3019)
Browse files Browse the repository at this point in the history
* pkg/proc: convert freebsd ptrace code to cgo

There is little point in having cgo call a custom C function, when the same
can be done directly from cgo (with less code and effort). Split the amd64
specific code into ptrace_freebsd_amd64.go. Also avoid mixing C.ptrace()
with syscall.SYS_PTRACE.

This will make further changes easier - no functional change intended.

* pkg/proc: check return values of ptrace calls on freebsd

The return values of the PT_GETNUMLWPS and PT_GETLWPLIST ptrace calls were
previously unchecked. While these should not fail, panic instead of using
-1 with slice allocation/handling.

* pkg/proc: return *amd64util.AMD64Xstate from freebsd ptraceGetRegset

Return a pointer to a struct, rather than a struct - this simplifies the
code in both the caller and the ptraceGetRegset function, while also avoiding
struct copying.

* pkg/proc: fix floating point register setting on freebsd

The original code could never work - PT_SETREGS on freebsd does not
take an iovec, nor does it set FP registers. Furthermore, the xsave
bytes were not stored in the amd64util.AMD64Xstate struct.

Updates #3001

* pkg/proc: re-enable function call injection on freebsd

Floating point registers can now be set and restored correctly.

This is a partial revert of 51090f0.

Fixes #3001

* pkg/proc: deduplicate register setting code on freebsd
  • Loading branch information
4a6f656c authored May 31, 2022
1 parent 5b88e45 commit 9c5777e
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 175 deletions.
72 changes: 72 additions & 0 deletions pkg/proc/fbsdutil/regs.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand Down Expand Up @@ -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
}
31 changes: 10 additions & 21 deletions pkg/proc/native/ptrace_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ package native
//#include <sys/ptrace.h>
//
// #include <stdlib.h>
// #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.
Expand Down Expand Up @@ -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]
}

Expand All @@ -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(&regset.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, &regset)
}
}
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))
Expand Down
73 changes: 0 additions & 73 deletions pkg/proc/native/ptrace_freebsd_amd64.c

This file was deleted.

89 changes: 89 additions & 0 deletions pkg/proc/native/ptrace_freebsd_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package native

/*
#include <sys/types.h>
#include <sys/ptrace.h>
*/
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(&regset.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, &regset)
return &regset, 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(&regset.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
}
7 changes: 0 additions & 7 deletions pkg/proc/native/ptrace_freebsd_amd64.h

This file was deleted.

Loading

0 comments on commit 9c5777e

Please sign in to comment.