You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:55:28