From 7624246633a079ce22b7a0975cedfe0af683c03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=B9=BF?= Date: Fri, 19 Sep 2025 18:57:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=A8=E6=80=81=E5=BA=93?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E5=8A=9F=E8=83=BD=E5=8F=8A=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + go.mod | 5 +- go.sum | 2 + interceptor/dl.go | 105 +++++++++++++++++++++++++++ interceptor/interceptor.go | 16 +++- interceptor/interceptor_test.go | 11 +++ interceptor/testdata/greeting.c | 14 ++++ interceptor/testdata/libgreeting.so | Bin 0 -> 15448 bytes interceptor/testdata/test_loadso.ank | 13 ++++ 9 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100644 interceptor/dl.go create mode 100644 interceptor/testdata/greeting.c create mode 100755 interceptor/testdata/libgreeting.so create mode 100644 interceptor/testdata/test_loadso.ank 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 0000000000000000000000000000000000000000..885e1ee6e9af2aa929ed57a738a371ac03592342 GIT binary patch literal 15448 zcmeHOU2Ggz6~1dH?YePnrzCAiDl#nzO`(}4ZqpiCH|zLsf}ErcwnU}2)3tYOFYHg) zoo(%ifZITe6-A1K5aQ>d52$LNDit0832>VJgu;W7^3az^ma0mw1hf_cnJ9QU=ic+} z&Q8`Sy!7c_Yv*k#2RLF<9+wq}y8z z@3k5VKNhoJ(lzqrqm6Vy@&rv9wa$_A?2))}x?Ai(^_)od*oR?;`|XY~h|_zGSb@Ut zaq{ClZ?pmS6Efau&UiJG50U(?CiY__ zyH&NTwk<|^NNlD=?unc+(9QR!^2LH*4%4O3_mzKS^r)W+O2JgN90sM)qy4#JAs9`M z=K`Z^uJI>k(*9(&kj`aK1!}ky1Yx!?rOJhqrEDRbRDGF@%4NqV+;Y)yc`JFb=3hPd9;vE({!GWtT`B4@Kh64&ma5eD z{FL~~^KkIa_5Owg-nor$F!}OoxQ)U;&3y{rR$c1cHOICWg_8Zewm@`A@tH ze?R11SZ(zZ?|6S%3%62$eG(v9U7GCNHBk5eN9X6R&h?IwT2;O@=AG%ig}dJThhe*S zw)YFfEI&b6E@$ZQjt-M}ipH*u&;B1hN6InsAuzhU`O~Z3*}+wB{sK+nN(Tpf^Y{Er zLu?19G5_wbc?QcXYt<^ruF`)7{&MNU_E`fB~G1CiJ7j%&i0#==jkDNH_7gx&!vy6RfnkR=o;$S{?*P+ z&n4&7p&bu@@&4WVbu0UG`~|YZJ4QBV-zVwwEgDb13he9XdaC zYD<@;eiSvL(BpZP>qoiWZisqygXutSip%SHzAAQ9G^-op(Ys%r|B?($Z;&+)9O>~? z^Y4lN-@+qL3HPZH)UC${d@xqmJKW#@6=&~25S+{woCDqN{qFvK2Oi33{y_IVB8o?; z^F03zYNcA2m>yqGEp$~qhC*a9p!e%qJ{ObUDRNw&Vy+&dPJ+&!lQI(E-LK_T5iZx8 z(JJ)~>hQR=x@J*4tL?Yc>Yri%BkDBE|7C4|yGo^~!%YTWZ@#Z+lP%J(xxe=1iTPv`thSS*$ObY(_O6!Rx@K^SD*{mm`7I6v#BOQrOz z9~8pUtePyP^MRkK!?Rq@JI08m~<1A%;%$9Ub%sj}7pi0glbXmW%#$ zx{%?m0s~JR>pOa+pQN44p==Pz(cis!(yqE7@FT zUpAvOH=Qm|D>pM+AWuWXlF>XHl*-v+p@H#9R|;}zc97o5T&P?&l2*79VqYL;WZjO)@e{j28-t}VwIr| z>)?<30#Mu+V0_?*`4`0humr??1}N;=ARM;DDp)Z@Srcn{ITwXw#QC5#znkyF@LO!ptv7{eSH2u6#v@! zbHq|8?jJ>}u|l8e&*;M`!hgMecx=lpKK^SYi~9FCY(@J49Zl5F4XGW08w9_k4&NK$ z`oeuS_UXm#!Gr#Rd^;olwfiFeVW0*(!_|4n|3nJD_Aox~Gq4YGLCzoM4L_X67s!`Q z;E(-bm+m7WHAf%o2l#_tCRNlQ_b1CY=zk^VkNcrr>{fO=3C-7p1uNqNY6(@MTf*b5^t85B`22_o)B--G)1dfkYW& U_3d&q|KI`Ruo4YQ{kZ>s0S8!Tu>b%7 literal 0 HcmV?d00001 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