Can Go executable be used as a dynamic library? - go

I am writing a generic library in GoLang and want to publish it (like a dynamic library) to be used by other apps written in any language.
If I write this lib in C/C++, I would have generated a .dll or .so file which can be imported and used in any other language. How can I do this in GoLang?
If I just generate a Go executable, can I use it instead of a dynamic library?

You can build a C-shared library in Go, this will produce a regular .dll or .so with exported functions compatible with the C calling convention, so that they can be invoked from other languages.
Compile with go build -buildmode=c-shared.
See go build command - Build modes
For example:
src/go/main.go:
package main
import "C"
import "fmt"
//export helloLib
func helloLib(x C.int) {
fmt.Printf("Hello from Go! x=%d\n", x)
}
func main() {}
src/c/main.c:
void helloLib(int);
int main() {
helloLib(12345);
}
Building and running:
$ go build -buildmode=c-shared -o libmy.so ./src/go/
$ gcc -o test src/c/main.c libmy.so
$ ./test
Hello from Go! x=12345
$

I believe it is possible using cgo:
https://pkg.go.dev/cmd/cgo
It is stated that Go functions can be exported for use by C code.

Related

How to resolve circular dependencies when using go modules and cgo

In my project, I am using callbacks for bi-directional calls from C into go and vice versa using CGO. I resolved the issue of circular dependencies by compiling the C part into a library, then compiling the go part into a library, then a final linker pass puts it all together. This is working fine when not using go modules. Go source files are listed on the command line explicitly. I have been told that as of go 1.12 "this is not the right way to do it".
As the project has grown, I now want to use go modules. Unfortunately, this changes the behaviour of the go compiler. It now wants to resolve external dependencies and implicitly includes them in the output file. Due to the circular dependency, it now always ends up with an undefined reference or multiple definitions. How to resolve circular dependencies when using cgo and go modules "the right way"?
This is a minimal example to illustrate the problem. Remove the file-name "hello.go" from the call to go in the Makefile to see how it falls apart.
This is the error message:
hello.c:3: multiple definition of `c_hello'; $WORK/b001/_cgo_hello.o:/tmp/go-build/hello.c:3: first defined here
Makefile:
libchello.a: Makefile hello.c
gcc -fPIC -c -o chello.o hello.c
ar r libchello.a chello.o
libgohello.a: Makefile hello.go libchello.a
env CGO_LDFLAGS=libchello.a go build -buildmode=c-archive -o libgohello.a hello.go
main: Makefile main.c libgohello.a libchello.a
gcc -o main main.c libchello.a libgohello.a -pthread
.PHONY: clean
clean:
rm -f main *.a *.o
echo "extern void go_hello();" > libgohello.h
hello.go:
package main
/*
extern void c_hello();
*/
import "C"
import "time"
import "fmt"
//export go_hello
func go_hello() {
fmt.Printf("Hello from go\n")
time.Sleep(1 * time.Second)
C.c_hello()
}
func main() {}
libgohello.h:
extern void go_hello();
hello.c:
#include "libgohello.h"
#include <stdio.h>
void c_hello() {
printf("Hello from c\n");
go_hello();
}
main.c:
void c_hello();
int main() {
c_hello();
}
go.mod:
module hehoe.de/cgocircular
If you look at the verbose output from the go build command, you will see that when compiling the directory as a complete go package, the main.c file is being included as part of the C code used in hello.go.
From the documentation:
When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile them as part of the Go package
The easiest solution here is to separate the main C and Go packages, so that they don't interfere with each other's build process. Testing this out, removing the main.c file will build libchello.a and libgohello.a, and then adding it back in will complete the build of main.

How can I build a dll with cgo and .def file

I want to implement dll forwarding(or dll proxy)
In mingw, I can use cpp source with .def file, like gcc -shared -o test.dll functions.def test.cpp
But I don't know how to implement it in golang
I know what to do to solve this problem.
use the extldflags
First Way:
compile the .def file to .exp file
dlltool --def functions.def --output-exp evildll.exp
compile main.go
main.go
package main
import "C"
func main() {
// Need a main function to make CGO compile package as C shared library
}
use command go build -buildmode=c-shared -o exportgo.dll -ldflags="-extldflags=-Wl,C:/Users/Akkuman/Desktop/go-dll-proxy/evildll.exp"
then you can get the dll
Second Way
use command go build -buildmode=c-shared -o exportgo.dll -ldflags="-extldflags=-Wl,C:/Users/Akkuman/Desktop/go-dll-proxy/functions.def"
then you can get the dll, but with a additional export function

Import path difference using "build" and "tool compile"

I'm trying to compile this example (which is saved as main.go in current working dir):
package main
import (
"flag"
"log"
"runtime"
"github.com/nats-io/go-nats"
)
// rest of the code
This works:
$ go build ./
But this does not:
$ go tool compile -o main.o main.go
main.go:8:2: can't find import: "github.com/nats-io/go-nats"
Both of examples above were run on the same terminal with same environment variables, so I don't understand why the 2nd one does not work. I've tried -D and -I parameters with various directories as $GOPATH, $GOPATH/src and so on with no success.
I know what it's considered best practice to not use go tool compile and so on, but my goal is to add my go source to existing C++ makefile project and useage of go tool will make it more consistent.
Correct syntax of go tool compile is as follows:
go tool compile -I $GOPATH/pkg/linux_amd64 -o main.o main.go
The problem was that by default, compile performs lookup only in $GOROOT and ignores $GOPATH.

Setting CGO_CFLAGS persistently in a Go package?

My Go package includes a .c file that uses a library which needs certain CFLAGS set. On the command line to "go install" I can specify CGO_CFLAGS with the needed flags, and everything works. However, I would like to make it so that someone can "go get" my package and build it without passing any extra command line arguments.
Does the Go packaging system provide a place where I could put some config like this, to specify some arguments that are always needed when go installing a package?
(I'm aware of doing #cgo CFLAGS: directives in Go source files, but recall that in my package I have a .c source file so need the CGO_CFLAGS setting to the overall build process)
cgo extracts your #cgo CFLAGS: to an environment variable during build (pass "-x" flag to go build). If you go install -x you see that it honors your cflags/ldflags specified in your Go library. In other words, it should work to just specify them in your Go files.
In order to use any of your C functions you still have to mix some cgo into your .go files, in those files just declare your flags, for example:
test.go:
package main
import "fmt"
/*
#cgo CFLAGS: -DTEST
#include <stdio.h>
extern void ACFunction();
*/
import "C"
//export AGoFunction
func AGoFunction() {
fmt.Println("AGoFunction()")
}
func main() {
C.ACFunction()
}
test.c:
#include "_cgo_export.h"
void ACFunction() {
#ifdef TEST
printf("ACFunction()\n");
#endif
AGoFunction();
}
Putting these in the same directory will make go build pickup the flags defined in test.go and apply them when building test.c.

How to link go package in Xcode CoreFoundation project?

My goal is to be able to call Go functions from a Cocoa project but I just started with a pure C CoreFoundation project.
Here is my simple go package:
package hello
import "C"
import (
"fmt"
)
//export SayHello
func SayHello() {
fmt.Println("Hello, World!")
}
I build this using go install which generates the lib hello.a.
I want to be able to link this library to my CoreFoundation project so I can call SayHello from my C code.
Doing this causes Xcode to show a warning stating that hello.a was ignored because it wasn't build for the X86_64 architecture.
I can tell that the issue most likely is due to the fact that the way the Go code was compiled is not compatible with the way XCode is compiling the CoreFoundation project.
Therefore my question is: Is it possible to somehow compile my Go package in a way which is linkable with my CoreFoundation project?
You can't link a Go library into a C program. The *.a archives that go outputs are not the same format as C object files so the C compiler won't know how to link them in.
The *.a files folow the format described here: http://golang.org/cmd/pack/ and here: http://plan9.bell-labs.com/magic/man2html/1/ar
CGO allows C to call go functions and vice versa but That will require the main app to be a Go binary not a C binary in order for the linking to work correctly.

Resources