Building and linking dynamically from a go binary - go

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
}

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
¹ 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.

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.

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

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

Feature promiced since 1.5 :)
http://talks.golang.org/2015/state-of-go-may.slide#23

Related

Every "go" command leads to a panic from a certain main.go

I've noticed that every go command has stopped working, due to a panic from a main.go in a particular module:
> go env
panic: required key FOO missing value
goroutine 1 [running]:
github.com/kelseyhightower/envconfig.MustProcess(...)
/Users/kurtpeek/go/pkg/mod/github.com/kelseyhightower/envconfig#v1.4.0/envconfig.go:233
main.main()
/Users/kurtpeek/go/pkg/mod/github.com/myorg/mymodule/go#v0.0.0-20210129234103-92f90e2df5c0/main.go:13 +0x314
where the 'offending' main go is similar to
package main
import (
"github.com/kelseyhightower/envconfig"
"github.pie.apple.com/someorg/somemodule/config"
)
func main() {
cfg := &config.Config{}
envconfig.MustProcess("", cfg)
}
I have no idea why a go env command should fail for this reason?
Your module is called github.com/myorg/mymodule/go, which means that the installed binary is called "go" (after the last path segment). This binary likely shadows the go tool depending on how your PATH is configured.
I suggest you change the module path to avoid this problem.
You might try to reinstall go as it seems that somehow the binary for go env command is replaced by a binary you were potentially trying to compile, possible reason could be you built the program in the directory containing the go tools. I recommend reinstalling go

Golang: using Windows 10 API / UWP / System.WindowsRuntime?

