Go語言、動態連結函式庫與它們的產地
前言
最近寫 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.
程式碼 (hello.go)
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
一直無法正常的安裝,最後就沒有往下測了。