gosh/interceptor/dl.go

129 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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.libhabdles = append(a.libhabdles, 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.libhabdles {
purego.Dlclose(lib)
}
}