Using syscall in Go how can I call the UWP APIs within Windows 10? I have seen and tried many win32 examples, but when I tried using System.WindowsRuntime.dll it was a no-go; specifically, I received:
panic: Failed to load System.WindowsRuntime.dll: The specified module could not be found.
(this was at runtime, the binary built fine)
I tried building both with a standard go build as well as
go build -ldflags="-H windows"
example code:
var(
windowsRuntime = syscall.NewLazyDLL("System.WindowsRuntime.dll")
getDiskFreeSpace = windowsRuntime.NewProc("GetDiskFreeSpace")
)
Note: Other variants tried:
windowsRuntime = syscall.NewLazyDLL("System.Runtime.WindowsRuntime.dll")
&
windowsRuntime = syscall.NewLazyDLL("WindowsRuntime.dll")
Anyone been able to get this running or have any advice on the matter?
As always, greatly appreciated!!
Create a file like this:
//go:generate mkwinsyscall -output zfree.go free.go
//sys getDiskFreeSpace(pathName string, sectorsPerCluster *int, bytesPerSector *int, freeClusters *int, numberOfClusters *int) (err error) = GetDiskFreeSpaceA
package main
func main() {
var bytesPerSector, freeClusters, numberOfClusters, sectorsPerCluster int
getDiskFreeSpace(
`C:\`,
&sectorsPerCluster,
&bytesPerSector,
&freeClusters,
&numberOfClusters,
)
println("bytesPerSector", bytesPerSector)
println("freeClusters", freeClusters)
println("numberOfClusters", numberOfClusters)
println("sectorsPerCluster", sectorsPerCluster)
}
Then build:
go generate
go mod init free
go mod tidy
go build
Result:
bytesPerSector 512
freeClusters 12511186
numberOfClusters 25434879
sectorsPerCluster 8
https://github.com/golang/sys/tree/master/windows/mkwinsyscall
I have no idea where you found "System.WindowsRuntime.dll" because as far as I can tell, it doesn't exist (unless you manually made a DLL named that).
As for System.Runtime.WindowsRuntime.dll: it's part of .NET Framework, not Windows, and it is a managed DLL. You cannot load such DLLs using syscall.NewLazyDLL. What's more, that DLL doesn't really contain any Windows APIs - just glue for .NET to be able to work with them.
You're probably looking for functions like RoGetActivationFactory in combase.dll.

Stringer tool complains about wrong archive header

I am trying to use go generate/stringer (golang.org/x/tools/cmd/stringer) to generate String() methods on enums. I have problems, which I believe, are because of slightly different format of .a packages on different systems. I have this file:
package main
import (
"math/rand"
)
//go:generate stringer -type=Foo
type Foo int;
const (
FooPrime Foo = iota
FooBis
)
func main() {
//Just use rand anywhere, otherwise we get a compiler error
rand.Seed(1)
}
Now if I run go generate example.go on my machine everything is all right: foo_string.go is created. However, on a test machine I get:
stringer: checking package: example.go:4:2: could not import math/rand (reading export data: /usr/lib64/go/pkg/linux_amd64/math/rand.a: go archive is missing __.PKGDEF)
Now, after some digging in the code I think that I get this error, because on my machine rand.a has the following header:
!<arch>
__.PKGDEF 0 0 0 644 2051
`
while on test machine it has the following header:
!<arch>
__.PKGDEF/ 0 399 399 100644 2051
`
I think that the crucial difference is slash after PKGDEFF. gcimporter refuses to process .a file, if it doesn't have __.PKGDEF header.
To check this, I edited manually gcimporter/exportdata.go and changed one of the line from this:
if name != "__.PKGDEF"
to this:
if name != "__.PKGDEF" && name != "__.PKGDEF\"
After this change (and compiling and installing everything) I was able to run go generate on example.go.
My questions are: why do I get this problem and how do I get rid of it (other then manually editing external library)?
What I can see from the spec for openSUSE's packaging they are disabling reinstallation of the standard library at updates. __.PKGDEF is a Go specific informational section, and some linker OpenSUSE has used has simply produced incompatible output.
There's nothing you can do except install a healthy Go from the official source.

Cross compile net/http for distribution

I have build the following code in a docker container with the following architecture:
cat /proc/version
Linux version 3.16.7-tinycore64 (root#064f0e1ce709) (gcc version 4.7.2 (Debian 4.7.2-5) ) #1 SMP Tue Dec 16 23:03:39 UTC 2014
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
The binary distributed, runs with no problem on a busybox container, with the same architecture without installing golang.
The problem
When I do the same for the following code:
package main
import (
"fmt"
"net/http"
)
const (
port = ":80"
)
var calls = 0
func HelloWorld(w http.ResponseWriter, r *http.Request) {
calls++
fmt.Fprintf(w, "Hello, world! You have called me %d times.\n", calls)
}
func init() {
fmt.Printf("Started server at http://localhost%v.\n", port)
http.HandleFunc("/", HelloWorld)
http.ListenAndServe(port, nil)
}
func main() {}
Then I get:
ash: ./hello_world: not found
I might be missing some dependencies - like "net/http"?
But I thought the go build would build all into the binaries.
This is for both go build & go install.
Any idea?
The answer is most probably the one described in this article.
Some critical parts of the standard library use CGO [...] if you cross-compile Go to Darwin or Linux your programs won’t use the system DNS resolver. They also can’t use the native host certificate store. They also can’t look up the user’s home directory, either.
And CGO links against some standard system interfaces by default, dynamically.
The article suggests using gonative to fix the problem. If that's not your cup of tea, some people suggest using:
go build -ldflags "-linkmode external -extldflags -static"
Also read: https://groups.google.com/d/topic/golang-nuts/H-NTwhQVp-8/discussion
I think you need to disable cgo and build with netgo flag :
The net package requires cgo by default because the host operating
system must in general mediate network call setup. On some systems,
though, it is possible to use the network without cgo, and useful to
do so, for instance to avoid dynamic linking. The new build tag netgo
(off by default) allows the construction of a net package in pure Go
on those systems where it is possible.
The netgo tag requires version 1.2 and above.

Why does GDB says "Architecture of file not recognized"?

I m using gdb on a aix shared lib running on aix 5.3?
When i try to run gdb for this file
i get a error message saying ""Architecture of file not recognized"
Don't know how to get this fixed.
Does anybody know why i get this message ""Architecture of file not recognized"?.
gdb runs fine on other executables compiled by xlc.
Is there some option that i might have used while compiling , which is not compatible with GDB.some processor specific option.
I compiled the shared lib w xlc v9.0 for aix.
Thanks.
You don't run GDB on a shared library, you run it on an executable.
If the executable loads your shared library, GDB will know about it.
void
set_gdbarch_from_file (bfd *abfd)
{
struct gdbarch_info info;
struct gdbarch *gdbarch;
gdbarch_info_init (&info);
info.abfd = abfd;
info.target_desc = target_current_description ();
gdbarch = gdbarch_find_by_info (info);
if (gdbarch == NULL)
error (_("Architecture of file not recognized."));
deprecated_current_gdbarch_select_hack (gdbarch);
}
This is the actual GDB code in question (gdb/arch-utils.c:530-544).
The information passed to the gdbarch pointer seems to be invalid. This is caused by gdb_find_by_info returning a NULL pointer and that is caused by find_arch_by_info (gdb/gdbarch.c:3656) returning a NULL pointer.
It basically means what it says: GDB could not identify the architecture of the file. This seems to be a common problem for xlc, even on recent gdb versions.
XLC and gdb are, as far i remember and understand, not very good when it comes down to compatability terms (AIX support is minimal), you might try using the Gnu C Compiler .You might look at the GDB sources for VERY specific information (that i can't really give you).
Here is a link to gcc-AIX specifics.

Resources