How to reuse variables in cgo? - c

I use GO code build a so file, and call it from go. at begin, i want to do some init job, so make sure the go call the shared library can reuse some variable. for example:
main.go
package main
import "C"
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
var (
globalConfig string
)
func main() {
}
//export search
func search() {
fmt.Println(globalConfig)
}
//export start
func start() {
// just keep running to keep globalConfig in RAM
go forever()
quitChannel := make(chan os.Signal, 1)
signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
<-quitChannel
}
func forever() {
v := "global value"
globalConfig = v
for {
time.Sleep(10 * time.Second)
fmt.Println(globalConfig)
}
}
and then generate the shared library.
go build -buildmode=c-shared -o libsdk.so main.go
call the shared lib.
cgo1.go
package main
/*
first step: export LD_LIBRARY_PATH="xxx sopath xxx"
*/
// #include "libsdk.h"
// #cgo LDFLAGS: -L. -lsdk
import "C"
func main() {
C.start()
// C.search()
}
cgo2.go
package main
/*
first step: export LD_LIBRARY_PATH="xxx sopath xxx"
*/
// #include "libsdk.h"
// #cgo LDFLAGS: -L. -lsdk
import "C"
func main() {
// C.start()
C.search()
}
and then the output first get the global value, the the second is null.
shouldn't shared lib share memory?

Related

how can use golang c-shared library in go with .h and .so file?

I had try call so file by define c code in same go file, It works!
but when I want use .h and .so file to do it. It not work.
this is my code:
sdk.go
package main
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
import "C"
func main() {
}
//export query
func query(info *C.char) *C.char {
GoStr := C.GoString(info)
return C.CString(GoStr)
}
i generate the so file with command:
go build -buildmode=c-shared -o sdk.so sdk.go
now I want to call it:
package main
/*
#cgo LDFLAGS: -L../lib -lsdk
#include "sdk.h"
*/
import "C"
import "fmt"
func main() {
val := C.query(C.CString("123"))
fmt.Println(val)
}
but some err occured,
Starting: /home/go/bin/dlv dap --check-go-version=false --listen=127.0.0.1:45412 --log-dest=3 from /root/goProject/src/cgo/src
DAP server listening at: 127.0.0.1:45412
Build Error: go build -o /root/goProject/src/cgo/src/__debug_bin -gcflags all=-N -l .
# CGO/src
./main.go:11:9: could not determine kind of name for C.query
cgo:
gcc errors for preamble:
In file included from ./main.go:5:0:
cgo-gcc-export-header-prolog:49:17: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'query' (exit status 2)
I think this may because the sdk.h format.
it looks like https://go.dev/play/p/rxlUp16s-u9
any thoughts or suggestions are appreciated...
last
i think the problem is can't receive go string from so file, so i change the data type from go source file, string to *C.char, and everything is ok now.

cgo result has go pointer

I am writing some go code that exports a function like that:
package main
import "C"
//export returnString
func returnString() string {
//
gostring := "hello world"
return gostring
}
func main() {}
I build the .so and the header file by using go build -buildmode=c-shared, but when I call returnString() in my C code, I get panic: runtime error: cgo result has Go pointer
Is there a way to to this in go 1.9?
You need to convert your go string to *C.char. C.Cstring is utility function for that.
package main
import "C"
//export returnString
func returnString() *C.char {
gostring := "hello world"
return C.CString(gostring)
}
func main() {}

Golang C (.so) imports segmentation violation on call

