Yuan のノート
心之所至,隨意亂書
Go語言、動態連結函式庫與它們的產地
Thu Jul 21, 2022
🏷️

前言

最近寫 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "C"

//export hello
func hello() {
	println("Hello, world! Library")
}

/* 
   We need the main function to make possible 
   cgo compiler to compile the package as c shared 
   library.
*/
func main() {}

編譯

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

結果

1
libhello.h libhello.so

C 語言程式

main.c

1
2
3
4
5
6
7
8
#import <stdio.h>
#import "libhello.h"

int main(int argc, char *argv[]) {
	printf("Hello world C !\n");
	hello();
	return 0;
}

編譯

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

結果

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

Go 語言程式 v1

main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

// #cgo LDFLAGS: -lhello -L.
// #include <libhello.h>
import "C"
import "fmt"

func main() {
	fmt.Println("Hello world Go!")
	C.hello()
}

編譯

1
go build -o main_go.out  main.go

Go 語言程式 v2

main_dl.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package main

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

// load dynamic library
func load(path string) (unsafe.Pointer, error) {
	handle, err := C.dlopen(C.CString(path), C.RTLD_LAZY)
	if err != nil {
		return nil, err
	}
	return handle, nil
}

// get symbol 
func findSym(lib unsafe.Pointer, sym string) (unsafe.Pointer, error) {
	pc := C.dlsym(lib, C.CString(sym))
	if pc == nil {
		return nil, fmt.Errorf("find symbol %s failed", sym)
	}
	return pc, nil
}

// unload dynamic library
func unload(lib unsafe.Pointer) error {
	if C.dlclose(lib) != 0 {
		return fmt.Errorf("unload library failed")
	}
	return nil
}

// entry point function
func main() {
	println("Hello World")

	lib, err := load("./libhello.so")
	if err != nil {
		panic(err)
	}
	defer unload(lib)

	addr, err := findSym(lib, "hello")
	if err != nil {
		panic(err)
	}

	var hello func()

	p := &addr
	hello = *(*func())(unsafe.Pointer(&p))
	
	hello()
}

編譯

1
go build -o main_go_dl.out  main_dl.go

小結

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

參考連結

相關頁面