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

gccgo编译的Go共享库能否用dlsym查找并调用符号?

问题描述

我尝试用-buildmode=plugin编译Go插件,但生成的.so文件体积过大,哪怕是如下简单代码,生成的文件也接近1MB:

package main

var URI string

func Eval() bool {
  return (URI == "Yes")
}

于是我改用gccgo编译,执行命令:

gccgo -o test.so -fPIC -shared test.go 

生成的文件体积很小,仅约16KB。

现在的问题是:能否用dlsym查找上述.so文件中的符号?dlsym似乎只能检索C符号而非Go符号,有没有办法检索并设置Go符号?

更新内容:nm输出

U __gcc_personality_v0
                 U getenv
                 U getpagesize
                 w __gmon_start__
0000000000002eb3 T __go_init_main
                 U internal..z2fbytealg..types
                 U internal..z2fcpu..import
                 U internal..z2fcpu..types
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
00000000000022d9 T main.Eval
0000000000006d20 D main.Eval..f
00000000000024bd T main.struct.4runtime.gList.2.runtime.n.0int32.5..eq
0000000000006d28 D main.struct.4runtime.gList.2.runtime.n.0int32.5..eq..f
0000000000002760 T main.struct.4Size.0uint32.2Mallocs.0uint64.2Frees.0uint64.5..eq
0000000000006d30 D main.struct.4Size.0uint32.2Mallocs.0uint64.2Frees.0uint64.5..eq..f
0000000000006d40 D main..types
0000000000007150 D main.URI
                 U malloc
                 U memcmp
00000000000000b8 B __morestack_current_segment
0000000000000000 B __morestack_initial_sp
00000000000000c0 B __morestack_segments
                 U protect

函数调用代码

sym := s
eval, err := h.GetSymbolPointer(sym)
if err != nil {
    fmt.Printf("Didn't find it ")
    return -1, fmt.Errorf("Unable to find symbol")
}
x := (*func() bool)(eval)
test := (*x)()         //此处直接触发panic
_ = test

return 0, nil

解决方案

1. 用dlsym检索gccgo生成的Go符号是可行的

从你的nm输出可以看到,Go的函数和变量已经以main.Eval、main.URI这样的符号名暴露出来了,这些符号可以直接被dlsym检索到——gccgo生成的符号名符合C链接规范,不需要额外处理符号名转义。

2. 调用Go函数触发panic的原因及解决

直接把符号指针强转为*func() bool后调用会panic,核心原因是gccgo生成的Go函数调用约定和标准Go runtime的调用约定不同,不能直接用Go函数类型强转调用。

推荐两种解决方法:

  • 方法一:给Go函数加//export注解,强制gccgo按C调用约定生成函数
    修改插件代码:

    package main
    
    import "C"
    
    var URI string
    
    //export Eval
    func Eval() bool {
      return URI == "Yes"
    }
    
    func main() {} // gccgo编译共享库需要main函数作为入口标记
    

    重新编译:

    gccgo -o test.so -fPIC -shared test.go
    

    此时可以把符号指针转为C函数类型,通过cgo桥接或遵循C调用约定的方式调用。

  • 方法二:手动适配gccgo函数调用约定(不推荐)
    gccgo生成的函数第一个参数是Go上下文指针(runtime.g),调用时需要传入合法上下文,但这种方法依赖gccgo内部实现,兼容性极差。

3. 设置Go全局变量main.URI的方法

从nm输出看main.URI是全局数据符号(标记为D),可直接通过dlsym获取指针后修改内存:

// 获取URI变量的指针
uriPtr, err := h.GetSymbolPointer("main.URI")
if err != nil {
    // 错误处理逻辑
}
// Go的string是结构体,需构造合法的Go string后写入内存
targetStr := "Yes"
*(*string)(uriPtr) = targetStr

额外注意事项

  • gccgo编译共享库时需要显式的空main函数,否则无法正确初始化Go runtime。
  • 调用Go函数或访问变量前,必须先调用__go_init_main符号完成runtime初始化,否则会直接崩溃。

内容的提问来源于stack exchange,提问作者kgunjikar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 05:27:32