如何在Go中通过指针调用动态加载的共享对象函数?
问题描述
尝试在Go中调用共享对象函数,但不想为所有函数编写带注释的C代码构建CGO接口。
共享对象C代码
#include <stdio.h> void greet(char* name) { printf("Hello, %s!\n", name); }
编译命令:
gcc -shared -fPIC -o libgreet.so greet.c
Go调用代码
package main // #cgo LDFLAGS: -ldl // #include <dlfcn.h> // #include <stdlib.h> import "C" import ( "unsafe" "fmt" ) func main() { so_name := C.CString("./libgreet.so") defer C.free(unsafe.Pointer(so_name)) function_name := C.CString("greet") defer C.free(unsafe.Pointer(function_name)) library := C.dlopen(so_name, C.RTLD_LAZY) defer C.dlclose(library) function := C.dlsym(library, function_name) greet := (*func(*C.char))(unsafe.Pointer(&function)) fmt.Printf("%p %p %p %p\n", greet, function, unsafe.Pointer(function), unsafe.Pointer(&function)) (*greet)(C.CString("Bob")) }
错误现象
运行可执行文件时触发SIGSEGV错误,用gdb调试发现段错误发生在指令0x47dde4 <main.main+548> call r8处,$r8的值为0x10ec8348e5894855(无效内存地址)。
解决方案
1. 修复当前代码的段错误问题
代码核心问题是函数指针类型转换逻辑错误:C.dlsym返回的是直接指向函数的指针,你错误地取了function变量的地址进行转换,导致最终调用的是存储函数地址的变量内存,而非函数本身。
修正后的关键代码
// 错误写法:取function变量的地址,指向的是存储函数地址的内存 // greet := (*func(*C.char))(unsafe.Pointer(&function)) // 正确写法:直接将function转换为目标函数指针类型 greet := (*func(*C.char))(unsafe.Pointer(function))
同时补充内存释放和错误检查,完整代码如下:
package main // #cgo LDFLAGS: -ldl // #include <dlfcn.h> // #include <stdlib.h> import "C" import ( "unsafe" "fmt" ) func main() { so_name := C.CString("./libgreet.so") defer C.free(unsafe.Pointer(so_name)) function_name := C.CString("greet") defer C.free(unsafe.Pointer(function_name)) library := C.dlopen(so_name, C.RTLD_LAZY) if library == nil { fmt.Printf("dlopen failed: %s\n", C.GoString(C.dlerror())) return } defer C.dlclose(library) function := C.dlsym(library, function_name) if function == nil { fmt.Printf("dlsym failed: %s\n", C.GoString(C.dlerror())) return } greet := (*func(*C.char))(unsafe.Pointer(function)) bobName := C.CString("Bob") defer C.free(unsafe.Pointer(bobName)) greet(bobName) }
2. 无需编写CGO注释C代码的方案
可以使用纯Go实现的动态链接库调用库,完全避免编写CGO相关的C代码,比如purego:
示例代码
package main import ( "fmt" "github.com/ebitengine/purego" ) func main() { lib, err := purego.Dlopen("./libgreet.so", purego.RTLD_LAZY) if err != nil { panic(err) } defer purego.Dlclose(lib) var greet func(name uintptr) purego.RegisterLibFunc(&greet, lib, "greet") name := purego.String("Bob") defer purego.Free(name) greet(name) }
该方案通过纯Go代码完成动态库加载、函数注册与调用,无需任何CGO注释代码。
内容的提问来源于stack exchange,提问作者MauriceLambert
相关产品推荐
相关产品推荐

