添加动态库加载功能及测试用例
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
|
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 h1:3QcIxCLirIxOZhIVtvo9eWz8tym/iZ9Nb29VCnzaMvc=
|
||||||
github.com/mattn/anko v0.1.10/go.mod h1:gjrudvzf1t7FWTZo1Nbywnr75g3uDnGjXdp2nkguBjQ=
|
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 {
|
type Ankointerceptor struct {
|
||||||
importMap map[string]interface{}
|
importMap map[string]interface{}
|
||||||
|
libMap map[string]interface{}
|
||||||
|
libhabdles []uintptr
|
||||||
ankoEnv *env.Env
|
ankoEnv *env.Env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,9 +20,14 @@ func NewAnkointerceptor(ankoEnv *env.Env) *Ankointerceptor {
|
||||||
|
|
||||||
a := &Ankointerceptor{
|
a := &Ankointerceptor{
|
||||||
importMap: make(map[string]interface{}),
|
importMap: make(map[string]interface{}),
|
||||||
|
libMap: make(map[string]interface{}),
|
||||||
|
libhabdles: make([]uintptr, 0),
|
||||||
ankoEnv: ankoEnv,
|
ankoEnv: ankoEnv,
|
||||||
}
|
}
|
||||||
a.ankoEnv.Define("println", fmt.Println)
|
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["fs"] = &FileModule{}
|
||||||
a.importMap["net"] = &NetModule{}
|
a.importMap["net"] = &NetModule{}
|
||||||
a.importMap["process"] = &ProcessModule{}
|
a.importMap["process"] = &ProcessModule{}
|
||||||
|
|
@ -40,6 +47,7 @@ func (a *Ankointerceptor) Exec(script string) (interface{}, error) {
|
||||||
scriptcode := string(scriptbytes)
|
scriptcode := string(scriptbytes)
|
||||||
result, err := vm.Execute(e, nil, scriptcode)
|
result, err := vm.Execute(e, nil, scriptcode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return result, nil
|
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