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
更多关于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指针。以下是几种处理方式:
- 使用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), ...)
}
- 使用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)
}
- 修改现有的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))
}
- 使用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指针问题。

