【问题标题】:Building and linking dynamically from a go binary从 go 二进制文件动态构建和链接
【发布时间】:2013-10-17 15:44:00
【问题描述】:

我的问题如下:

  1. 我的机器上有一个 go 二进制文件
  2. 我需要从该二进制文件编译一个外部 .go 文件
  3. 编译后,我需要将编译后的 go 文件链接到当前二进制文件中,以便使用刚刚编译的 go 代码。

你认为这可能吗?

我做了一些研究,似乎不可能,但我可能忽略了一些东西。

谢谢:)

第一个 go 二进制文件将包含类似

func main() {
    // Here I need to compile an external go file (or package) which contains
    // The definition of runFoo()

    // Once the file/package is compiled and linked I need to call the compiled code
    runFoo()

    // Continue the execution process normally here
}

【问题讨论】:

  • 解释你想要达到的目标。特别是第 2 步让我感到困惑。
  • 你不能这样做。但是,如果您说明您真正想要做什么,我们可以提供解决方案。
  • 我刚刚在这里创建了一个示例文件:play.golang.org/p/CiAe69vkfR。我希望它能让事情更清楚。
  • Go 目前不支持动态链接。好吧,它可以链接 C .dll/.so 文件,但没有办法动态链接到 Go 二进制文件(或从 Go 源创建 .dll/.so)。我已经看到在 golang-nuts 上完成了一些工作,但我知道它目前还没有准备好发布。
  • 我相信我们在 go-nuts 上读到了相同的主题;)感谢您确认这是不可能的。

标签: go


【解决方案1】:

Go 1.5 将于 2015 年 8 月推出创建共享库的功能¹。

来自 Andrew Gerrand 的“The State of Go”演讲:

共享库

Go 1.5 可以生成可供 Go 使用的 Go 共享库 程序。

将标准库构建为共享库:

$ go install -buildmode=shared std

构建一个“Hello, world”程序,链接到共享 图书馆:

$ go build -linkshared hello.go
$ ls -l hello
-rwxr-xr-x 1 adg adg 13926 May 26 02:13 hello

Go 1.5 还可以将 Go 程序构建为 C 归档文件(用于静态 链接)或共享库(用于动态链接),可以 由 C 程序使用。

[见:]golang.org/s/execmodes

¹ 请注意,gccgo 对此的支持已经有一段时间了,Go 1.5 将是第一次得到常规 go 构建工具的支持。