Hello I'm going to work with thirdparty library (.so file) with golang in linux environment. So I tried to practice a bit with something trivial, like importing functions from linux native libs. And got stuck on importing and calling sqrt function. Here is my code:
package main
// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <stdio.h>
import "C"
import "fmt"
func main() {
export_name := "sqrt"
lib_path := "/lib/libm.so.6"
//Loading .so
handle := C.dlopen(C.CString(lib_path), C.RTLD_LAZY)
if handle == nil {
fmt.Println(lib_path+":\tNOT FOUND")
return
} else {
fmt.Println(lib_path+":\tSUCCESS")
}
//looking for function address
func_pointer := C.dlsym(handle, C.CString(export_name ))
if func_pointer == nil {
fmt.Println(export_name+":\tNOT FOUND")
return
} else {
fmt.Println(export_name+":\t", func_pointer)
}
//negotiating datatypes
//From c lib description: double sqrt(double x);
sqrt := *(*(func(float64)float64))(func_pointer)
//Calling function
sqrt(4)
}
when I run it, I always get segmentation violation:
/lib/libm.so.6: SUCCESS
sqrt: 0x7f37117ea270
unexpected fault address 0x0
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x4019fa]
goroutine 1 [running]:
runtime.throw(0x4a6643, 0x5)
/usr/lib/go/src/runtime/panic.go:566 +0x95 fp=0xc42004be00 sp=0xc42004bde0
runtime.sigpanic()
/usr/lib/go/src/runtime/sigpanic_unix.go:27 +0x288 fp=0xc42004be58 sp=0xc42004be00
main.main()
/home/afx/goc/so.go:37 +0x2ba fp=0xc42004bf48 sp=0xc42004be58
runtime.main()
/usr/lib/go/src/runtime/proc.go:183 +0x1f4 fp=0xc42004bfa0 sp=0xc42004bf48
runtime.goexit()
/usr/lib/go/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc42004bfa8 sp=0xc42004bfa0
goroutine 17 [syscall, locked to thread]:
runtime.goexit()
/usr/lib/go/src/runtime/asm_amd64.s:2086 +0x1
exit status 2
What is the problem ?
Thank you in advance.
P.S.
When I'm redefining function pointers of native Go functions (like here Go: convert unsafe.Pointer to function pointer and vice versa) everything works fine. But import fails.
Here is solution. I had to use bridge C function:
package main
// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <stdio.h>
//
// double
// my_sqrt_bridge(void *f, double x)
// {
// //description: ((return_data_type (*)(input_data_type))bridge_input_function_pointer) (bridge_input_value)
// return ((double (*)(double))f)(x);
// }
import "C"
import "fmt"
func main() {
export_name := "sqrt"
lib_path := "/lib/libm.so.6"
//Loading .so
handle := C.dlopen(C.CString(lib_path), C.RTLD_LAZY)
if handle == nil {
fmt.Println(lib_path + ":\tNOT FOUND")
return
} else {
fmt.Println(lib_path + ":\tSUCCESS")
}
//looking for function address
func_pointer := C.dlsym(handle, C.CString(export_name))
if func_pointer == nil {
fmt.Println(export_name + ":\tNOT FOUND")
return
} else {
fmt.Println(export_name+":\t", func_pointer)
}
fmt.Printf("%f", C.my_sqrt_bridge(func_pointer, 2))
}
Would it be possible in your case to let cgo link the library for you? Eg:
package main
/*
#cgo LDFLAGS: -lm
#include <math.h>
*/
import "C"
import "fmt"
func main() {
fmt.Printf("%g\n", C.sqrt(2)) //prints 1.4142135623730951
}
For a third party library /some/lib/dir/libxxx.so:
LDFLAGS: -L/some/lib/dir -lxxx

golang cgo check if C function exists

