cgo result has go pointer - c

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() {}

Related

How to reuse variables in cgo?

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?

How to call C function received as an argument

I want to use Go library with some adjusted in C.
I made GoAdder Go function that having 3 arguments int x, y and function typed f.
And GoAdder function going to call f argument.
adder.go
package main
import "fmt"
import "C"
//export Ftest
type Ftest func(C.int);
//export GoAdder
func GoAdder(x, y int, f Ftest) int {
fmt.Printf("Go says: adding %v and %v\n", x, y)
f(10);
return x + y
}
func main() {} // Required but ignored
And I built go package as a static library named libadder.a in above like this:
go build -buildmode=c-archive -o libadder.a adder.go
And then I have written C++ codes bellow.
main.c
#include <stdio.h>
#include "adder/libadder.h"
void a( GoInt a ){
printf("Hello %d", a);
}
int main() {
printf("C says: about to call Go...\n");
int total = GoAdder(1, 7, &a);
printf("C says: Go calculated our total as %i\n", total);
return 0;
}
I have complied the source like this:
gcc -pthread -o static_go_lib main.c adder/libadder.a
when executing codes above it occurs errors
unexpected fault address 0x0
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x563c99b74244]
goroutine 17 [running, locked to thread]:
...
How to get a correct C function address a in go function GoAdder ?
I referenced https://github.com/draffensperger/go-interlang/tree/master/c_to_go/static_go_lib
C function is just jump pointer, whereas golang's callback are complicate struct, and you cannot convert them.
There's only one (safe) way to call C function pointer:
1) Declare that somewhere:
//go:linkname cgocall runtime.cgocall
//go:nosplit
func cgocall(fn, arg unsafe.Pointer /* may be uintptr */) int32
2) Also, be type safe:
func GoAdder(x, y C.int, f unsafe.Pointer /* don't sure if this available, mb C.uintptr_t */) C.int
3) C function should take pointer (to what ever) as argument
void a(GoInt *a)
(I'd use native types)
4)
ten := 10
cgocall(f, unsafe.Pointer(&ten))
(It should be struct, if you wanna pass several args)

Where do I specify the filename to link to in FFI for Rust? [duplicate]

This question already has answers here:
Using .c source files with Rust
(2 answers)
Closed 3 years ago.
I'm experimenting with FFI on Rust, but I can't find how you tell cargo run where to find your C code, after 2 hours of searching.
I know there's an FFI chapter in the Rust book, but it doesn't state what should I pass to cargo run so that it knows that my C file is located at ./c/main.c.
Rust code:
#[link(name = "main")]
extern {
fn a() -> u8;
}
fn main() {
println!("{}", unsafe {
a()
});
}
C code:
char a() {
return 'A';
}
Do I need to compile the C code to an .o file so that Rust can detect it? Where should I put it if I need to do so? I am on Windows.
I also tried adding a build script that prints cargo:rustc-link-search=./ but that didn't fix it.
The error I get is:
ld: cannot find -lmain
Your Cargo.toml should look like:
[build-dependencies]
cc = "1.0.32"
You should also have a build.rs located in the same folder as Cargo.toml:
extern crate cc;
fn main() {
cc::Build::new()
.file("src/main.c") //here I specify that main.c is in src folder, you can change the location likewise
.compile("libmain.a");
}
Finally, main.rs has:
extern "C" {
fn a() -> u8;
}
fn main() {
println!("{}", unsafe { a() });
}
After this, running cargo run will compile the C code and link it as a library, you might find these examples useful.

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

Go cgo ldap_init could not determine kind of name for C.ldap_init

I was trying to learn and understand cgo.
I am writing a program in Go where I connect to AD and parse the output. I tested the code in C, which is working fine as expected. The relevant part in C is
char *ldap_host = "x.x.x.x";
int ldap_port = 389;
ldap = ldap_init(ldap_host, ldap_port))
Now I am trying to get the same working in go as
//#cgo CFLAGS: -lldap
//#include <ldap.h>
import "C"
func main() {
var hostname *string
hostname = &os.Args[1]
ldap_port := 389
ldap := C.ldap_init(*hostname, ldap_port)
}
But I am getting the following error
could not determine kind of name for C.ldap_init
What am I doing wrong here?
I would start by getting the basics of Go and cgo working before adding the individual peculiarities of a library like ldap. Some good starting points, especially the official cgo doc page.
https://golang.org/cmd/cgo/
http://blog.golang.org/c-go-cgo
https://github.com/golang/go/wiki/cgo
Starting from the top:
You don't need the CFLAGS here, but you will need LDFLAGS for the linker, and liblber for this to run.
#cgo LDFLAGS: -lldap -llber
You can't pass a pointer to a Go string as a C *char, they are different types. Strings in Go actually aren't addressable at all, so this would be a compile error if the build process got that far. Use C.CString, and C.free if you need to create C strings. You will also need to include stdlib.h for C.free.
url := C.CString(os.Args[1])
defer C.free(unsafe.Pointer(url))
Go's int size varies by architecture, and is not C's int type. ldap_port in your example is converted to the default type of int, where you would have needed C.int.
Finally, the original error in your question has nothing to do with Go or cgo. The ldap_init function is deprecated, and does not exist without setting LDAP_DEPRECATED. You should use the ldap_initialize function. Here is a minimal example that compiles:
package main
import (
"log"
"unsafe"
)
/*
#include <stdlib.h>
#include <ldap.h>
#cgo LDFLAGS: -lldap -llber
*/
import "C"
func main() {
url := C.CString("ldap://127.0.0.1:389/")
defer C.free(unsafe.Pointer(url))
var ldap *C.LDAP
rv := C.ldap_initialize(&ldap, url)
if rv != 0 {
log.Fatalf("ldap_initialize() error %d", rv)
}
log.Println("initialized")
}

Resources