英文:
Building and linking dynamically from a go binary
问题
你的问题是:
- 我有一个在一台机器上的Go二进制文件。
- 我需要从该二进制文件中编译一个外部的.go文件。
- 一旦编译完成,我需要将编译好的go文件链接到当前的二进制文件中,以便我可以使用刚刚编译的go代码。
你认为这个可能吗?
我做了一些研究,似乎不太可能,但我可能忽略了一些东西。
谢谢
第一个Go二进制文件可能包含以下内容:
func main() {
// 在这里我需要编译一个包含runFoo()定义的外部go文件(或包)
// 一旦文件/包被编译和链接,我需要调用编译好的代码
runFoo()
// 在这里继续正常执行过程
}
英文:
My problem is the following:
- I have a go binary on a machine
- From that binary I need to compile an external .go file
- Once compiled, I need to link the compiled go file into the current binary so I can use the just-compiled go code.
Do you think that's possible ?
I did a few researches and it does not seem to be possible, but I might have overlooked something.
Thanks
The first go binary would contain something like
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
得分: 29
能够创建共享库的功能将在Go 1.5中推出,预计在2015年8月发布。
根据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程序使用。
注:注意,gccgo
已经有了有限的支持,Go 1.5将是常规go构建工具首次支持此功能的版本。
英文:
The ability to create shared libraries will be in Go 1.5 in August 2015¹.
From "The State of Go" talk by Andrew Gerrand:
> Shared libraries
> ----------------
>
> Go 1.5 can produce Go shared libraries that can be consumed by Go
> programs.
>
> Build the standard library as shared libraries:
>
> $ go install -buildmode=shared std
>
> Build a "Hello, world" program that links against the shared
> libraries:
>
> $ go build -linkshared hello.go
> $ ls -l hello
> -rwxr-xr-x 1 adg adg 13926 May 26 02:13 hello
>
> Go 1.5 can also build Go programs as C archive files (for static
> linking) or shared libraries (for dynamic linking) that can be
> consumed by C programs.
>
> [See:] golang.org/s/execmodes
<sub>¹ Note, gccgo
already had limited support for this for some time, Go 1.5 will be the first time this is supported by the regular go build tools.</sub>
答案2
得分: 16
**更新:**现在在Go的主线版本中可以实现这一点,请参阅Go执行模式。
根据Go 1.5发布说明:
仅适用于amd64架构,编译器有一个新选项-dynlink,通过支持对外部共享库中定义的Go符号的引用来帮助动态链接。
旧答案(有关其他选项的有用讨论):
目前在Go的主线版本中无法创建动态链接库。关于这个问题已经有一些讨论,所以将来可能会看到支持。然而,有一个第三方的Go项目叫做goandroid,它需要与您相同的功能,所以他们维护了一些补丁,应该可以让您打补丁到官方的Go代码库中,以支持您所请求的动态链接支持。
如果您想使用标准的Go运行时,我建议使用以下其中一种方法来与其他程序进行通信:
- 使用管道进行通信
- 使用UNIX域套接字
- 使用映射的共享内存区域
- 也就是说,在/dev/shm上创建一个文件,并让两个程序都将其映射为内存。
- Go的mmap库:https://github.com/edsrzf/mmap-go
每个连续的选项都需要更多的设置工作,更加依赖于平台,但可能比前一个选项更强大。
***注意:**在Windows世界中是DLL文件,在UNIX/Linux世界中是.so文件。
英文:
Update: It is now possible to do this in mainline Go, see Go Execution Modes
From the Go 1.5 release notes:
> For the amd64 architecture only, the compiler has a new option,
> -dynlink, that assists dynamic linking by supporting references to Go symbols defined in external shared libraries.
Old Answer (useful discussion of other options):
It is not currently possible to create dynamically linked libraries* in main line Go. There has been some talk about this, so you may see support in the future. However, there is a 3rd party go project called goandroid that needed the same functionality you need, so they maintain patches that should allow you to patch the official Go code base to support the dynamic linked support you are requesting.
If you want to use a the standard Go run-time, I would recommend the one of the following.
Invoke your Go program from your other program, and communicate using:
- Pipes to communicate
- A UNIX domain socket
- An mmaped region of shared memory.
- That is, create a file on /dev/shm and have both programs mmap it.
- The Go mmap library: https://github.com/edsrzf/mmap-go
Each consecutive option will take more effort to setup, be more platform specific, but potentially be more powerful than the previous one.
*Note: That is, DLLs in the Windows world, and .so files in the UNIX/Linux world.
答案3
得分: 7
我认为go插件也可能与这个问题有关,它们从go版本1.8开始支持。它允许你在运行时动态链接实现所需接口的go二进制文件。
例如,你的代码依赖于一个日志后端,但你希望支持多个后端并在运行时解决它,elasticsearch
和splunk
可能适用于这里。
你可能需要有两个文件:es.go
和splunk.go
,它们都应该包含一个类型为LoggingBackend
的结构体,实现一个方法Write(log string)
。
要创建插件,你需要在编译过程中使用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(mode)
loggingBackend, _ := plug.Lookup("LoggingBackend")
logWriter, _ := loggingBackend.(LoggingBackend)
logWriter.Write("Hello world")
}
英文:
I think go plugins could be also related to this question, they are supported from go version 1.8. It allows you to dynamically link go binaries implementing required interfaces at runtime.
For example your code has a dependency for a logging backend, but you'd like to support several of them and resolve it at runtime, elasticsearch
and splunk
could fit here.
You might need to have 2 files: es.go
and splunk.go
which should both contain a struct of type LoggingBackend
implementing a method Write(log string)
.
To create plugins you need to use buildmode plugin
during compilation:
> go build -buildmode=plugin -o es.so es.go
>
> go build -buildmode=plugin -o splunk.so splunk.go
After that you could pass the needed plugin via command line arguments and load it:
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
得分: 3
这是非常可能的,你甚至可以将其编译为本地共享库。
go build -buildmode=c-shared goc.go
文件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")
}
英文:
This is very possible, you can even compile it as a native shared library
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
like so (tested on 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
得分: 0
1.5版本中承诺的功能
http://talks.golang.org/2015/state-of-go-may.slide#23
英文:
Feature promiced since 1.5
http://talks.golang.org/2015/state-of-go-may.slide#23
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论