dqqpf32897 2017-11-11 11:24
浏览 96
已采纳

当C库使用不透明的结构指针时,如何解决CGO中的“写障碍错误指针”恐慌

I'm currently writing a Go wrapper around a C library. That C library uses opaque struct pointers to hide information across the interface. However, the underlying implementation stores size_t values in there. This leads to runtime errors in the resulting program. A minimum working example to reproduce the problem looks like this:

main.go:

package main

/*
#include "stddef.h"
// Create an opaque type to hide the details of the underlying data structure.
typedef struct HandlePrivate *Handle;

// In reality, the implementation uses a type derived from size_t for the Handle.
Handle getInvalidPointer() {
    size_t actualHandle = 1;
    return (Handle) actualHandle;
}
 */
import "C"

// Create a temporary slice containing invalid pointers.
// The idea is that the local variable slice can be garbage collected at the end of the function call.
// When the slice is scanned for linked objects, the GC comes across the invalid pointers.
func getTempSlice() {
    slice := make([]C.Handle, 1000000)
    for i, _ := range slice {
        slice[i] = C.getInvalidPointer()
    }
}

func main() {
    getTempSlice()
}

Running this program will lead to the following error

runtime: writebarrierptr *0xc42006c000 = 0x1
fatal error: bad pointer in write barrier
[...stack trace omitted...]

Note that the errors disappear when the GC is disabled by setting the environment variable GOGC=off.

My question is which is the best way to solve or work around this problem. The library stores integer values in pointers for the sake of information hiding and this seems to confuse the GC. For obvious reasons I don't want to start messing with the library itself but rather absorb this behaviour in my wrapping layer.

My environment is Ubuntu 16.04, with gcc 5.4.0 and Go 1.9.2.

Documentation of cgo

  • 写回答

1条回答 默认 最新

  • dongqingcheng2903 2017-11-11 14:55
    关注

    I can reproduce the error for go1.8.5 and go1.9.2. I cannot reproduce the error for tip: devel +f01b928 Sat Nov 11 06:17:48 2017 +0000 (effectively go1.10alpha).


    // Create a temporary slice containing invalid pointers.
    // The idea is that the local variable slice can be garbage collected at the end of the function call.
    // When the slice is scanned for linked objects, the GC comes across the invalid pointers.
    

    A Go mantra is do not ignore errors. However, you seem to assume that that the GC will gracefully ignore errors. The GC should complain loudly (go1.8.5 and go1.9.2). At worst, with undefined behavior that may vary from release to release, the GC may appear to ignore errors (go devel).

    The Go compiler sees a pointer and the Go runtime GC expects a valid pointer.

    // go tool cgo
    // type _Ctype_Handle *_Ctype_struct_HandlePrivate
    // var handle _Ctype_Handle
    var handle C.Handle
    // main._Ctype_Handle <nil> 0x0
    fmt.Fprintf(os.Stderr, "%[1]T %[1]v %[1]p
    ", handle)
    
    slice := make([]C.Handle, 1000000)
    for i, _ := range slice {
        slice[i] = C.getInvalidPointer()
    }
    

    Use type uintptr. For example,

    package main
    
    import "unsafe"
    
    /*
    #include "stddef.h"
    // Create an opaque type to hide the details of the underlying data structure.
    typedef struct HandlePrivate *Handle;
    
    // In reality, the implementation uses a type derived from size_t for the Handle.
    Handle getInvalidPointer() {
        size_t actualHandle = 1;
        return (Handle) actualHandle;
    }
    */
    import "C"
    
    // Create a temporary slice of C pointers as Go integer type uintptr.
    func getTempSlice() {
        slice := make([]uintptr, 1000000)
        for i, _ := range slice {
            slice[i] = uintptr(unsafe.Pointer(C.getInvalidPointer()))
        }
    }
    
    func main() {
        getTempSlice()
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥15 c++头文件不能识别CDialog