I'm linking a library via CGO, and not all implementations or versions have a feature which I'd like to utilize if possible — namely the presence of a function int feature(void). Is there a way I can check if this symbol is defined before attempting a call?
Any attempted use of C.feature() unsurprisingly results in a build failure on systems with a version of the library that doesn't support the feature.
In case it isn't clear, I want to build against many platforms, which may or may not have the feature. I imagine I'd either need to be able to check if a function exists at runtime (more ideal) or use go:generate to do a check and change the code depending on what it finds (less ideal). Either way, I'm not too sure how exactly to proceed.
Most libraries come with a #define that describes its version.
For example, in version 1.0:
// simple.h
#define LIB_SIMPLE_VERSION 0x00010000
void hello();
In version 1.1:
// simple.h
#define LIB_SIMPLE_VERSION 0x00010001
void hello();
void bye();
You have many choices:
Provide an IsByeAvailable() function to check the existence
Modify the signature of bye() in Go such that it returns an error, or a bool for ok
Panic if the implementation doesn't exist
Code for Choice 1
// simple.go
// #include <simple.h>
import "C"
func IsByeAvailable() bool {
return C.LIB_SIMPLE_VERSION >= 0x00010001
}
func Hello() {
C.hello()
}
func Bye() {
C.bye()
}
// version_support.c
#include <simple.h>
#if LIB_SIMPLE_VERSION < 0x00010001
void bye() { /* Empty */ }
#endif
There are several ways to do that when building your program, but that will leave you with a need to always make two builds and maintain two versions of your program, which is not convenient. In runtime you're very limited because C itself is not reflective and Go runtime features don't (actually can't) give you any benefits.
Still, there are two non-portable (but most probably good enough) hacks you can do. And as the problem is more of a C problem, the hacks are also more of C hacks. One is direct dynamic linker interface, namely dlopen()/dlsym() and the other one is usage of weak symbols in dynamic linker.
Let's first create some setup to test things out:
$ tree
.
├── lib
│   ├── lib1.c
│   ├── lib1.h
│   └── lib2.c
└── some.go
$ cat lib/lib1.h
int feature(void);
int fun(int a);
$ cat lib/lib1.c
int feature(void)
{
return 5;
}
int fun(int a)
{
return a*a;
}
$ cat lib/lib2.c
int fun(int a)
{
return a*a;
}
That's a very simple library that declares two functions and has two implementations, one has both, the other just one. Building them is easy:
$ gcc -shared -o lib/lib1.so.featured lib/lib1.c
$ gcc -shared -o lib/lib1.so.featureless lib/lib2.c
And a symlink to easily switch between two versions:
$ ln -s lib1.so.featured lib/lib1.so
So, for the dlopen()/dlsym() you create a wrapper and use dynamic linker interface to get the feature() pointer like this (yes, you can do that without calling dlsym() on every call, but let's use the very minimum code):
package main;
// #cgo LDFLAGS: -Llib -Wl,-rpath lib -l1 -ldl
// #include "lib/lib1.h"
// #include <stddef.h>
// #include <dlfcn.h>
//
// int feature_wrap(void)
// {
// static void* dlhandle;
// static int (*featurep)(void);
//
// if (!dlhandle)
// dlhandle = dlopen(NULL, RTLD_NOW);
// if (!dlhandle) // error
// return 0;
// featurep = dlsym(dlhandle, "feature");
// if (featurep)
// return featurep();
// else
// return 3;
//}
import "C"
import "fmt"
func main() {
r := C.feature_wrap()
fmt.Println(r)
}
Testing:
$ go build some.go
$ ln -sf lib1.so.featured lib/lib1.so
$ ./some
5
$ ln -sf lib1.so.featureless lib/lib1.so
$ ./some
3
For the weak symbol approach (that is preferrable IMO as it is simpler) you need to redefine you feature() function as weak and also create a wrapper for it that will provide runtime switch between two implementations:
package main;
// #cgo LDFLAGS: -Llib -Wl,-rpath lib -l1
// #include "lib/lib1.h"
// int feature(void) __attribute__((weak));
// int feature_wrap(void)
// {
// if (feature)
// return feature();
// else
// return 3;
//}
import "C"
import "fmt"
func main() {
r := C.feature_wrap()
fmt.Println(r)
}
Testing is the same.
Obviously, once you have proper if, you can do whatever you need to substitute missing feature() including callbacks to Go code.

How to get from cgo to exe

From a basic test program. . .
package main
/*
#include <stdio.h>
static void test() {
printf("hello world");
}
*/
import "C"
func main() {
C.test();
}
I do "cgo hello_cgo.go" and get:
_cgo_.o
_cgo_defun.c
_cgo_gotypes.go
hello_cgo.cgo1.go
hello_cgo.cgo2.c
How do I go about compiling from here to an exe?
Try using the go makefiles. Create a makefile like
# Makefile
CGOFILES=test.go
TARG=test
include $(GOROOT)/src/Make.$(GOARCH)
include $(GOROOT)/src/Make.pkg
Running make will then produce the file _obj/test.a, which you'll have to link with 6l or similar.
Update for go1:
$ cat foo.go
package main
// #include <stdio.h>
// static void test() { printf("Hello, world\n"); }
import "C"
func main() {
C.test()
}
$ go build foo.go
$ ./foo
Hello, world

Resources