如何解决函数调用中更改指针值的问题? 这是CGO的错误吗?

问题描述:

I'm calling a C function via cgo thusly:

var _outptr_7 C.double 
var _outptr_8 C.double  
var kk uintptr = uintptr(unsafe.Pointer(&_outptr_7))
gogsl.InitializeGslFunction(f)
_result := int32(C.gsl_integration_qags((*C.gsl_function)(unsafe.Pointer(f.CPtr())), C.double(a), C.double(b), C.double(epsabs), C.double(epsrel), C.size_t(limit), (*C.gsl_integration_workspace)(unsafe.Pointer(workspace.Ptr())), (*C.double)(&_outptr_7), (*C.double)(&_outptr_8)))
fmt.Printf("%10.10X
",kk)
return _result, *(*float64)(unsafe.Pointer(&_outptr_7)), *(*float64)(unsafe.Pointer(&_outptr_8))

Now, the kk is here for debugging purposes. I've modified the C function gsl_integration_qags to output the 8th argument it receives (that's &_outptr_7, the _<n> count from 0!)

(The InitializeGslFunction can be ignored for the purpose here...)

The two values - kk and the C function output - match exactly.

However, I get a different number out than the C function returns - it sets the 8th argument via indirection. I'm sure this is correct, I've watched it work in gdb.

It looks like this: (omitting lines that just read (gdb) where I hit enter to repeat command)

Breakpoint 3, qags (f=0xc20800a260, a=0, b=1, epsabs=0, epsrel=9.9999999999999995e-08, limit=1000, workspace=0x782ec0, result=0xc208031e28, abserr=0xc208031e20, q=0x7ffff79ad12c <gsl_integration_qk21>)
 at qags.c:479
479   *result = res_ext;
(gdb) n
480   *abserr = err_ext;
(gdb) p *result
$1 = -4.0000000000000853
(gdb) p result
$2 = (double *) 0xc208031e28
(gdb) n
482   if (err_ext == GSL_DBL_MAX)
485   if (error_type || error_type2)
513     double max_area = GSL_MAX_DBL (fabs (res_ext), fabs (area)); 
515     if (!positive_integrand && max_area < 0.01 * resabs0)
520     double ratio = res_ext / area;
522     if (ratio < 0.01 || ratio > 100.0 || errsum > fabs (area))
526   goto return_error;
535   if (error_type > 2) 
540   if (error_type == 0) 
542       return GSL_SUCCESS;
573 }
gsl_integration_qags (f=0xc20800a260, a=0, b=1, epsabs=0, epsrel=9.9999999999999995e-08, limit=1000, workspace=0x782ec0, result=0xc208031e28, abserr=0xc208031e20) at qags.c:53
53    return status ;
54  }

// Correct value (-4.0) is being returned from C function.

_cgo_3ce45c051e63_Cfunc_gsl_integration_qags (v=0xc208031db8) at /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/numint.go:241
asmcgocall () at /usr/lib/golang/src/runtime/asm_amd64.s:669
669     MOVQ    48(SP), DI
670     MOVQ    (g_stack+stack_hi)(DI), SI
671     SUBQ    40(SP), SI
672     MOVQ    DI, g(CX)
673     MOVQ    SI, SP
asmcgocall () at /usr/lib/golang/src/runtime/asm_amd64.s:674
674     RET
runtime.asmcgocall_errno () at /usr/lib/golang/src/runtime/asm_amd64.s:627
627     MOVL    AX, ret+16(FP)
runtime.asmcgocall_errno () at /usr/lib/golang/src/runtime/asm_amd64.s:628
628     RET
runtime.cgocall_errno (fn=0x405210 <_cgo_3ce45c051e63_Cfunc_gsl_integration_qags>, arg=0xc20805fdb8, ~r2=0) at /usr/lib/golang/src/runtime/cgocall.go:132
132     exitsyscall()
134     return errno

// cgo C glue is about to return from call into Go caller

github.com/dtromb/gogsl/numint._Cfunc_gsl_integration_qags (p0=0xc20800a260, p1=0, p2=1, p3=0, p4=9.9999999999999995e-08, p5=1000, p6=0x782ec0, p7=0xc20805fe28, p8=0xc20805fe20, r1=0)
at /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/:92
92  /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/: No such file or directory.

// Bang, it's broken. p7 value in return val struct is /different than the value that went in/... the location of _outptr_7 has changed.

(gdb) p p7
$3 = (github.com/dtromb/gogsl/numint._Ctype_double *) 0xc20805fe28
(gdb) p *p7
$4 = 0
(gdb) n
github.com/dtromb/gogsl/numint.Qags (f=0xc20805ff58, a=0, b=1, epsabs=0, epsrel=9.9999999999999995e-08, limit=1000, workspace=0xc20800a1d0, ~r7=7753968, ~r8=0, ~r9=0)
at /home/dtrombley/go/src/github.com/dtromb/gogsl/numint/numint.go:75
75     fmt.Printf("%10.10X
",kk)
C208031E28
76     return _result, *(*float64)(unsafe.Pointer(&_outptr_7)), *(*float64)(unsafe.Pointer(&_outptr_8))
(gdb) p _outptr_7
$5 = 0
(gdb) p &_outptr_7
$6 = (float64 *) 0xc20805fe28

// But the correct value is still hanging out there.

 (gdb) p $1
 $7 = -4.0000000000000853

So, what gives? What is going on here? How to fix/workaround?

Here is the cgo binding showing the struct mapping, for completeness:

void
_cgo_a9ebceabba03_Cfunc_gsl_integration_qags(void *v)
{
    struct {
            gsl_function* p0;
            double p1;
            double p2;
            double p3;
            double p4;
            size_t p5;
            gsl_integration_workspace* p6;
            double* p7;
            double* p8;
            int r;
            char __pad76[4];
    } __attribute__((__packed__, __gcc_struct__)) *a = v;
    char *stktop = _cgo_topofstack();
    __typeof__(a->r) r = gsl_integration_qags((void*)a->p0, a->p1, a->p2, a->p3, a->p4, a->p5, (void*)a->p6, (void*)a->p7, (void*)a->p8);
    a = (void*)((char*)a + (_cgo_topofstack() - stktop));
    a->r = r;
}

The bug occurs or does not occur randomly depending on some details of the compilation which should not affect semantics - adding a line which prints out &_outptr_7 before calling the C function causes it to return correctly, for example. Making other changes to the code reverts it back, etc etc.

Something I'm doing wrong? Or bug in cgo?

This is due to a bug in Go 1.4. Fixed in 1.5.

https://golang.org/issue/10303