如何解决CEG库中使用不透明结构指针时cgo中的“写入障碍中的错误指针”恐慌
问题描述:
我目前正在围绕C库编写一个Go包装。该C库使用不透明的结构指针来隐藏接口中的信息。但是,底层实现会在其中存储size_t值。这会导致生成的程序出现运行时错误。 最小工作示例来重现问题是这样的:如何解决CEG库中使用不透明结构指针时cgo中的“写入障碍中的错误指针”恐慌
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()
}
运行这个程序会导致以下错误
runtime: writebarrierptr *0xc42006c000 = 0x1
fatal error: bad pointer in write barrier
[...stack trace omitted...]
注意,错误消失时,GC通过设置环境变量GOGC=off
来禁用。
我的问题是哪个是解决或解决此问题的最佳方法。该库为了隐藏信息而在指针中存储整数值,这似乎混淆了GC。出于显而易见的原因,我不想开始搞乱库本身,而是在包装图层中吸收这种行为。
我的环境是Ubuntu 16.04,带有gcc 5.4.0和Go 1.9.2。
答
我能重现错误go1.8.5
和go1.9.2
。我无法重现提示的错误: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.
一去的口头禅是不要忽略错误。但是,您似乎认为GC将优雅地忽略错误。 GC应该大声抱怨(go1.8.5
和go1.9.2
)。最糟糕的是,由于发布到发布的行为可能会有所不同,GC可能会忽略错误(go devel
)。
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()
}
使用类型uintptr
。例如,
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()
}
我在使用OP代码的“go version go1.9.2 linux/amd64”与我的拱门上有同样的错误。错误:“运行时:writebarrierptr * 0xc42007a000 = 0x1 致命错误:写入障碍中的指针错误” – Stargateur