Calling functions in an so file from Go
Asked Answered
A

4

13

Is it possible to call a static object (.so) file from Go? I've been searchign Google and I keep hitting upon the claim that I can do

lib, _ := syscall.LoadLibrary("...")

But trying this gives an error

undefined: syscall.LoadLibrary

and searching through Godocs I cannot find reference to this function in the syscall package. Is it possible to load a library and call its functions?

Astra answered 16/12, 2014 at 14:10 Comment(1)
syscall.LoadLibrary is a windows function. Why not just use cgo?Hairraising
S
12

On a POSIX platform, you could use cgo to call dlopen and friends:

// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
import "C"

import fmt

func foo() {
     handle := C.dlopen(C.CString("libfoo.so"), C.RTLD_LAZY)
     bar := C.dlsym(handle, C.CString("bar"))
     fmt.Printf("bar is at %p\n", bar)
}
Slat answered 16/12, 2014 at 17:45 Comment(2)
The bar is a void pointer, how to convert it to function pointer and call it in Go?Rune
To my knowledge the only way to do this is to use a C function (which you can expose to Go using cgo) that accepts the pointer as well as any arguments to the function, casts the pointer to the appropriate function pointer type and then calls it with the arguments it was supplied.Shebeen
E
7

As @JimB said, you should just use CGO, and put the linking to the dynamic/static library there. as per this example:

// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"

...

var x:= C.png_whatever() // whatever the API is

Read more here: http://blog.golang.org/c-go-cgo

Edp answered 16/12, 2014 at 14:17 Comment(3)
Hmm, I'm struggling to see how I would use CGO. But what your saying is, there is no equivalent to Python's (for example) Ctypes module: cdll.LoadLibrary(...) ?Astra
@Caveman you could in theory wrap loading libraries with a CGO module, but why? What's your use case? I don't think Go supports any sort of dynamic linking besides CGO. Why are you struggling to see how you'd use CGO?Edp
is possible to use this without have to include the header file?Gilchrist
H
4

The answer by @Martin Törnwall explains how to use dlopen() for function lookup. Adding this answer to include sample code for how to actually call that function as well. (Using the approach suggested in the comments).

The idea is to write a wrapper function in C language for each function the shared library, which accepts a void* pointer (pointer to the function returned by dlopen()), converts it into an appropriate function pointer, and then call it.

Suppose we have a function named str_length in libfoo.so to calculate the length of a string, then the resulting Go code would be:

package main

import (
    "fmt"
)

/*
#cgo LDFLAGS: -ldl
#include <dlfcn.h>

typedef int (*str_length_type)(char*); // function pointer type

int str_length(void* f, char* s) { // wrapper function
    return ((str_length_type) f)(s);
}
*/
import "C"

func main() {
    handle := C.dlopen(C.CString("libfoo.so"), C.RTLD_LAZY)
    str_length_ptr := C.dlsym(handle, C.CString("str_length"))
    result := C.str_length(str_length_ptr, C.CString("Hello World!"))
    fmt.Println(result) // prints 12
}
Heterolecithal answered 20/10, 2020 at 18:22 Comment(3)
Hmmm. 🤔 How does C.str_length get resolved when in Go you can't have dynamic typing and objects like PHP and C#? Or more specifically, where is C.str_length defined?Shields
@Shields C.str_length is defined in CGO (that is, int str_length(void* f, char* s) function defined in the comments). Any code written inside the comment above import "C" is treated as a C language code, it is parsed by the compiler (NOT ignored like regular comments) and can be accessed using C.<name>.Heterolecithal
Alright. I'll have to look further into that. ThanksShields
D
2

Found using purego much easier, their README example works:

package main

import (
    "fmt"
    "runtime"

    "github.com/ebitengine/purego"
)

func getSystemLibrary() string {
    switch runtime.GOOS {
    case "darwin":
        return "/usr/lib/libSystem.B.dylib"
    case "linux":
        return "libc.so.6"
    default:
        panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
    }
}

func main() {
    libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
    if err != nil {
        panic(err)
    }
    var puts func(string)
    purego.RegisterLibFunc(&puts, libc, "puts")
    puts("Calling C from Go without Cgo!")
}
Delamare answered 22/3, 2023 at 2:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.