diff --git a/internal/loader/loader.go b/internal/loader/loader.go index e1ec86260..5fa8eb2f1 100644 --- a/internal/loader/loader.go +++ b/internal/loader/loader.go @@ -1,3 +1,6 @@ +//go:build linux || darwin +// +build linux darwin + /* * Copyright 2021 ByteDance Inc. * diff --git a/internal/loader/loader_windows_amd64.go b/internal/loader/loader_windows_amd64.go new file mode 100644 index 000000000..455f01929 --- /dev/null +++ b/internal/loader/loader_windows_amd64.go @@ -0,0 +1,112 @@ +/* + * Copyright 2021 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package loader + +import ( + `fmt` + `os` + `reflect` + `syscall` + `unsafe` +) + +const ( + MEM_COMMIT = 0x00001000 + MEM_RESERVE = 0x00002000 +) + +var ( + libKernel32 = syscall.NewLazyDLL("KERNEL32.DLL") + libKernel32_VirtualAlloc = libKernel32.NewProc("VirtualAlloc") + libKernel32_VirtualProtect = libKernel32.NewProc("VirtualProtect") +) + +type Loader []byte +type Function unsafe.Pointer + +func (self Loader) LoadWithFaker(fn string, fp int, args int, faker interface{}) (f Function) { + p := os.Getpagesize() + n := (((len(self) - 1) / p) + 1) * p + + /* register the function */ + m := mmap(n) + v := fmt.Sprintf("runtime.__%s_%x", fn, m) + argsptr, localsptr := stackMap(faker) + registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argsptr, localsptr) + + /* reference as a slice */ + s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader { + Data : m, + Cap : n, + Len : len(self), + })) + + /* copy the machine code, and make it executable */ + copy(s, self) + mprotect(m, n) + return Function(&m) +} + +func (self Loader) Load(fn string, fp int, args int) (f Function) { + return self.LoadWithFaker(fn, fp, args, func(){}) +} + +func mmap(nb int) uintptr { + addr, err := winapi_VirtualAlloc(0, nb, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE) + if err != nil { + panic(err) + } + return addr +} + +func mprotect(p uintptr, nb int) (oldProtect int) { + err := winapi_VirtualProtect(p, nb, syscall.PAGE_EXECUTE_READ, &oldProtect) + if err != nil { + panic(err) + } + return +} + +// winapi_VirtualAlloc allocate memory +// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc +func winapi_VirtualAlloc(lpAddr uintptr, dwSize int, flAllocationType int, flProtect int) (uintptr, error) { + r1, _, err := libKernel32_VirtualAlloc.Call( + lpAddr, + uintptr(dwSize), + uintptr(flAllocationType), + uintptr(flProtect), + ) + if r1 == 0 { + return 0, err + } + return r1, nil +} + +// winapi_VirtualProtect change memory protection +// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect +func winapi_VirtualProtect(lpAddr uintptr, dwSize int, flNewProtect int, lpflOldProtect *int) error { + r1, _, err := libKernel32_VirtualProtect.Call( + lpAddr, + uintptr(dwSize), + uintptr(flNewProtect), + uintptr(unsafe.Pointer(lpflOldProtect)), + ) + if r1 == 0 { + return err + } + return nil +} diff --git a/issue_test/plugin_test.go b/issue_test/plugin_test.go index 89d41cc49..542788ba1 100644 --- a/issue_test/plugin_test.go +++ b/issue_test/plugin_test.go @@ -1,4 +1,5 @@ -// +build !race +//go:build (linux && !race) || (unix && !race) +// +build linux,!race unix,!race /* * Copyright 2021 ByteDance Inc. @@ -23,9 +24,9 @@ import ( `fmt` `os/exec` `plugin` + `reflect` `runtime` `testing` - `reflect` _ `github.com/bytedance/sonic` ) @@ -37,8 +38,8 @@ func buildPlugin() { panic(err) } cmd0 := exec.Cmd{ - Path: bin0, - Args: []string{"rm", "-f", "plugin/plugin."+runtime.Version()+".so"}, + Path: bin0, + Args: []string{"rm", "-f", "plugin/plugin." + runtime.Version() + ".so"}, Stdout: out, Stderr: out, } @@ -51,8 +52,8 @@ func buildPlugin() { panic(err) } cmd := exec.Cmd{ - Path: bin, - Args: []string{"go", "build", "-buildmode", "plugin", "-o", "plugin/plugin."+runtime.Version()+".so", "plugin/main.go"}, + Path: bin, + Args: []string{"go", "build", "-buildmode", "plugin", "-o", "plugin/plugin." + runtime.Version() + ".so", "plugin/main.go"}, Stdout: out, Stderr: out, } @@ -61,10 +62,9 @@ func buildPlugin() { } } - func TestPlugin(t *testing.T) { buildPlugin() - p, err := plugin.Open("plugin/plugin."+runtime.Version()+".so") + p, err := plugin.Open("plugin/plugin." + runtime.Version() + ".so") if err != nil { t.Fatal(err) }