How to call shared object function (loaded dynamically) from pointer in Go?

I try to call few shared object functions from Go. I don’t want to write a C commented code to build a CGO interface for all functions.

I write my shared object like that:

#include <stdio.h>

void greet(char* name) {
    printf("Hello, %s!\n", name);
}

I compile it with: gcc -shared -fPIC -o libgreet.so greet.c.

My Go code to call the greet function:

package main

// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <stdlib.h>
import "C"
import (
    "unsafe"
    "fmt"
)

func main() {
    so_name := C.CString("./libgreet.so")
    defer C.free(unsafe.Pointer(so_name))
    function_name := C.CString("greet")
    defer C.free(unsafe.Pointer(function_name))

    library := C.dlopen(so_name, C.RTLD_LAZY)
    defer C.dlclose(library)
    function := C.dlsym(library, function_name)

    greet := (*func(*C.char))(unsafe.Pointer(&function))

    fmt.Println("%p %p %p %p\n", greet, function, unsafe.Pointer(function), unsafe.Pointer(&function))
    (*greet)(C.CString("Bob"))
}

When i start the executable i get SIGSEGV errors.

I try to debug it with gdb and pointers printed seems to be good (that the same value than x greet in gdb). The segmentation fault come on the instruction 0x47dde4 <main.main+548> call r8 where $r8 contains 0x10ec8348e5894855 (probably not a memory address).

Do you have any idea how i can fix this error ? There is a soluce to call shared object function in Go without a C code commented as the CGO syntax (i don’t find any documentation to do it) ?

Possibly this fragment from go doc cmd/cgo is helpful?

Calling C function pointers is currently not supported, however you can declare
Go variables which hold C function pointers and pass them back and forth between
Go and C. C code may call function pointers received from Go. For example:

    package main

    // typedef int (*intFunc) ();
    //
    // int
    // bridge_int_func(intFunc f)
    // {
    //		return f();
    // }
    //
    // int fortytwo()
    // {
    //	    return 42;
    // }
    import "C"
    import "fmt"

    func main() {
    	f := C.intFunc(C.fortytwo)
    	fmt.Println(int(C.bridge_int_func(f)))
    	// Output: 42
    }

I’m trying to implement a solution myself but it seems I have to refresh (or add to) my knowledge of function pointers in C.

But I guess this solution doesn’t help your original problem, i.e. avoiding to write commented wrapper-code for the lib you want to use.
Or are you using the dlopen-interface because you want a plugin-style usage of shared libraries (loading + unloading at runtime)?

Thanks for your response !

Your answer works well but is not what I want… I’m using a SharedObject with a lot of functions and the CGO syntax is not easy to read and maintain because it’s a comment without syntax highlighting and it’s a Copy and paste from SharedObject signatures, so I’d have to change it every time I update the SharedObject.

If you managed to get the example working, I’d like to see the code, if possible.

I can think of two more options that might work?

  1. just using LDFLAGS: -lgreet so not using the dynamic loading at all (rather dynamic linking). Or is this too obvious/am I missing something?
  2. the modernc-project can transpile C into Go-code. I never tried it directly, only the transpiled sqlite-code from the same author. https://gitlab.com/cznic/ccgo

Yes, sure. I write a POC in this repository: https://github.com/mauricelambert/TerminalMessages/blob/main/GoDemonstrationLinux.go

This library is small but i would like to start a bigger project with lot of function in my library.

1 Like

Thank you ! The first solution is not possible, I have to start the executable with the library dynamically loaded because there are multiple paths for this library and because it should not be in the prerequisites to run the executable.

I will test the second option.

#1: different lib pathes (on the target system) aren’t the big deal, actually that’s pretty common - see ld.conf, rpath, …

#2: dlopen() + friends should always done with great care - even in C. you really need to make sure your function pointers really match the prototype. might look trivial, but can easily have lots of pitfalls, eg. arch specific calling conventions - your code needs to explicitly take care of that !

#3: if it really needs to be plugin-kind, you could try putting the binding into a golang plugin, which then directly links against that external library

#4: depending on your actual use case, it could make sense putting the code using the external library into a separate program/service.

Thanks for your response !

I should load my library dynamically because it’s like an optional plugin, my project will offer many features but each feature will be implemented in a DLL/SO and users will choose what they want. My library therefore needs to be loaded dynamically so as not to be a necessary condition for the main executable to run.

I don’t know what is a golang plugin i’m going to find out more about it.

I don’t want to start another process for each of my features so the last solution cannot be used in my project.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.