pkg/proc: fix and improve freebsd register handling (#3019)
* 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 51090f003bace1f8cc37b8480ffdb6f6cc91fa5a. Fixes #3001 * pkg/proc: deduplicate register setting code on freebsd
This commit is contained in:
parent
5b88e45ca9
commit
9c5777e762
@ -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
|
||||
}
|
||||
|
||||
@ -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.
|
||||
@ -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))
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
89
pkg/proc/native/ptrace_freebsd_amd64.go
Normal file
89
pkg/proc/native/ptrace_freebsd_amd64.go
Normal file
@ -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(®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
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
#include <stddef.h>
|
||||
|
||||
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);
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -2,11 +2,10 @@ package native
|
||||
|
||||
// #include <sys/thr.h>
|
||||
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) {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)")
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user