Golang中CGO参数传递Go指针到Go指针导致的运行时错误:panic

Golang中CGO参数传递Go指针到Go指针导致的运行时错误:panic 我正在测试使用bradfitz测试框架(https://github.com/bradfitz/go-sql-test)的驱动程序(https://github.com/ibmdb/go_ibm_db),该驱动程序在Windows上运行良好,但在Linux上无法正常工作,并产生以下类型的错误:

panic: runtime error: cgo argument has Go pointer to Go pointer

goroutine 40 [running]:
github.com/ibmdb/go_ibm_db/api.SQLBindCol.func1(0x4000100070001, 0xc00005b138, 0xc000000004, 0xc00005b118, 0x5aba40)
/home/rakhil/go/src/github.com/bradfitz/go-sql-test/src/github.com/ibmdb/go_ibm_db/api/zapi_unix.go:26 +0x4c
github.com/ibmdb/go_ibm_db/api.SQLBindCol(0x4000100070001, 0xc00005b138, 0x4, 0xc00005b118, 0x0)
/home/rakhil/go/src/github.com/bradfitz/go-sql-test/src/github.com/ibmdb/go_ibm_db/api/zapi_unix.go:26 +0x59
github.com/ibmdb/go_ibm_db.(*BufferLen).Bind(0xc00005b118, 0x70001, 0x0, 0x440004, 0xc00005b138, 0x4, 0x8, 0xc00014e560)
/home/rakhil/go/src/github.com/bradfitz/go-sql-test/src/github.com/ibmdb/go_ibm_db/column.go:29 +0x62
github.com/ibmdb/go_ibm_db.(*BindableColumn).Bind(0xc00005b100, 0x70001, 0x0, 0xc00005b100, 0x0, 0x0)
/home/rakhil/go/src/github.com/bradfitz/go-sql-test/src/github.com/ibmdb/go_ibm_db/column.go:202 +0x6c
github.com/ibmdb/go_ibm_db.(*ODBCStmt).BindColumns(0xc0000ad1d0, 0x91fcb0, 0x0)
/home/rakhil/go/src/github.com/bradfitz/go-sql-test/src/github.com/ibmdb/go_ibm_db/odbcstmt.go:148 +0x1eb

我不知道为什么会产生这种错误


更多关于Golang中CGO参数传递Go指针到Go指针导致的运行时错误:panic的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中CGO参数传递Go指针到Go指针导致的运行时错误:panic的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这个错误是由于在CGO调用中传递了包含Go指针的Go指针,违反了Go的CGO指针传递规则。在Go 1.6及以上版本中,CGO严格禁止将包含其他Go指针的Go指针传递给C代码。

从错误堆栈看,问题出现在SQLBindCol函数中,该函数通过CGO调用ODBC API时传递了不合规的指针。

问题分析:

  • 在Windows上可能使用了不同的内存模型或编译器设置
  • Linux上对CGO指针检查更加严格
  • SQLBindCol函数可能传递了包含Go指针的结构体

解决方案:

需要修改CGO调用,确保不传递包含Go指针的Go指针。以下是几种处理方式:

  1. 使用C分配的内存:
package main

/*
#include <stdlib.h>
*/
import "C"
import "unsafe"

// 示例:使用C.malloc分配内存传递给C函数
func safeBindCol() {
    // 使用C分配内存,而不是Go指针
    cBuffer := C.malloc(C.size_t(1024))
    defer C.free(cBuffer)
    
    // 将C指针传递给CGO函数
    // api.SQLBindCol(..., unsafe.Pointer(cBuffer), ...)
}
  1. 使用uintptr转换:
package main

import "unsafe"

// 将Go指针转换为uintptr传递
func safePointerPassing() {
    var data []byte = make([]byte, 1024)
    
    // 获取底层数组指针并转换为uintptr
    dataPtr := unsafe.Pointer(&data[0])
    
    // 在CGO调用中使用uintptr
    // 注意:需要确保data在调用期间不会被GC移动
    _ = uintptr(dataPtr)
}
  1. 修改现有的API包装: 查看zapi_unix.go第26行附近的代码,可能需要类似这样的修改:
// 原来的可能有问题代码:
// func SQLBindCol(hstmt Handle, icol uint16, target unsafe.Pointer) { ... }

// 修改为使用C分配的内存
func SQLBindCol(hstmt Handle, icol uint16, target unsafe.Pointer) {
    // 使用C分配的内存缓冲区
    cBuf := C.malloc(bufferSize)
    defer C.free(cBuf)
    
    // 调用C函数
    C.SQLBindCol(C.SQLHSTMT(hstmt), C.SQLUSMALLINT(icol), 
                 C.SQLSMALLINT(dataType), cBuf, ...)
    
    // 将数据从C缓冲区复制回Go
    // C.GoBytes(cBuf, C.int(actualLength))
}
  1. 使用cgo.Handle(Go 1.17+):
import "runtime/cgo"

// 对于需要长期保存的Go指针
func useCgoHandle() {
    goObj := &MyGoStruct{}
    handle := cgo.NewHandle(goObj)
    
    // 将handle.Value()(uintptr)传递给C
    // C.my_c_function(C.uintptr_t(handle.Value()))
    
    // 使用时从handle恢复
    // recoveredObj := handle.Value().(*MyGoStruct)
}

关键是要确保传递给C代码的指针不包含指向其他Go内存的指针。检查go_ibm_db驱动程序中涉及CGO调用的数据结构,确保所有传递给C的结构体都是"纯"的,不包含切片、映射、函数或其他Go指针。

建议联系go_ibm_db驱动程序的维护者,报告这个Linux特定的CGO指针问题。

回到顶部