pky/proc: enable function call injection in Delve for linux/ppc64le (#3449)

* enable func call injection on delve for ppc64le

* Function call injection on Delve/ppc64le, modified DWARF encoding and decoding for floating point registers to make floatsum test work

* Function call injection on Delve/ppc64le cleanup

* skip PIE tests for function call injection on other packages

* Address review comments

* accounted for additional skipped PIE tests for function call injection

* Code cleanup and undoing revert of previous commit

* Enable function call injection only on 1.22 and above and some cleanup

* additional cleanup, go fmt run

* Debug function call tests fail on ppc64le/PIE mode adjusted the backup_test_health.md file accordingly
This commit is contained in:
Archana Ravindar 2023-09-21 23:09:57 +05:30 committed by GitHub
parent 4d30cd461b
commit ebc3e61367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 150 additions and 33 deletions

@ -20,6 +20,8 @@ Tests skipped by each supported backend:
* 1 broken - cgo stacktraces * 1 broken - cgo stacktraces
* linux/ppc64le/native skipped = 1 * linux/ppc64le/native skipped = 1
* 1 broken in linux ppc64le * 1 broken in linux ppc64le
* linux/ppc64le/native/pie skipped = 3
* 3 broken - pie mode
* pie skipped = 2 * pie skipped = 2
* 2 upstream issue - https://github.com/golang/go/issues/29322 * 2 upstream issue - https://github.com/golang/go/issues/29322
* ppc64le skipped = 11 * ppc64le skipped = 11

@ -17,13 +17,14 @@ const (
PPC64LE_F0 = PPC64LE_FIRST_FPR PPC64LE_F0 = PPC64LE_FIRST_FPR
PPC64LE_LAST_FPR = 63 PPC64LE_LAST_FPR = 63
// Vector (Altivec/VMX) registers: from V0 to V31 // Vector (Altivec/VMX) registers: from V0 to V31
PPC64LE_FIRST_VMX = 64 PPC64LE_FIRST_VMX = 77
PPC64LE_V0 = PPC64LE_FIRST_VMX PPC64LE_V0 = PPC64LE_FIRST_VMX
PPC64LE_LAST_VMX = 95 PPC64LE_LAST_VMX = 108
// Vector Scalar (VSX) registers: from VS0 to VS63 // Vector Scalar (VSX) registers: from VS32 to VS63
PPC64LE_FIRST_VSX = 96 // On ppc64le these are mapped to F0 to F31
PPC64LE_FIRST_VSX = 32
PPC64LE_VS0 = PPC64LE_FIRST_VSX PPC64LE_VS0 = PPC64LE_FIRST_VSX
PPC64LE_LAST_VSX = 160 PPC64LE_LAST_VSX = 63
// Condition Registers: from CR0 to CR7 // Condition Registers: from CR0 to CR7
PPC64LE_CR0 = 0 PPC64LE_CR0 = 0
// Special registers // Special registers

@ -336,10 +336,16 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
if err := writePointer(bi, scope.Mem, regs.SP()-3*uint64(bi.Arch.PtrSize()), uint64(fncall.argFrameSize)); err != nil { if err := writePointer(bi, scope.Mem, regs.SP()-3*uint64(bi.Arch.PtrSize()), uint64(fncall.argFrameSize)); err != nil {
return nil, err return nil, err
} }
case "arm64": case "arm64", "ppc64le":
// debugCallV2 on arm64 needs a special call sequence, callOP can not be used // debugCallV2 on arm64 needs a special call sequence, callOP can not be used
sp := regs.SP() sp := regs.SP()
sp -= 2 * uint64(bi.Arch.PtrSize()) var spOffset uint64
if bi.Arch.Name == "arm64" {
spOffset = 2 * uint64(bi.Arch.PtrSize())
} else {
spOffset = 4 * uint64(bi.Arch.PtrSize())
}
sp -= spOffset
if err := setSP(thread, sp); err != nil { if err := setSP(thread, sp); err != nil {
return nil, err return nil, err
} }
@ -349,7 +355,7 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
if err := setLR(thread, regs.PC()); err != nil { if err := setLR(thread, regs.PC()); err != nil {
return nil, err return nil, err
} }
if err := writePointer(bi, scope.Mem, sp-uint64(2*bi.Arch.PtrSize()), uint64(fncall.argFrameSize)); err != nil { if err := writePointer(bi, scope.Mem, sp-spOffset, uint64(fncall.argFrameSize)); err != nil {
return nil, err return nil, err
} }
regs, err = thread.Registers() regs, err = thread.Registers()
@ -365,6 +371,7 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
fncallLog("function call initiated %v frame size %d goroutine %d (thread %d)", fncall.fn, fncall.argFrameSize, scope.g.ID, thread.ThreadID()) fncallLog("function call initiated %v frame size %d goroutine %d (thread %d)", fncall.fn, fncall.argFrameSize, scope.g.ID, thread.ThreadID())
@ -483,11 +490,12 @@ func callOP(bi *BinaryInfo, thread Thread, regs Registers, callAddr uint64) erro
return err return err
} }
return setPC(thread, callAddr) return setPC(thread, callAddr)
case "arm64": case "arm64", "ppc64le":
if err := setLR(thread, regs.PC()); err != nil { if err := setLR(thread, regs.PC()); err != nil {
return err return err
} }
return setPC(thread, callAddr) return setPC(thread, callAddr)
default: default:
panic("not implemented") panic("not implemented")
} }
@ -864,6 +872,8 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
archoff := uint64(0) archoff := uint64(0)
if bi.Arch.Name == "arm64" { if bi.Arch.Name == "arm64" {
archoff = 8 archoff = 8
} else if bi.Arch.Name == "ppc64le" {
archoff = 40
} }
// get error from top of the stack and return it to user // get error from top of the stack and return it to user
errvar, err := readStackVariable(p, thread, regs, archoff, "string", loadFullValue) errvar, err := readStackVariable(p, thread, regs, archoff, "string", loadFullValue)
@ -912,7 +922,7 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
cfa := regs.SP() cfa := regs.SP()
oldpc := regs.PC() oldpc := regs.PC()
var oldlr uint64 var oldlr uint64
if bi.Arch.Name == "arm64" { if bi.Arch.Name == "arm64" || bi.Arch.Name == "ppc64le" {
oldlr = regs.LR() oldlr = regs.LR()
} }
callOP(bi, thread, regs, fncall.fn.Entry) callOP(bi, thread, regs, fncall.fn.Entry)
@ -925,7 +935,7 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
case "amd64": case "amd64":
setSP(thread, cfa) setSP(thread, cfa)
setPC(thread, oldpc) setPC(thread, oldpc)
case "arm64": case "arm64", "ppc64le":
setLR(thread, oldlr) setLR(thread, oldlr)
setPC(thread, oldpc) setPC(thread, oldpc)
default: default:
@ -1006,7 +1016,7 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
if threadg, _ := GetG(thread); threadg != nil { if threadg, _ := GetG(thread); threadg != nil {
callScope.callCtx.stacks = append(callScope.callCtx.stacks, threadg.stack) callScope.callCtx.stacks = append(callScope.callCtx.stacks, threadg.stack)
} }
if bi.Arch.Name == "arm64" { if bi.Arch.Name == "arm64" || bi.Arch.Name == "ppc64le" {
oldlr, err := readUintRaw(thread.ProcessMemory(), regs.SP(), int64(bi.Arch.PtrSize())) oldlr, err := readUintRaw(thread.ProcessMemory(), regs.SP(), int64(bi.Arch.PtrSize()))
if err != nil { if err != nil {
fncall.err = fmt.Errorf("could not restore LR: %v", err) fncall.err = fmt.Errorf("could not restore LR: %v", err)
@ -1023,6 +1033,8 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
archoff := uint64(0) archoff := uint64(0)
if bi.Arch.Name == "arm64" { if bi.Arch.Name == "arm64" {
archoff = 8 archoff = 8
} else if bi.Arch.Name == "ppc64le" {
archoff = 32
} }
fncall.panicvar, err = readStackVariable(p, thread, regs, archoff, "interface {}", callScope.callCtx.retLoadCfg) fncall.panicvar, err = readStackVariable(p, thread, regs, archoff, "interface {}", callScope.callCtx.retLoadCfg)
if err != nil { if err != nil {
@ -1068,7 +1080,6 @@ func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64
scope.PC = fn.Entry scope.PC = fn.Entry
scope.Fn = fn scope.Fn = fn
scope.File, scope.Line = scope.BinInfo.EntryLineForFunc(fn) scope.File, scope.Line = scope.BinInfo.EntryLineForFunc(fn)
scope.Regs.CFA = cfa scope.Regs.CFA = cfa
scope.Regs.Reg(scope.Regs.SPRegNum).Uint64Val = sp scope.Regs.Reg(scope.Regs.SPRegNum).Uint64Val = sp
scope.Regs.Reg(scope.Regs.PCRegNum).Uint64Val = fn.Entry scope.Regs.Reg(scope.Regs.PCRegNum).Uint64Val = fn.Entry
@ -1260,7 +1271,7 @@ func debugCallProtocolReg(archName string, version int) (uint64, bool) {
return 0, false return 0, false
} }
return protocolReg, true return protocolReg, true
case "arm64": case "arm64", "ppc64le":
if version == 2 { if version == 2 {
return regnum.ARM64_X0 + 20, true return regnum.ARM64_X0 + 20, true
} }
@ -1335,6 +1346,15 @@ func regabiMallocgcWorkaround(bi *BinaryInfo) ([]*godwarf.Tree, error) {
m("~r1", t("unsafe.Pointer"), regnum.ARM64_X0, true), m("~r1", t("unsafe.Pointer"), regnum.ARM64_X0, true),
} }
return r, err1 return r, err1
case "ppc64le":
r := []*godwarf.Tree{
m("size", t("uintptr"), regnum.PPC64LE_R0+3, false),
m("typ", t(ptrToRuntimeType), regnum.PPC64LE_R0+4, false),
m("needzero", t("bool"), regnum.PPC64LE_R0+5, false),
m("~r1", t("unsafe.Pointer"), regnum.PPC64LE_R0+3, true),
}
return r, err1
default: default:
// do nothing // do nothing
return nil, nil return nil, nil

@ -3,6 +3,8 @@ package linutil
import ( import (
"fmt" "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"
) )
@ -162,13 +164,51 @@ func (r *PPC64LERegisters) Copy() (proc.Registers, error) {
return &rr, nil return &rr, nil
} }
func (r *PPC64LERegisters) SetReg(regNum uint64, reg *op.DwarfRegister) (fpchanged bool, err error) {
switch regNum {
case regnum.PPC64LE_PC:
r.Regs.Nip = reg.Uint64Val
return false, nil
case regnum.PPC64LE_LR:
r.Regs.Link = reg.Uint64Val
return false, nil
case regnum.PPC64LE_SP:
r.Regs.Gpr[1] = reg.Uint64Val
return false, nil
default:
switch {
case regNum >= regnum.PPC64LE_R0 && regNum <= regnum.PPC64LE_R0+31:
r.Regs.Gpr[regNum-regnum.PPC64LE_R0] = reg.Uint64Val
return false, nil
case regNum >= regnum.PPC64LE_F0 && regNum <= regnum.PPC64LE_F0+31:
if r.loadFpRegs != nil {
err := r.loadFpRegs(r)
r.loadFpRegs = nil
if err != nil {
return false, err
}
}
// On ppc64le, PPC64LE_VS0 .. PPC64LE_VS31 are mapped onto
// PPC64LE_F0 .. PPC64LE_F31
i := regNum - regnum.PPC64LE_VS0
reg.FillBytes()
copy(r.Fpregset[8*i:], reg.Bytes)
return true, nil
default:
return false, fmt.Errorf("changing register %d not implemented", regNum)
}
}
}
type PPC64LEPtraceFpRegs struct { type PPC64LEPtraceFpRegs struct {
Fp []byte Fp []byte
} }
func (fpregs *PPC64LEPtraceFpRegs) Decode() (regs []proc.Register) { func (fpregs *PPC64LEPtraceFpRegs) Decode() (regs []proc.Register) {
for i := 0; i < len(fpregs.Fp); i += 16 { for i := 0; i < len(fpregs.Fp); i += 8 {
regs = proc.AppendBytesRegister(regs, fmt.Sprintf("V%d", i/16), fpregs.Fp[i:i+16]) regs = proc.AppendBytesRegister(regs, fmt.Sprintf("VS%d", i/8), fpregs.Fp[i:i+8])
} }
return return
} }

@ -6,7 +6,6 @@ import (
"unsafe" "unsafe"
"github.com/go-delve/delve/pkg/dwarf/op" "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"
"github.com/go-delve/delve/pkg/proc/linutil" "github.com/go-delve/delve/pkg/proc/linutil"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
@ -63,25 +62,30 @@ func (t *nativeThread) setPC(pc uint64) error {
} }
// SetReg changes the value of the specified register. // SetReg changes the value of the specified register.
func (t *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error { func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
ir, err := registers(t) ir, err := registers(thread)
if err != nil { if err != nil {
return err return err
} }
r := ir.(*linutil.PPC64LERegisters) r := ir.(*linutil.PPC64LERegisters)
switch regNum { fpchanged, err := r.SetReg(regNum, reg)
case regnum.PPC64LE_PC: if err != nil {
r.Regs.Nip = reg.Uint64Val return err
case regnum.PPC64LE_SP: }
r.Regs.Gpr[1] = reg.Uint64Val thread.dbp.execPtraceFunc(func() {
case regnum.PPC64LE_LR: err = ptraceSetGRegs(thread.ID, r.Regs)
r.Regs.Link = reg.Uint64Val if err != syscall.Errno(0) && err != nil {
default: return
panic("SetReg") }
if fpchanged && r.Fpregset != nil {
iov := sys.Iovec{Base: &r.Fpregset[0], Len: uint64(len(r.Fpregset))}
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(thread.ID), uintptr(elf.NT_FPREGSET), uintptr(unsafe.Pointer(&iov)), 0, 0)
}
})
if err == syscall.Errno(0) {
err = nil
} }
t.dbp.execPtraceFunc(func() { err = ptraceSetGRegs(t.ID, r.Regs) })
return err return err
} }

@ -2,6 +2,12 @@ package native
import ( import (
"fmt" "fmt"
"debug/elf"
"syscall"
"unsafe"
sys "golang.org/x/sys/unix"
"github.com/go-delve/delve/pkg/proc" "github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/linutil" "github.com/go-delve/delve/pkg/proc/linutil"
@ -21,5 +27,22 @@ func (t *nativeThread) fpRegisters() ([]proc.Register, []byte, error) {
} }
func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error { func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error {
panic("Unimplemented restoreRegisters method in threads_linux_ppc64le.go") sr := savedRegs.(*linutil.PPC64LERegisters)
var restoreRegistersErr error
t.dbp.execPtraceFunc(func() {
restoreRegistersErr = ptraceSetGRegs(t.ID, sr.Regs)
if restoreRegistersErr != syscall.Errno(0) && restoreRegistersErr != nil {
return
}
if sr.Fpregset != nil {
iov := sys.Iovec{Base: &sr.Fpregset[0], Len: _PPC64LE_FPREGS_SIZE}
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), uintptr(elf.NT_FPREGSET), uintptr(unsafe.Pointer(&iov)), 0, 0)
}
})
if restoreRegistersErr == syscall.Errno(0) {
restoreRegistersErr = nil
}
return restoreRegistersErr
} }

@ -35,9 +35,12 @@ func PPC64LEArch(goos string) *Arch {
usesLR: true, usesLR: true,
PCRegNum: regnum.PPC64LE_PC, PCRegNum: regnum.PPC64LE_PC,
SPRegNum: regnum.PPC64LE_SP, SPRegNum: regnum.PPC64LE_SP,
ContextRegNum: regnum.PPC64LE_R0 + 11,
LRRegNum: regnum.PPC64LE_LR, LRRegNum: regnum.PPC64LE_LR,
asmRegisters: ppc64leAsmRegisters, asmRegisters: ppc64leAsmRegisters,
RegisterNameToDwarf: nameToDwarfFunc(regnum.PPC64LENameToDwarf), RegisterNameToDwarf: nameToDwarfFunc(regnum.PPC64LENameToDwarf),
debugCallMinStackSize: 320,
maxRegArgBytes: 13*8 + 13*8,
} }
} }

