129 lines
4.0 KiB
Go
129 lines
4.0 KiB
Go
// Package interceptor 提供了动态加载和调用共享库函数的功能。
|
||
// 该包使用purego库实现跨平台的动态库加载,支持Windows的DLL和Linux的SO文件。
|
||
package interceptor
|
||
|
||
import (
|
||
"fmt"
|
||
"path/filepath"
|
||
"reflect"
|
||
"strings"
|
||
|
||
"github.com/ebitengine/purego"
|
||
)
|
||
|
||
// funcType 定义了函数的参数类型和返回值类型。
|
||
// 用于在动态加载库函数时指定函数签名。
|
||
type funcType struct {
|
||
ParamTypes []string
|
||
ReturnTypes []string
|
||
}
|
||
|
||
// typeMap 将字符串类型名映射到对应的Go reflect.Type。
|
||
// 支持基本数据类型如int、float、string等,以及特殊的"void"类型表示无返回值。
|
||
var typeMap = map[string]reflect.Type{
|
||
"int": reflect.TypeOf(int(0)),
|
||
"int32": reflect.TypeOf(int32(0)),
|
||
"int64": reflect.TypeOf(int64(0)),
|
||
"uint": reflect.TypeOf(uint(0)),
|
||
"float32": reflect.TypeOf(float32(0)),
|
||
"float64": reflect.TypeOf(float64(0)),
|
||
"string": reflect.TypeOf(""),
|
||
"bool": reflect.TypeOf(false),
|
||
"void": nil, // 用于表示无返回值
|
||
}
|
||
|
||
// makeFuncType 根据给定的参数类型和返回值类型字符串列表,创建对应的函数类型。
|
||
// paramTypes: 函数参数类型的字符串列表
|
||
// returnTypes: 函数返回值类型的字符串列表
|
||
// 返回: 创建的函数类型和可能的错误
|
||
func makeFuncType(paramTypes, returnTypes []string) (reflect.Type, error) {
|
||
var in, out []reflect.Type
|
||
|
||
// 参数类型
|
||
for _, t := range paramTypes {
|
||
rt, ok := typeMap[t]
|
||
if !ok {
|
||
return nil, fmt.Errorf("unknown parameter type: %s", t)
|
||
}
|
||
in = append(in, rt)
|
||
}
|
||
|
||
// 返回值类型
|
||
for _, t := range returnTypes {
|
||
if t == "void" {
|
||
continue // 无返回值
|
||
}
|
||
rt, ok := typeMap[t]
|
||
if !ok {
|
||
return nil, fmt.Errorf("unknown return type: %s", t)
|
||
}
|
||
out = append(out, rt)
|
||
}
|
||
|
||
return reflect.FuncOf(in, out, false), nil
|
||
}
|
||
|
||
// loadLibrary 加载指定路径的动态库文件,并根据提供的函数映射表注册库中的函数。
|
||
// 如果库已经被加载过,则直接返回之前加载的实例。
|
||
// libPath: 动态库文件的路径
|
||
// libFuncMap: 库中函数名到函数类型的映射表
|
||
// 返回: 包含所有注册函数的接口实例
|
||
func (a *Ankointerceptor) loadLibrary(libPath string, libFuncMap map[string]funcType) interface{} {
|
||
libName := filepath.Base(libPath)
|
||
libName = strings.TrimSuffix(libName, filepath.Ext(".dll"))
|
||
// 是不是so文件?libName里边是否包含.so
|
||
if strings.Contains(libName, ".so") {
|
||
soIndex := strings.Index(libName, ".so")
|
||
libName = libName[0:soIndex]
|
||
|
||
}
|
||
libName = strings.TrimPrefix(libName, "lib")
|
||
if _, ok := a.libMap[libName]; ok {
|
||
return a.libMap[libName] // 如果已经加载过,直接返回
|
||
}
|
||
lib, err := purego.Dlopen(libPath, purego.RTLD_LAZY)
|
||
if err != nil {
|
||
panic("Load library error")
|
||
}
|
||
a.liberalises = append(a.liberalises, lib)
|
||
fnmap := make(map[string]interface{})
|
||
fileds := make([]reflect.StructField, 0, len(fnmap))
|
||
for name, types := range libFuncMap {
|
||
ft, err := makeFuncType(types.ParamTypes, types.ReturnTypes)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
fn := reflect.New(ft).Elem().Addr().Interface()
|
||
purego.RegisterLibFunc(fn, lib, name)
|
||
|
||
//Get ft 指针类型
|
||
// ftp := reflect.PtrTo(ft)
|
||
fileds = append(fileds, reflect.StructField{
|
||
Name: name,
|
||
Type: ft,
|
||
})
|
||
fnmap[name] = fn
|
||
}
|
||
|
||
// 根据fnmap构建一个匿名结构体,结构体的字段名为函数名,字段值为函数指针
|
||
libstruct := reflect.StructOf(fileds)
|
||
// 创建一个匿名结构体的实例
|
||
libinstance := reflect.New(libstruct).Elem()
|
||
for name, fn := range fnmap {
|
||
// 将函数指针指向的函数设置到结构体实例的对应字段中
|
||
libinstance.FieldByName(name).Set(reflect.ValueOf(fn).Elem())
|
||
// libinstance.FieldByName(name).Set()
|
||
}
|
||
a.libMap[libName] = libinstance.Interface()
|
||
|
||
return a.libMap[libName]
|
||
}
|
||
|
||
// Close 关闭所有已加载的动态库句柄,释放资源。
|
||
// 应在不再需要使用动态库时调用此方法。
|
||
func (a *Ankointerceptor) Close() {
|
||
for _, lib := range a.liberalises {
|
||
_ = purego.Dlclose(lib)
|
||
}
|
||
}
|