使用Go二进制文件进行动态构建和链接

23

我的问题如下:

  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
}

1
请解释您想要实现的内容。特别是第二步对我来说很困惑。 - fabrizioM
1
你不能这样做。但是如果你解释清楚你真正想做什么,我们可以提供一个解决方案。 - Volker
我在这里创建了一个示例文件:http://play.golang.org/p/CiAe69vkfR。希望它可以使事情更清晰。 - Jérôme R
1
Go目前不支持动态链接。虽然它可以很好地链接C .dll/.so文件,但是没有办法动态链接到Go二进制文件(或从Go源代码创建.dll/.so)。我在golang-nuts上看到了一些相关工作,但我知道目前还没有发布准备好的版本。 - Linear
我相信我们在 go-nuts 上读了同样的帖子 ;) 谢谢你确认这是不可能的。 - Jérôme R
显示剩余2条评论
5个回答

29

在2015年8月,Go 1.5将新增创建共享库的功能¹。

引用Andrew Gerrand的"Go的现状"演讲:

共享库

Go 1.5可以生成可被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构建工具首次支持此功能。


16
更新:现在可以在主流的Go语言中实现这一点,请参见Go执行模式 来自Go 1.5版本发布说明:
对于amd64架构,编译器有一个新选项- dynlink,通过支持引用外部共享库中定义的Go符号来帮助动态链接。
旧答案(其他选项有用的讨论):
目前不可能在主线Go中创建动态链接库。关于这个问题已经有了一些讨论,因此您可能会在未来看到支持。但是,有一个第三方go项目叫做goandroid需要您所需的相同功能,因此他们维护了应该允许您打补丁官方Go代码库以支持您请求的动态链接支持的补丁。
如果您想使用标准的Go运行时,则建议使用以下之一。 从其他程序中调用Go程序,并使用以下方法通信:
1.管道进行通信 2. UNIX域套接字 3.映射的共享内存区域。
也就是说,在/dev/shm上创建一个文件,并将两个程序都映射到它上面。 4. Go的mmap库:https://github.com/edsrzf/mmap-go 每个连续选项将需要更多的设置工作,更具平台特定性,但可能比前一个选项更强大。
注意:即在Windows世界中DLL,在Unix/Linux世界中为.so文件。

1
请问您能否解释一下如何使用-dynlink参数?我在最新的Golang(1.14)上尝试了一下,但是出现了以下错误信息:flag provided but not defined: -dynlink - AK_

7
我认为Go插件也可能与此问题有关,它们从Go版本1.8开始支持。它允许您在运行时动态链接实现所需接口的Go二进制文件。
例如,您的代码依赖于日志记录后端,但您希望支持其中几个并在运行时解决它,elasticsearchsplunk可能适用于此处。 您可能需要有两个文件:es.gosplunk.go,它们都应包含一个实现方法Write(log string)的类型为LoggingBackend的结构体。
要创建插件,您需要在编译期间使用buildmode plugin

go build -buildmode=plugin -o es.so es.go

go build -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")
}

3
这是完全可能的,你甚至可以将其编译为本地共享库。
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)
}

// https://dev59.com/FVwY5IYBdhLWcg3wyaXW
// 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

// https://dev59.com/uWIk5IYBdhLWcg3wRMNR
// go build -linkshared hello.g
// go install -buildmode=shared std



func main() {
    fmt.Println("Hello world")
}

0

2
虽然此链接可能回答了问题,但最好在此处包含答案的必要部分并提供链接以供参考。仅链接的答案可能会因链接页面更改而失效。与其编辑您的答案,我已经添加了我的,使用您的链接(但修改为指向正确的幻灯片)作为基础。 - Dave C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接