@ -4312,6 +4312,8 @@ func TestReadDeferArgs(t *testing.T) {
func TestIssue1374(t *testing.T) { func TestIssue1374(t *testing.T) {
// Continue did not work when stopped at a breakpoint immediately after calling CallFunction. // Continue did not work when stopped at a breakpoint immediately after calling CallFunction.
skipOn(t, "broken - pie mode", "linux", "ppc64le", "native", "pie")
protest.MustSupportFunctionCalls(t, testBackend) protest.MustSupportFunctionCalls(t, testBackend)
withTestProcess("issue1374", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { withTestProcess("issue1374", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFileBreakpoint(p, t, fixture.Source, 7) setFileBreakpoint(p, t, fixture.Source, 7)
@ -4537,6 +4539,8 @@ func testCallConcurrentCheckReturns(p *proc.Target, t *testing.T, gid1, gid2 int
} }
func TestCallConcurrent(t *testing.T) { func TestCallConcurrent(t *testing.T) {
skipOn(t, "broken - pie mode", "linux", "ppc64le", "native", "pie")
protest.MustSupportFunctionCalls(t, testBackend) protest.MustSupportFunctionCalls(t, testBackend)
withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { withTestProcess("teststepconcurrent", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
bp := setFileBreakpoint(p, t, fixture.Source, 24) bp := setFileBreakpoint(p, t, fixture.Source, 24)
@ -4932,6 +4936,7 @@ func TestIssue1925(t *testing.T) {
// In particular the stepInstructionOut function called at the end of a // In particular the stepInstructionOut function called at the end of a
// 'call' procedure should clean the G cache like every other function // 'call' procedure should clean the G cache like every other function
// altering the state of the target process. // altering the state of the target process.
skipOn(t, "broken - pie mode", "linux", "ppc64le", "native", "pie")
protest.MustSupportFunctionCalls(t, testBackend) protest.MustSupportFunctionCalls(t, testBackend)
withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
assertNoError(grp.Continue(), t, "Continue()") assertNoError(grp.Continue(), t, "Continue()")

@ -252,7 +252,7 @@ func (t *Target) Valid() (bool, error) {
// Currently only non-recorded processes running on AMD64 support // Currently only non-recorded processes running on AMD64 support
// function calls. // function calls.
func (t *Target) SupportsFunctionCalls() bool { func (t *Target) SupportsFunctionCalls() bool {
return t.Process.BinInfo().Arch.Name == "amd64" || (t.Process.BinInfo().Arch.Name == "arm64" && t.Process.BinInfo().GOOS != "windows") return t.Process.BinInfo().Arch.Name == "amd64" || (t.Process.BinInfo().Arch.Name == "arm64" && t.Process.BinInfo().GOOS != "windows") || t.Process.BinInfo().Arch.Name == "ppc64le"
} }
// ClearCaches clears internal caches that should not survive a restart. // ClearCaches clears internal caches that should not survive a restart.

@ -310,7 +310,7 @@ func MustSupportFunctionCalls(t *testing.T, testBackend string) {
if runtime.GOOS == "darwin" && os.Getenv("TRAVIS") == "true" && runtime.GOARCH == "amd64" { 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)") t.Skip("function call injection tests are failing on macOS on Travis-CI (see #1802)")
} }
if runtime.GOARCH == "386" || runtime.GOARCH == "ppc64le" { if runtime.GOARCH == "386" {
t.Skip(fmt.Errorf("%s does not support FunctionCall for now", runtime.GOARCH)) t.Skip(fmt.Errorf("%s does not support FunctionCall for now", runtime.GOARCH))
} }
if runtime.GOARCH == "arm64" { if runtime.GOARCH == "arm64" {
@ -318,6 +318,12 @@ func MustSupportFunctionCalls(t *testing.T, testBackend string) {
t.Skip("this version of Go does not support function calls") t.Skip("this version of Go does not support function calls")
} }
} }
if runtime.GOARCH == "ppc64le" {
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 22) {
t.Skip("On PPC64LE Building with Go lesser than 1.22 does not support function calls")
}
}
} }
// DefaultTestBackend changes the value of testBackend to be the default // DefaultTestBackend changes the value of testBackend to be the default

@ -1158,6 +1158,8 @@ type testCaseCallFunction struct {
} }
func TestCallFunction(t *testing.T) { func TestCallFunction(t *testing.T) {
skipOn(t, "broken - pie mode", "linux", "ppc64le", "native", "pie")
protest.MustSupportFunctionCalls(t, testBackend) protest.MustSupportFunctionCalls(t, testBackend)
protest.AllowRecording(t) protest.AllowRecording(t)
@ -1289,6 +1291,7 @@ func TestCallFunction(t *testing.T) {
testCallFunctionSetBreakpoint(t, p, grp, fixture) testCallFunctionSetBreakpoint(t, p, grp, fixture)
assertNoError(grp.Continue(), t, "Continue()") assertNoError(grp.Continue(), t, "Continue()")
for _, tc := range testcases { for _, tc := range testcases {
testCallFunction(t, grp, p, tc) testCallFunction(t, grp, p, tc)
} }

@ -996,6 +996,9 @@ func findStarFile(name string) string {
} }
func TestIssue1598(t *testing.T) { func TestIssue1598(t *testing.T) {
if buildMode == "pie" && runtime.GOARCH == "ppc64le" {
t.Skip("Debug function call Test broken in PIE mode")
}
test.MustSupportFunctionCalls(t, testBackend) test.MustSupportFunctionCalls(t, testBackend)
withTestTerminal("issue1598", t, func(term *FakeTerminal) { withTestTerminal("issue1598", t, func(term *FakeTerminal) {
term.MustExec("break issue1598.go:5") term.MustExec("break issue1598.go:5")

@ -2109,6 +2109,10 @@ func TestForceStopWhileContinue(t *testing.T) {
} }
func TestClientServerFunctionCall(t *testing.T) { func TestClientServerFunctionCall(t *testing.T) {
if buildMode == "pie" && runtime.GOARCH == "ppc64le" {
t.Skip("Debug function call Test broken in PIE mode")
}
protest.MustSupportFunctionCalls(t, testBackend) protest.MustSupportFunctionCalls(t, testBackend)
withTestClient2("fncall", t, func(c service.Client) { withTestClient2("fncall", t, func(c service.Client) {
c.SetReturnValuesLoadConfig(&normalLoadConfig) c.SetReturnValuesLoadConfig(&normalLoadConfig)
@ -2139,6 +2143,9 @@ func TestClientServerFunctionCall(t *testing.T) {
} }
func TestClientServerFunctionCallPanic(t *testing.T) { func TestClientServerFunctionCallPanic(t *testing.T) {
if buildMode == "pie" && runtime.GOARCH == "ppc64le" {
t.Skip("Debug function call Test broken in PIE mode")
}
protest.MustSupportFunctionCalls(t, testBackend) protest.MustSupportFunctionCalls(t, testBackend)
withTestClient2("fncall", t, func(c service.Client) { withTestClient2("fncall", t, func(c service.Client) {
c.SetReturnValuesLoadConfig(&normalLoadConfig) c.SetReturnValuesLoadConfig(&normalLoadConfig)