简体   繁体   English

当C库使用不透明的struct指针时,如何在cgo中解决“写入障碍中的坏指针”恐慌

[英]How to solve “bad pointer in write barrier” panic in cgo when C library uses opaque struct pointers

I'm currently writing a Go wrapper around a C library. 我目前正在围绕C库编写Go包装器。 That C library uses opaque struct pointers to hide information across the interface. 该C库使用不透明的struct指针来隐藏整个接口的信息。 However, the underlying implementation stores size_t values in there. 但是,底层实现存储size_t值。 This leads to runtime errors in the resulting program. 这会导致生成的程序中出现运行时错误。 A minimum working example to reproduce the problem looks like this: 重现问题的最小工作示例如下所示:

main.go : 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 . 请注意,通过设置环境变量GOGC=off禁用GC时,错误消失。

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. 为了隐藏信息,库在指针中存储整数值,这似乎混淆了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. 我的环境是Ubuntu 16.04,gcc 5.4.0和Go 1.9.2。

Documentation of cgo cgo的文档

I can reproduce the error for go1.8.5 and go1.9.2 . 我可以重现go1.8.5go1.9.2的错误。 I cannot reproduce the error for tip: devel +f01b928 Sat Nov 11 06:17:48 2017 +0000 (effectively go1.10alpha). 我无法重现提示的错误: devel +f01b928 Sat Nov 11 06:17:48 2017 +0000 (有效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. Go mantra不会忽略错误。 However, you seem to assume that that the GC will gracefully ignore errors. 但是,您似乎假设GC将优雅地忽略错误。 The GC should complain loudly ( go1.8.5 and go1.9.2 ). GC应该大声抱怨( go1.8.5go1.9.2 )。 At worst, with undefined behavior that may vary from release to release, the GC may appear to ignore errors ( go devel ). 在最坏的情况下,由于未定义的行为可能因版本而异,GC可能会忽略错误( go devel )。

The Go compiler sees a pointer and the Go runtime GC expects a valid pointer. Go编译器看到一个指针,Go运行时GC需要一个有效的指针。

// 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\n", handle)

slice := make([]C.Handle, 1000000)
for i, _ := range slice {
    slice[i] = C.getInvalidPointer()
}

Use type uintptr . 使用类型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()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM