go调用lib和so动态库
# go 调用dll
# 1. sysCall.LoadDll(推荐使用)
系统调用是程序向操作系统内核请求服务的过程,通常包含硬件相关的服务(例如访问硬盘),创建新进程等。系统调用提供了一个进程和操作系统之间的接口
fmt中的syscall
func Println(a ...interface{}) (n int, err error) { return Fprintln(os.Stdout, a...) } Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
1
2
3
4
5调用dll 示例
dll, err := syscall.LoadDLL("scan.dll") //根据名称从dll中查找proc MemoryStream_Get = dll.FindProc("AllocateMemory") MemoryStream_Get.Call()
1
2
3
4此方式可以 也可以调用go 代码打包的dll
# 2. Cgo调用
项目目录结构如下
├── include │ └── add.c │ └── add.h ├── lib │ └── libadd.dll └── main.go
1
2
3
4
5
6add.h
#ifndef __ADD_H__ #define __ADD_H__ char* Add(char* src, int n); #endif
1
2
3
4
5
6
7add.c
#include <string.h> #include <stdio.h> #include <stdlib.h> char* Add(char* src, int n) { char str[20]; sprintf(str, "%d", n); char *result = malloc(strlen(src)+strlen(str)+1); strcpy(result, src); strcat(result, str); return result; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14main.go
func GetFinalStrategyString(request string) { dll, err := syscall.LoadDLL("./middleware_c.dll") if err != nil { log.Fatal("Error loading DLL:", err) } defer dll.Release() getFinalStrategyString, err := dll.FindProc("GetFinalStrategyString") if err != nil { log.Fatal("Error finding GetFinalStrategyString:", err) } freeFinalStrategyString, err := dll.FindProc("FreeFinalStrategyString") if err != nil { log.Fatal("Error finding FreeFinalStrategyString:", err) } // cRequest, err := syscall.UTF16PtrFromString(request) // if err != nil { // log.Fatal("Error converting request:", err) // } var cResponse *uint16 var responseLen uint32 ret, _, _ := getFinalStrategyString.Call( uintptr(unsafe.Pointer(request)), uintptr(len(request)), uintptr(unsafe.Pointer(&cResponse)), uintptr(unsafe.Pointer(&responseLen)), ) if ret != 0 { log.Fatal("ret Error:", ret) } fmt.Println("responseLen:", responseLen) fmt.Println("cResponse:", cResponse) cResponseBytes := (*[1 << 20]byte)(unsafe.Pointer(cResponse))[:responseLen] fmt.Println("cResponseBytes:", cResponseBytes) }
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
# 3. 动态调用 dll (推荐使用)
动态调用步骤
- 通过文件路径加载c/c++ 动态库中的 handle
- 在go代码中定义和c/c++ 动态库中对应的go func
- 使用库purego(底层是dlopen)或者sys/windows 将handle中对应的方法符号(Symbol)映射到 go func 地址上
- 逻辑调用
main.go
import "C" import ( "context" "fmt" "github.com/ebitengine/purego" "sync" ) var ( getFinalStrategyString func(*C.char, C.uint, **C.char, *C.uint) C.int //2. 定义和c/c++ 动态库中对应的go func freeFinalStrategyString func(*C.char) // 2. 定义和c/c++ 动态库中对应的go func mcRunModeOnce sync.Once mcRunMode int ) func main(){ InitMC(libFilePath) } func InitMC(libFilePath string) (err error) { defer func() { if _err := recover(); _err != nil { logger.Errorf("RegisterLibFunc panic error: %v", err) err = fmt.Errorf("RegisterLibFunc panic error: %v", err.Error()) } }() ... mcHandle, err := LoadHandle(libFilePath) // 1. 通过路径加载c/c++ 动态库handle if err != nil { logger.Errorf("LoadHandle error:%v", err.Error()) return err } // 找不到函数会panic purego.RegisterLibFunc(&getFinalStrategyString, mcHandle, "GetFinalStrategyString") // 3. 使用库purego将handle中对应的方法映射到go func purego.RegisterLibFunc(&freeFinalStrategyString, mcHandle, "FreeFinalStrategyString") // 3. 使用库purego将handle中对应的方法映射到go func return nil }
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
40load_linux.go
//go:build !windows // +build !windows package main import "github.com/ebitengine/purego" const MCLibPath = "c.so" func LoadHandle(libPath string) (uintptr, error) { return purego.Dlopen(libPath, purego.RTLD_NOW|purego.RTLD_GLOBAL) }
1
2
3
4
5
6
7
8
9
10
11
12
13load_windows.go
//go:build windows // +build windows package main import "golang.org/x/sys/windows" const MCLibPath = "" func LoadHandle(libPath string) (uintptr, error) { handle, err := windows.LoadLibrary(libPath) return uintptr(handle), err }
1
2
3
4
5
6
7
8
9
10
11
12
13
14业务调用
c++ 头文件
#ifdef __cplusplus extern "C" { #endif
MIDDLEWARE_C_EXPORT int GetFinalStrategyString( /::browserconsoleapiv3::middle_ware::GetUserStrategiesRequest request/ const char* request, unsigned request_len, /::browserconsoleapiv3::api::user::FinalGroupStrategy response/ char** response, unsigned* response_len);
MIDDLEWARE_C_EXPORT void FreeFinalStrategyString(const char*);
#ifdef __cplusplus } #endif
package middleware /* #include <stdlib.h> // 要引入 */ import "C" import ( "fmt" "unsafe" ) type MiddlewareC struct { response *C.char } func (mc *MiddlewareC) GetFinalStrategyString(req string) (resByte []byte, err error) { cRequest := C.CString(req) defer C.free(unsafe.Pointer(cRequest)) var ( reqLen C.uint cResponse *C.char responseLen C.uint resCodeC C.int ) reqLen = C.uint(len(req)) resCodeC = getFinalStrategyString(cRequest, reqLen, &cResponse, &responseLen) //4. 真正的业务调用 resCode := int(resCodeC) if resCode != 0 { errMsg, ok := MCError[resCode] if !ok { err = fmt.Errorf( "unknown error code, code:%v, cResponse: %v, cResponse len: %v", resCode, cResponse, responseLen, ) } else { err = fmt.Errorf( "middleware error msg: %v, error code: %v, cResponse: %v, cResponse len: %v", errMsg, resCode, cResponse, responseLen, ) } return resByte, err } resByte = C.GoBytes(unsafe.Pointer(cResponse), C.int(responseLen)) mc.response = cResponse return resByte, nil } func (mc *MiddlewareC) MustFreeFinalStrategyString() { freeFinalStrategyString(mc.response) }
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
# go 调用so
# 1. Cgo调用
项目目录结构如下
├── include │ └── add.c │ └── add.h ├── lib │ └── libadd.so └── main.go
1
2
3
4
5
6add.h
#ifndef __ADD_H__ #define __ADD_H__ char* Add(char* src, int n); #endif
1
2
3
4
5
6
7add.c
#include <string.h> #include <stdio.h> #include <stdlib.h> char* Add(char* src, int n) { char str[20]; sprintf(str, "%d", n); char *result = malloc(strlen(src)+strlen(str)+1); strcpy(result, src); strcat(result, str); return result; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14linux 下编译
会在当前目录下生成
libadd.so
文件, 在 Linux 下可用nm -D libadd.so
查看其中的方法gcc -fPIC -shared -o lib/libadd.so include/add.c
1main.go
package main /* // 头文件的位置,相对于源文件是当前目录,所以是 .,头文件在多个目录时写多个 #cgo CFLAGS: ... #cgo CFLAGS: -I./include // 从哪里加载动态库,位置与文件名,-ladd 加载 libadd.so 文件 #cgo LDFLAGS: -L./lib -ladd -Wl,-rpath,lib #include "add.h" */ import "C" import "fmt" func main() { val := C.Add(C.CString("go"), 2023) fmt.Println("run c: ", C.GoString(val)) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17注意:
如果把
#cgo LDFLAGS: -L./lib -ladd -Wl,-rpath,lib
改为cgo LDFLAGS: -L./lib -ladd
编译不会报错,执行时会出错error while loading shared libraries: libadd.so: cannot open shared object file: No such file or directory
1设置了环境变量 LD_LIBRARY_PATH=/home/.../lib 也能让它跑起来
LD_LIBRARY_PATH=lib/ ./demo
1
# 2. 动态调用 so(推荐使用)
动态调用步骤
- 通过路径加载c/c++ 动态库 handle
- 定义和c/c++ 动态库中对应的go func
- 使用库purego 将handle中对应的方法映射到go func
- 逻辑调用
main.go
import "C" import ( "context" "fmt" "github.com/ebitengine/purego" "sync" ) var ( getFinalStrategyString func(*C.char, C.uint, **C.char, *C.uint) C.int //2. 定义和c/c++ 动态库中对应的go func freeFinalStrategyString func(*C.char) // 2. 定义和c/c++ 动态库中对应的go func mcRunModeOnce sync.Once mcRunMode int ) func main(){ InitMC(libFilePath) } func InitMC(libFilePath string) (err error) { defer func() { if _err := recover(); _err != nil { logger.Errorf("RegisterLibFunc panic error: %v", err) err = fmt.Errorf("RegisterLibFunc panic error: %v", err.Error()) } }() ... mcHandle, err := LoadHandle(libFilePath) // 1. 通过路径加载c/c++ 动态库handle if err != nil { logger.Errorf("LoadHandle error:%v", err.Error()) return err } // 找不到函数会panic purego.RegisterLibFunc(&getFinalStrategyString, mcHandle, "GetFinalStrategyString") // 3. 使用库purego将handle中对应的方法映射到go func purego.RegisterLibFunc(&freeFinalStrategyString, mcHandle, "FreeFinalStrategyString") // 3. 使用库purego将handle中对应的方法映射到go func return nil }
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
40load_linux.go
//go:build !windows // +build !windows package main import "github.com/ebitengine/purego" const MCLibPath = "c.so" func LoadHandle(libPath string) (uintptr, error) { return purego.Dlopen(libPath, purego.RTLD_NOW|purego.RTLD_GLOBAL) }
1
2
3
4
5
6
7
8
9
10
11
12
13load_windows.go
//go:build windows // +build windows package main import "golang.org/x/sys/windows" const MCLibPath = "" func LoadHandle(libPath string) (uintptr, error) { handle, err := windows.LoadLibrary(libPath) return uintptr(handle), err }
1
2
3
4
5
6
7
8
9
10
11
12
13
14业务调用
c++ 头文件
#ifdef __cplusplus extern "C" { #endif
MIDDLEWARE_C_EXPORT int GetFinalStrategyString( /::browserconsoleapiv3::middle_ware::GetUserStrategiesRequest request/ const char* request, unsigned request_len, /::browserconsoleapiv3::api::user::FinalGroupStrategy response/ char** response, unsigned* response_len);
MIDDLEWARE_C_EXPORT void FreeFinalStrategyString(const char*);
#ifdef __cplusplus } #endif
package middleware /* #include <stdlib.h> // 要引入 */ import "C" import ( "fmt" "unsafe" ) type MiddlewareC struct { response *C.char } func (mc *MiddlewareC) GetFinalStrategyString(req string) (resByte []byte, err error) { cRequest := C.CString(req) defer C.free(unsafe.Pointer(cRequest)) var ( reqLen C.uint cResponse *C.char responseLen C.uint resCodeC C.int ) reqLen = C.uint(len(req)) resCodeC = getFinalStrategyString(cRequest, reqLen, &cResponse, &responseLen) //4. 真正的业务调用 resCode := int(resCodeC) if resCode != 0 { errMsg, ok := MCError[resCode] if !ok { err = fmt.Errorf( "unknown error code, code:%v, cResponse: %v, cResponse len: %v", resCode, cResponse, responseLen, ) } else { err = fmt.Errorf( "middleware error msg: %v, error code: %v, cResponse: %v, cResponse len: %v", errMsg, resCode, cResponse, responseLen, ) } return resByte, err } resByte = C.GoBytes(unsafe.Pointer(cResponse), C.int(responseLen)) mc.response = cResponse return resByte, nil } func (mc *MiddlewareC) MustFreeFinalStrategyString() { freeFinalStrategyString(mc.response) }
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
# 大坑!!!
动态库中的崩溃会直接导致主程序崩溃!!!!!!!!!!
崩溃测试
创建崩溃程序
package main import "C" import "fmt" //export PrintTest func PrintTest() int { defer func() { if err := recover(); err != nil { print("err\n") print(err) } }() print("hello world\n") var a []int a[0] = 1 print("hello world 2\n") return 1 } func main() { fmt.Println("call cpp test") }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24打包dll文件
go build -o pt.dll -buildmode=c-shared main.go
主程序调用
package main import "C" import ( "github.com/ebitengine/purego" win "golang.org/x/sys/windows" "log" ) var PrintTest func() int func openLibrary(name string) (uintptr, error) { handle, err := win.LoadLibrary(name) return uintptr(handle), err } func main() { // recover无效了! defer func() { if err := recover(); err != nil { log.Println("err") log.Println(err) } }() lib, err := openLibrary("./dll/pt.dll") if err != nil { log.Fatalln(err) } purego.RegisterLibFunc(&PrintTest, lib, "PrintTest") res := PrintTest() log.Println(res) } // !!! 直接panic,recover无效
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