diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a725465 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vendor/ \ No newline at end of file diff --git a/go.mod b/go.mod index 9ecacb9..331a706 100644 --- a/go.mod +++ b/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 +) diff --git a/go.sum b/go.sum index 75df7a4..f4c88a1 100644 --- a/go.sum +++ b/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= diff --git a/interceptor/dl.go b/interceptor/dl.go new file mode 100644 index 0000000..b9e337f --- /dev/null +++ b/interceptor/dl.go @@ -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] +} diff --git a/interceptor/interceptor.go b/interceptor/interceptor.go index 403d090..8510455 100644 --- a/interceptor/interceptor.go +++ b/interceptor/interceptor.go @@ -10,17 +10,24 @@ import ( ) type Ankointerceptor struct { - importMap map[string]interface{} - ankoEnv *env.Env + importMap map[string]interface{} + libMap map[string]interface{} + libhabdles []uintptr + ankoEnv *env.Env } func NewAnkointerceptor(ankoEnv *env.Env) *Ankointerceptor { a := &Ankointerceptor{ - importMap: make(map[string]interface{}), - ankoEnv: ankoEnv, + 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 diff --git a/interceptor/interceptor_test.go b/interceptor/interceptor_test.go index df19d8a..fee3fb4 100644 --- a/interceptor/interceptor_test.go +++ b/interceptor/interceptor_test.go @@ -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") + } +} diff --git a/interceptor/testdata/greeting.c b/interceptor/testdata/greeting.c new file mode 100644 index 0000000..885c5c8 --- /dev/null +++ b/interceptor/testdata/greeting.c @@ -0,0 +1,14 @@ +// clib/greeting.c +#include +#include + +// 返回静态缓冲区(线程不安全,仅演示) +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; +} \ No newline at end of file diff --git a/interceptor/testdata/libgreeting.so b/interceptor/testdata/libgreeting.so new file mode 100755 index 0000000..885e1ee Binary files /dev/null and b/interceptor/testdata/libgreeting.so differ diff --git a/interceptor/testdata/test_loadso.ank b/interceptor/testdata/test_loadso.ank new file mode 100644 index 0000000..c134c75 --- /dev/null +++ b/interceptor/testdata/test_loadso.ank @@ -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 \ No newline at end of file