前言

最近寫 Go 時想嘗試使用動態連結函式庫。於是乎這一篇就誔生了。 本文會試著使用 Go 編出一個動態連結函式庫,並使用 C 語言程式以及 Go 語言程式呼叫它。

主要內容

動態連結函式庫

官方文件中有提到,可以被呼叫的函式要使用 export 註解 來提示 cgo。

-buildmode=c-shared Build the listed main package, plus all packages it imports, into a C shared library. The only callable symbols will be those functions exported using a cgo //export comment. Requires exactly one main package to be listed.

 1package main
 2
 3import "C"
 4
 5//export hello
 6func hello() {
 7	println("Hello, world! Library")
 8}
 9
10/* 
11   We need the main function to make possible 
12   cgo compiler to compile the package as c shared 
13   library.
14*/
15func main() {}

編譯

1go build -buildmode=c-shared -o libhello.so hello.go

結果

1libhello.h libhello.so

C 語言程式

main.c

1#import <stdio.h>
2#import "libhello.h"
3
4int main(int argc, char *argv[]) {
5	printf("Hello world C !\n");
6	hello();
7	return 0;
8}

編譯

1gcc -o main_c.out -L. -I. -lhello main.c

結果

1./main_c.out
2Hello world C !
3Hello, world! Library

Go 語言程式 v1

main.go

 1package main
 2
 3// #cgo LDFLAGS: -lhello -L.
 4// #include <libhello.h>
 5import "C"
 6import "fmt"
 7
 8func main() {
 9	fmt.Println("Hello world Go!")
10	C.hello()
11}

編譯

1go build -o main_go.out  main.go

Go 語言程式 v2

main_dl.go

 1package main
 2
 3// #cgo LDFLAGS: -ldl
 4// #include <dlfcn.h>
 5import "C"
 6import (
 7	"fmt"
 8	"unsafe"
 9)
10
11// load dynamic library
12func load(path string) (unsafe.Pointer, error) {
13	handle, err := C.dlopen(C.CString(path), C.RTLD_LAZY)
14	if err != nil {
15		return nil, err
16	}
17	return handle, nil
18}
19
20// get symbol 
21func findSym(lib unsafe.Pointer, sym string) (unsafe.Pointer, error) {
22	pc := C.dlsym(lib, C.CString(sym))
23	if pc == nil {
24		return nil, fmt.Errorf("find symbol %s failed", sym)
25	}
26	return pc, nil
27}
28
29// unload dynamic library
30func unload(lib unsafe.Pointer) error {
31	if C.dlclose(lib) != 0 {
32		return fmt.Errorf("unload library failed")
33	}
34	return nil
35}
36
37// entry point function
38func main() {
39	println("Hello World")
40
41	lib, err := load("./libhello.so")
42	if err != nil {
43		panic(err)
44	}
45	defer unload(lib)
46
47	addr, err := findSym(lib, "hello")
48	if err != nil {
49		panic(err)
50	}
51
52	var hello func()
53
54	p := &addr
55	hello = *(*func())(unsafe.Pointer(&p))
56	
57	hello()
58}

編譯

1go build -o main_go_dl.out  main_dl.go

小結

原本也想試試 Node.js 的使用動態連結函式庫的部份。但是相關的函式庫 ffi 一直無法正常的安裝,最後就沒有往下測了。

參考連結