Passing array of Go pointers to C

According to cgo Rules § Passing Pointers:

Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers.

So code like this isn’t allowed:

func test_slices(slices ...[]byte) int {
	lens := make([]C.size_t, len(slices))
	ptrs := make([]*C.uchar, len(slices))
	for i, _ := range slices {
		lens[i] = (C.size_t)(len(slices[i]))
		ptrs[i] = (*C.uchar)(&slices[i][0])
	}
	cPtrInputs := (**C.uchar)(&ptrs[0])
	cSizesPtr := (*C.size_t)(&lens[0])
	cAmounts := (C.size_t)(len(slices))
	C.test_slices(cPtrInputs, cSizesPtr, cAmounts)
}

Why is that? why can are we allowed to pass Go pointers to C but not an array of Go pointers to C?

And also, what’s the right way to do? create C pointers via C.malloc + copy + pass those? is there a way to do this without a malloc+copy overhead?

1 Like

I think this source code from cgoCheckPointer hints at the reason:

// When and if we implement a moving garbage collector,
// cgoCheckPointer will pin the pointer for the duration of the cgo
// call.  (This is necessary but not sufficient; the cgo program will
// also have to change to pin Go pointers that cannot point to Go
// pointers.)

If passing pointers to pointers was allowed, then if/when Go ever starts moving its allocations, though the slice itself was pinned, any pointer elements within the slice could be moved at any time and cgo could end up accessing freed/repurposed memory. To prevent that, cgoCheckPointer would have to do something like recursivelly pin the entire graph of pointers to pointers to pointers…, disable moving all together for programs that use cgo, perhaps disable moving during a cgo call, etc.

1 Like