添加动态库加载功能及测试用例
This commit is contained in:
parent
64e48a4094
commit
7624246633
|
|
@ -0,0 +1 @@
|
|||
vendor/
|
||||
5
go.mod
5
go.mod
|
|
@ -2,4 +2,7 @@ module git.kingecg.top/kingecg/gosh
|
|||
|
||||
go 1.23.1
|
||||
|
||||
require github.com/mattn/anko v0.1.10 // indirect
|
||||
require (
|
||||
github.com/ebitengine/purego v0.8.4
|
||||
github.com/mattn/anko v0.1.10
|
||||
)
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -1,2 +1,4 @@
|
|||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/mattn/anko v0.1.10 h1:3QcIxCLirIxOZhIVtvo9eWz8tym/iZ9Nb29VCnzaMvc=
|
||||
github.com/mattn/anko v0.1.10/go.mod h1:gjrudvzf1t7FWTZo1Nbywnr75g3uDnGjXdp2nkguBjQ=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
package interceptor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
type funcType struct {
|
||||
ParamTypes []string
|
||||
ReturnTypes []string
|
||||
}
|
||||
|
||||
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, // 用于表示无返回值
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ import (
|
|||
|
||||
type Ankointerceptor struct {
|
||||
importMap map[string]interface{}
|
||||
libMap map[string]interface{}
|
||||
libhabdles []uintptr
|
||||
ankoEnv *env.Env
|
||||
}
|
||||
|
||||
|
|
@ -18,9 +20,14 @@ func NewAnkointerceptor(ankoEnv *env.Env) *Ankointerceptor {
|
|||
|
||||
a := &Ankointerceptor{
|
||||
importMap: make(map[string]interface{}),
|
||||
libMap: make(map[string]interface{}),
|
||||
libhabdles: make([]uintptr, 0),
|
||||
ankoEnv: ankoEnv,
|
||||
}
|
||||
a.ankoEnv.Define("println", fmt.Println)
|
||||
a.ankoEnv.Define("loadLibrary", func(libPath string, libFuncMap map[string]funcType) interface{} {
|
||||
return a.loadLibrary(libPath, libFuncMap)
|
||||
})
|
||||
a.importMap["fs"] = &FileModule{}
|
||||
a.importMap["net"] = &NetModule{}
|
||||
a.importMap["process"] = &ProcessModule{}
|
||||
|
|
@ -40,6 +47,7 @@ func (a *Ankointerceptor) Exec(script string) (interface{}, error) {
|
|||
scriptcode := string(scriptbytes)
|
||||
result, err := vm.Execute(e, nil, scriptcode)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
|
|
|
|||
|
|
@ -22,3 +22,14 @@ func TestAnkointerceptor_ExecRequire(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAnkointerceptor_loadlib(t *testing.T) {
|
||||
e := env.NewEnv()
|
||||
interceptor := NewAnkointerceptor(e)
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
scriptPath := filepath.Join(filepath.Dir(file), "testdata/test_loadso.ank")
|
||||
v, _ := interceptor.Exec(scriptPath)
|
||||
if v != "Hello, world!" {
|
||||
t.Errorf("Loadso test failed")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// clib/greeting.c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// 返回静态缓冲区(线程不安全,仅演示)
|
||||
const char* Greeting(const char* name) {
|
||||
static char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Hello, %s!", name);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int Add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,13 @@
|
|||
input=[]string{"string"}
|
||||
output=[]string{"string"}
|
||||
libdefine=make(struct{
|
||||
ParamTypes []string,
|
||||
ReturnTypes []string
|
||||
})
|
||||
libdefine.ParamTypes=input
|
||||
libdefine.ReturnTypes=output
|
||||
libdefines=map[string]interface{}
|
||||
libdefines["Greeting"]=libdefine
|
||||
lib=loadLibrary(__dirname+'/libgreeting.so',libdefines)
|
||||
x=lib.Greeting('world')
|
||||
x
|
||||
Loading…
Reference in New Issue