【讨论】:

    【解决方案2】:

    更新:现在可以在主线 Go 中执行此操作,请参阅 Go Execution Modes

    来自Go 1.5 release notes

    仅对于 amd64 架构,编译器有一个新选项, -dynlink,通过支持对外部共享库中定义的 Go 符号的引用来辅助动态链接。

    旧答案对其他选项的有用讨论):

    目前无法在主线 Go 中创建动态链接库*。已经有一些关于这个的讨论,所以你将来可能会看到支持。但是,有一个名为 goandroid 的第三方 Go 项目需要您需要的相同功能,因此他们维护的补丁应该允许您修补官方 Go 代码库以支持您请求的动态链接支持。

    如果您想使用标准的 Go 运行时,我会推荐以下之一。 从您的其他程序调用您的 Go 程序,并使用以下方式进行通信:

    1. 用于通信的管道
    2. 一个 UNIX 域套接字
    3. 共享内存的映射区域。
      1. 也就是说,在 /dev/shm 上创建一个文件并让两个程序对其进行映射。
      2. Go mmap 库:https://github.com/edsrzf/mmap-go

    每个连续的选项都需要花费更多的精力来设置,更具体的平台,但可能比前一个更强大。

    *注意:即在 Windows 世界中是 DLL,在 UNIX/Linux 世界中是 .so 文件。

    【讨论】:

    • 你能解释一下如何使用-dynlink 吗?我尝试了最新的 golang (1.14),我得到:flag provided but not defined: -dynlink
    【解决方案3】:

    我认为go plugins 也可能与这个问题有关,它们从 go 版本 1.8 开始支持。它允许您在运行时动态链接实现所需接口的 go 二进制文件。

    例如,您的代码依赖于日志记录后端,但您希望支持其中的几个并在运行时解决它,elasticsearchsplunk 可以适合这里。 您可能需要 2 个文件:es.gosplunk.go,它们都应该包含一个类型为 LoggingBackend 的结构,实现方法 Write(log string)

    要创建插件,您需要在编译期间使用 buildmode plugin

    去构建 -buildmode=plugin -o es.so es.go

    去构建 -buildmode=plugin -o splunk.so splunk.go

    之后,您可以通过命令行参数传递所需的插件并加载它:

    package main
    
    import "plugin"
    import "flag"
    
    
    type LoggingBackend interface {
        Write(log string)
    }
    var (
        backend = flag.String("backend", "elasticsearch", "Default logging backend is elasticsearch")
    )
    
    func main() {
        flag.Parse()
        var mode string
        switch backend {
        case "elasticsearch":
            mode = "./es.so"
        case "splunk":
            mode = "./splunk.so"
        default:
            fmt.Println("Didn't recognise your backend")
            os.Exit(1)
        plug, _ := plugin.Open(mod)
        loggingBackend, _ := plug.Lookup("LoggingBackend")
        logWriter, _ := loggingBackend.(LoggingBackend)
        logWriter.Write("Hello world")
    }
    

    【讨论】:

      【解决方案4】:

      这个很有可能,你甚至可以编译成原生共享库

      go build -buildmode=c-shared goc.go 
      
      # file goc
      goc: ELF 32-bit LSB  shared object, ARM, EABI5 version 1 (SYSV),
      dynamically linked, 
      BuildID[sha1]=f841e63ee8e916d7848ac8ee50d9980642b3ad86, 
      not stripped
      

      nm -D --defined-only ./goc | grep "T"

      0004ebe8 T _cgoexp_f88ec80374ab_PrintInt
      000a6178 T _cgo_panic
      0004e954 T _cgo_sys_thread_start
      000a48c8 T _cgo_topofstack
      0004e88c T _cgo_wait_runtime_init_done
      000a61a4 T crosscall2
      0004ebc8 T crosscall_arm1
      0004e7b0 T fatalf
      00102648 T _fini
      0004e544 T _init
      0004e76c T PrintInt
      0004ebe4 T __stack_chk_fail_local
      0004eb5c T x_cgo_free
      0004ea60 T x_cgo_init
      0004eb24 T x_cgo_malloc
      0004e8e0 T x_cgo_notify_runtime_init_done
      0004eb14 T x_cgo_setenv
      0004e820 T x_cgo_sys_thread_create
      0004eb64 T x_cgo_thread_start
      0004eb20 T x_cgo_unsetenv
      

      像这样(在 go 1.5.1 linux/arm 上测试)

      goc.go:

      package main
      
      import (
          "C"
          "fmt"
      )
      
      //export PrintInt
      func PrintInt(x int) {
          fmt.Println(x)
      }
      
      // http://stackoverflow.com/questions/32215509/using-go-code-in-an-existing-c-project
      // go build -buildmode=c-archive goc.go
      // go build -buildmode=c-shared goc.go 
      
      // https://groups.google.com/forum/#!topic/golang-nuts/1oELh6joLQg
      // Trying it on windows/amd64, looks like it isn't supported yet.  Is this planned for the 1.5 release? 
      // It will not be in the 1.5 release.
      // It would be nice if somebody worked on it for 1.6.
      // https://golang.org/s/execmodes
      
      // http://stackoverflow.com/questions/19431296/building-and-linking-dynamically-from-a-go-binary
      // go build -linkshared hello.g
      // go install -buildmode=shared std
      
      
      
      func main() {
          fmt.Println("Hello world")
      }
      

      【讨论】:

        【解决方案5】:

        【讨论】:

        • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。我没有编辑您的答案,而是使用您的链接(但修改为指向正确的幻灯片)作为基础添加了 my own
        猜你喜欢
        • 2015-02-27
        • 1970-01-01
        • 2016-04-22
        • 2018-06-30
        • 1970-01-01
        • 2015-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多