On Mon, Sep 20, 2021 at 07:23:35AM +0200, Laszlo Ersek wrote:
The current "arg_string_list" and
"free_string_list" implementations go
back to commit b6f01f32606d ("Add Go (language) bindings.", 2013-07-03).
There are two problems with them:
- "free_string_list" doesn't actually free anything,
Doh.
- at the *first* such g.Internal_test() call site that passes an
Ostringlist member inside the Optargs argument, namely:
> g.Internal_test ("abc",
> string_addr ("def"),
> []string{},
> false,
> 0,
> 0,
> "123",
> "456",
> []byte{'a', 'b', 'c', '\000',
'a', 'b', 'c'},
> &guestfs.OptargsInternal_test{Ostringlist_is_set: true,
> Ostringlist: []string{}
> }
> )
the "golang/run-bindtests" case crashes:
> panic: runtime error: cgo argument has Go pointer to Go pointer
>
> goroutine 1 [running]:
>
libguestfs.org/guestfs.(*Guestfs).Internal_test.func7(0xc000018180,
> 0xadfb60, 0xadfb80, 0xc000010048, 0x0, 0x0, 0x0, 0xae3e10, 0xae3e30,
> 0xade3a0, ...)
>
golang/src/libguestfs.org/guestfs/guestfs.go:6729 +0xa9
>
libguestfs.org/guestfs.(*Guestfs).Internal_test(0xc000018180, 0x4ee3a5,
> 0x3, 0xc000061be8, 0xc000061af8, 0x0, 0x0, 0xc000061a00, 0x0, 0x0, ...)
>
golang/src/libguestfs.org/guestfs/guestfs.go:6729 +0x3c9
> main.main()
> golang/bindtests/bindtests.go:77 +0x151e
> exit status 2
> FAIL run-bindtests (exit status: 1)
What distro / go version do you see this on, as I can't reproduce
this pointer problem with a standalone demo app ?
It turns out that a C-language array containing C-language pointers can
only be allocated in C:
https://stackoverflow.com/questions/35924545/golang-cgo-panic-runtime-err...
Rewrite the "arg_string_list" and "free_string_list" functions
almost
entirely in C.
Signed-off-by: Laszlo Ersek <lersek(a)redhat.com>
---
generator/golang.ml | 46 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 37 insertions(+), 9 deletions(-)
diff --git a/generator/golang.ml b/generator/golang.ml
index d328abe4ed08..5db478e8a494 100644
--- a/generator/golang.ml
+++ b/generator/golang.ml
@@ -52,6 +52,39 @@ _go_guestfs_create_flags (unsigned flags)
{
return guestfs_create_flags (flags);
}
+
+//
+// A C-language array of C-language pointers needs to be allocated in C.
+//
https://stackoverflow.com/questions/35924545/golang-cgo-panic-runtime-err...
+//
+typedef char *pChar;
+
+pChar *allocStringArray (size_t nmemb)
+{
+ pChar *array;
+
+ array = malloc (sizeof (pChar) * (nmemb + 1));
+ array[nmemb] = NULL;
+ return array;
+}
+
+void storeString (pChar *array, size_t idx, pChar string)
+{
+ array[idx] = string;
+}
+
+void freeStringArray (pChar *array)
+{
+ pChar *position;
+ pChar string;
+
+ position = array;
+ while ((string = *position) != NULL) {
+ free (string);
+ position++;
+ }
+ free (array);
+}
*/
import \"C\"
@@ -148,12 +181,11 @@ func (g *Guestfs) Close () *GuestfsError {
* C strings and golang []string.
*/
func arg_string_list (xs []string) **C.char {
- r := make ([]*C.char, 1 + len (xs))
+ r := C.allocStringArray (C.size_t (len (xs)))
for i, x := range xs {
- r[i] = C.CString (x)
+ C.storeString (r, C.size_t (i), C.CString (x));
}
- r[len (xs)] = nil
- return &r[0]
+ return (**C.char) (r)
}
This could be done purely in Go without helper functions
func array_elem(arr **C.char, idx int) **C.char {
return (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(arr)) +
(unsafe.Sizeof(arr) * uintptr(idx))))
}
func array_size(arr **C.char, len int) C.size_t {
return C.size_t(unsafe.Sizeof(*arr) * (1 + uintptr(len)))
}
func arg_string_list2(xs []string) **C.char {
var r **C.char
r = (**C.char)(C.malloc(array_size(len(xs))))
for i, x := range xs {
str := array_elem(r, i)
*str = C.CString(x)
}
str := array_elem(r, len(xs))
*str = nil
return r
}
func count_string_list (argv **C.char) int {
@@ -167,11 +199,7 @@ func count_string_list (argv **C.char) int {
}
func free_string_list (argv **C.char) {
- for *argv != nil {
- //C.free (*argv)
- argv = (**C.char) (unsafe.Pointer (uintptr (unsafe.Pointer (argv)) +
- unsafe.Sizeof (*argv)))
- }
+ C.freeStringArray ((*C.pChar) (argv))
}
This can be done entirely in Go
func free_string_list (argv **C.char) {
for i := 0; ; i++ {
str := array_elem(argv, i)
if *str == nil {
break
}
C.free(unsafe.Pointer(*str))
}
C.free(unsafe.Pointer(argv))
}
func return_string_list (argv **C.char) []string {
--
2.19.1.3.g30247aa5d201
_______________________________________________
Libguestfs mailing list
Libguestfs(a)redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs
Regards,
Daniel
--
|:
https://berrange.com -o-
https://www.flickr.com/photos/dberrange :|
|:
https://libvirt.org -o-
https://fstop138.berrange.com :|
|:
https://entangle-photo.org -o-
https://www.instagram.com/dberrange :|