从Go中调用so文件中的函数

huangapple go评论91阅读模式
英文:

Calling functions in an so file from Go

问题

从Go中调用静态对象(.so)文件是否可能?
我一直在搜索Google,我一直看到这样的说法,我可以这样做:

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

但是尝试这样做会出现错误:

undefined: syscall.LoadLibrary

并且在Godocs中搜索,我找不到syscall包中对这个函数的引用。
是否可能加载一个库并调用它的函数?

英文:

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?

答案1

得分: 10

在POSIX平台上,您可以使用cgo来调用dlopen和相关函数:

// #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)
}
英文:

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

// #cgo LDFLAGS: -ldl
// #include &lt;dlfcn.h&gt;
import &quot;C&quot;

import fmt

func foo() {
     handle := C.dlopen(C.CString(&quot;libfoo.so&quot;), C.RTLD_LAZY)
     bar := C.dlsym(handle, C.CString(&quot;bar&quot;))
     fmt.Printf(&quot;bar is at %p\n&quot;, bar)
}

答案2

得分: 7

如@JimB所说,你应该使用CGO,并在那里将动态/静态库链接到代码中。示例如下:

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

...

var x := C.png_whatever() // 根据API的具体情况而定

在这里阅读更多信息:http://blog.golang.org/c-go-cgo

英文:

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 &lt;png.h&gt;
import &quot;C&quot;

...

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

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

答案3

得分: 4

答案由@Martin Törnwall解释了如何使用dlopen()进行函数查找。添加此答案以包括如何实际调用该函数的示例代码(使用评论中建议的方法)。

思路是为共享库中的每个函数编写一个C语言的包装函数,该函数接受一个void*指针(由dlopen()返回的函数指针),将其转换为适当的函数指针,然后调用它。

假设我们有一个名为str_length的函数在libfoo.so中用于计算字符串的长度,那么生成的Go代码将如下所示:

package main

import (
    "fmt"
)

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

typedef int (*str_length_type)(char*); // 函数指针类型

int str_length(void* f, char* s) { // 包装函数
    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) // 输出 12
}

希望对你有帮助!

英文:

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 (
    &quot;fmt&quot;
)

/*
#cgo LDFLAGS: -ldl
#include &lt;dlfcn.h&gt;

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 &quot;C&quot;

func main() {
    handle := C.dlopen(C.CString(&quot;libfoo.so&quot;), C.RTLD_LAZY)
    str_length_ptr := C.dlsym(handle, C.CString(&quot;str_length&quot;))
    result := C.str_length(str_length_ptr, C.CString(&quot;Hello World!&quot;))
    fmt.Println(result) // prints 12
}

答案4

得分: 1

使用purego更容易,他们的README示例可行:

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!")
}
英文:

Found using purego much easier, their README example works:

package main

import (
	&quot;fmt&quot;
	&quot;runtime&quot;

	&quot;github.com/ebitengine/purego&quot;
)

func getSystemLibrary() string {
	switch runtime.GOOS {
	case &quot;darwin&quot;:
		return &quot;/usr/lib/libSystem.B.dylib&quot;
	case &quot;linux&quot;:
		return &quot;libc.so.6&quot;
	default:
		panic(fmt.Errorf(&quot;GOOS=%s is not supported&quot;, 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(&amp;puts, libc, &quot;puts&quot;)
	puts(&quot;Calling C from Go without Cgo!&quot;)
}

huangapple
  • 本文由 发表于 2014年12月16日 22:10:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/27506579.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定