From 7212a48a9b7767d35f62e836d188db44def100b0 Mon Sep 17 00:00:00 2001 From: lauk001 Date: Tue, 9 Jul 2024 13:45:45 +0800 Subject: [PATCH] update vendor --- .../agiledragon/gomonkey/v2/.gitignore | 6 + .../agiledragon/gomonkey/v2/LICENSE | 21 + .../agiledragon/gomonkey/v2/Makefile | 4 + .../agiledragon/gomonkey/v2/README.md | 54 +++ .../gomonkey/v2/creflect/ae1.17.go | 39 ++ .../gomonkey/v2/creflect/be1.16.go | 25 ++ .../agiledragon/gomonkey/v2/creflect/type.go | 179 ++++++++ .../agiledragon/gomonkey/v2/jmp_386.go | 13 + .../agiledragon/gomonkey/v2/jmp_amd64.go | 18 + .../agiledragon/gomonkey/v2/jmp_arm64.go | 37 ++ .../agiledragon/gomonkey/v2/jmp_loong64.go | 73 ++++ .../gomonkey/v2/modify_binary_darwin.go | 24 ++ .../gomonkey/v2/modify_binary_linux.go | 27 ++ .../gomonkey/v2/modify_binary_windows.go | 25 ++ .../agiledragon/gomonkey/v2/patch.go | 391 ++++++++++++++++++ .../github.com/agiledragon/gomonkey/v2/ut.sh | 14 + .../gomonkey/v2/write_darwin_amd64.s | 64 +++ .../gomonkey/v2/write_darwin_arm64.s | 63 +++ 18 files changed, 1077 insertions(+) create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/.gitignore create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/LICENSE create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/Makefile create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/README.md create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/patch.go create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/ut.sh create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s create mode 100644 vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s diff --git a/vendor/github.com/agiledragon/gomonkey/v2/.gitignore b/vendor/github.com/agiledragon/gomonkey/v2/.gitignore new file mode 100644 index 0000000..d8a69c0 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/.gitignore @@ -0,0 +1,6 @@ +.* +!.gitignore +!.github + +coverage.* +profile.out \ No newline at end of file diff --git a/vendor/github.com/agiledragon/gomonkey/v2/LICENSE b/vendor/github.com/agiledragon/gomonkey/v2/LICENSE new file mode 100644 index 0000000..d75dc90 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Zhang Xiaolong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/agiledragon/gomonkey/v2/Makefile b/vendor/github.com/agiledragon/gomonkey/v2/Makefile new file mode 100644 index 0000000..dae4aa1 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/Makefile @@ -0,0 +1,4 @@ +test: + bash ./ut.sh + +.PHONY: test \ No newline at end of file diff --git a/vendor/github.com/agiledragon/gomonkey/v2/README.md b/vendor/github.com/agiledragon/gomonkey/v2/README.md new file mode 100644 index 0000000..44bcd58 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/README.md @@ -0,0 +1,54 @@ +# gomonkey + +gomonkey is a library to make monkey patching in unit tests easy, and the core idea of monkey patching comes from [Bouke](https://github.com/bouk), you can read [this blogpost](https://bou.ke/blog/monkey-patching-in-go/) for an explanation on how it works. + +## Features + ++ support a patch for a function ++ support a patch for a public member method ++ support a patch for a private member method ++ support a patch for a interface ++ support a patch for a function variable ++ support a patch for a global variable ++ support patches of a specified sequence for a function ++ support patches of a specified sequence for a member method ++ support patches of a specified sequence for a interface ++ support patches of a specified sequence for a function variable + +## Notes ++ gomonkey fails to patch a function or a member method if inlining is enabled, please running your tests with inlining disabled by adding the command line argument that is `-gcflags=-l`(below go1.10) or `-gcflags=all=-l`(go1.10 and above). ++ A panic may happen when a goroutine is patching a function or a member method that is visited by another goroutine at the same time. That is to say, gomonkey is not threadsafe. + +## Supported Platform: + +- ARCH + - amd64 + - arm64 + - 386 + - loong64 + +- OS + - Linux + - MAC OS X + - Windows + +## Installation +- below v2.1.0, for example v2.0.2 +```go +$ go get github.com/agiledragon/gomonkey@v2.0.2 +``` +- v2.1.0 and above, for example v2.11.0 +```go +$ go get github.com/agiledragon/gomonkey/v2@v2.11.0 +``` + +## Test Method +```go +$ cd test +$ go test -gcflags=all=-l +``` + +## Using gomonkey + +Please refer to the test cases as idioms, very complete and detailed. + diff --git a/vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go b/vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go new file mode 100644 index 0000000..68a7e75 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/creflect/ae1.17.go @@ -0,0 +1,39 @@ +//go:build go1.17 +// +build go1.17 + +package creflect + +import ( + "unsafe" +) + +// name is an encoded type name with optional extra data. +type name struct { + bytes *byte +} + +func (n name) data(off int, whySafe string) *byte { + return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe)) +} + +func (n name) readVarint(off int) (int, int) { + v := 0 + for i := 0; ; i++ { + x := *n.data(off+i, "read varint") + v += int(x&0x7f) << (7 * i) + if x&0x80 == 0 { + return i + 1, v + } + } +} + +func (n name) name() (s string) { + if n.bytes == nil { + return + } + i, l := n.readVarint(1) + hdr := (*String)(unsafe.Pointer(&s)) + hdr.Data = unsafe.Pointer(n.data(1+i, "non-empty string")) + hdr.Len = l + return +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go b/vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go new file mode 100644 index 0000000..1fb6988 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/creflect/be1.16.go @@ -0,0 +1,25 @@ +//go:build !go1.17 +// +build !go1.17 + +package creflect + +import ( + "unsafe" +) + +// name is an encoded type name with optional extra data. +type name struct { + bytes *byte +} + +func (n name) name() (s string) { + if n.bytes == nil { + return + } + b := (*[4]byte)(unsafe.Pointer(n.bytes)) + + hdr := (*String)(unsafe.Pointer(&s)) + hdr.Data = unsafe.Pointer(&b[3]) + hdr.Len = int(b[1])<<8 | int(b[2]) + return s +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go b/vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go new file mode 100644 index 0000000..7df1f09 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/creflect/type.go @@ -0,0 +1,179 @@ +// Customized reflect package for gomonkey,copy most code from go/src/reflect/type.go + +package creflect + +import ( + "reflect" + "unsafe" +) + +// rtype is the common implementation of most values. +// rtype must be kept in sync with ../runtime/type.go:/^type._type. +type rtype struct { + size uintptr + ptrdata uintptr // number of bytes in the type that can contain pointers + hash uint32 // hash of type; avoids computation in hash tables + tflag tflag // extra type information flags + align uint8 // alignment of variable with this type + fieldAlign uint8 // alignment of struct field with this type + kind uint8 // enumeration for C + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + equal func(unsafe.Pointer, unsafe.Pointer) bool + gcdata *byte // garbage collection data + str nameOff // string form + ptrToThis typeOff // type for pointer to this type, may be zero +} + +func Create(t reflect.Type) *rtype { + i := *(*funcValue)(unsafe.Pointer(&t)) + r := (*rtype)(i.p) + return r +} + +type funcValue struct { + _ uintptr + p unsafe.Pointer +} + +func funcPointer(v reflect.Method, ok bool) (unsafe.Pointer, bool) { + return (*funcValue)(unsafe.Pointer(&v.Func)).p, ok +} +func MethodByName(r reflect.Type, name string) (fn unsafe.Pointer, ok bool) { + t := Create(r) + if r.Kind() == reflect.Interface { + return funcPointer(r.MethodByName(name)) + } + ut := t.uncommon(r) + if ut == nil { + return nil, false + } + + for _, p := range ut.methods() { + if t.nameOff(p.name).name() == name { + return t.Method(p), true + } + } + return nil, false +} + +func (t *rtype) Method(p method) (fn unsafe.Pointer) { + tfn := t.textOff(p.tfn) + fn = unsafe.Pointer(&tfn) + return +} + +type tflag uint8 +type nameOff int32 // offset to a name +type typeOff int32 // offset to an *rtype +type textOff int32 // offset from top of text section + +//go:linkname resolveTextOff reflect.resolveTextOff +func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +func (t *rtype) textOff(off textOff) unsafe.Pointer { + return resolveTextOff(unsafe.Pointer(t), int32(off)) +} + +//go:linkname resolveNameOff reflect.resolveNameOff +func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer + +func (t *rtype) nameOff(off nameOff) name { + return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} +} + +const ( + tflagUncommon tflag = 1 << 0 +) + +// uncommonType is present only for defined types or types with methods +type uncommonType struct { + pkgPath nameOff // import path; empty for built-in types like int, string + mcount uint16 // number of methods + xcount uint16 // number of exported methods + moff uint32 // offset from this uncommontype to [mcount]method + _ uint32 // unused +} + +// ptrType represents a pointer type. +type ptrType struct { + rtype + elem *rtype // pointer element (pointed at) type +} + +// funcType represents a function type. +type funcType struct { + rtype + inCount uint16 + outCount uint16 // top bit is set if last input parameter is ... +} + +func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// interfaceType represents an interface type. +type interfaceType struct { + rtype + pkgPath name // import path + methods []imethod // sorted by hash +} + +type imethod struct { + name nameOff // name of method + typ typeOff // .(*FuncType) underneath +} + +type String struct { + Data unsafe.Pointer + Len int +} + +func (t *rtype) uncommon(r reflect.Type) *uncommonType { + if t.tflag&tflagUncommon == 0 { + return nil + } + switch r.Kind() { + case reflect.Ptr: + type u struct { + ptrType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case reflect.Func: + type u struct { + funcType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case reflect.Interface: + type u struct { + interfaceType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case reflect.Struct: + type u struct { + interfaceType + u uncommonType + } + return &(*u)(unsafe.Pointer(t)).u + default: + return nil + } +} + +// Method on non-interface type +type method struct { + name nameOff // name of method + mtyp typeOff // method type (without receiver) + ifn textOff // fn used in interface call (one-word receiver) + tfn textOff // fn used for normal method call +} + +func (t *uncommonType) methods() []method { + if t.mcount == 0 { + return nil + } + return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.mcount > 0"))[:t.mcount:t.mcount] +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go new file mode 100644 index 0000000..27c8d4f --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_386.go @@ -0,0 +1,13 @@ +package gomonkey + +func buildJmpDirective(double uintptr) []byte { + d0 := byte(double) + d1 := byte(double >> 8) + d2 := byte(double >> 16) + d3 := byte(double >> 24) + + return []byte{ + 0xBA, d0, d1, d2, d3, // MOV edx, double + 0xFF, 0x22, // JMP [edx] + } +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go new file mode 100644 index 0000000..02c1c42 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_amd64.go @@ -0,0 +1,18 @@ +package gomonkey + +func buildJmpDirective(double uintptr) []byte { + d0 := byte(double) + d1 := byte(double >> 8) + d2 := byte(double >> 16) + d3 := byte(double >> 24) + d4 := byte(double >> 32) + d5 := byte(double >> 40) + d6 := byte(double >> 48) + d7 := byte(double >> 56) + + return []byte{ + 0x48, 0xBA, d0, d1, d2, d3, d4, d5, d6, d7, // MOV rdx, double + 0xFF, 0x22, // JMP [rdx] + } +} + diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go new file mode 100644 index 0000000..772aa0c --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_arm64.go @@ -0,0 +1,37 @@ +//go:build arm64 +// +build arm64 + +package gomonkey + +import "unsafe" + +func buildJmpDirective(double uintptr) []byte { + res := make([]byte, 0, 24) + d0d1 := double & 0xFFFF + d2d3 := double >> 16 & 0xFFFF + d4d5 := double >> 32 & 0xFFFF + d6d7 := double >> 48 & 0xFFFF + + res = append(res, movImm(0b10, 0, d0d1)...) // MOVZ x26, double[16:0] + res = append(res, movImm(0b11, 1, d2d3)...) // MOVK x26, double[32:16] + res = append(res, movImm(0b11, 2, d4d5)...) // MOVK x26, double[48:32] + res = append(res, movImm(0b11, 3, d6d7)...) // MOVK x26, double[64:48] + res = append(res, []byte{0x4A, 0x03, 0x40, 0xF9}...) // LDR x10, [x26] + res = append(res, []byte{0x40, 0x01, 0x1F, 0xD6}...) // BR x10 + + return res +} + +func movImm(opc, shift int, val uintptr) []byte { + var m uint32 = 26 // rd + m |= uint32(val) << 5 // imm16 + m |= uint32(shift&3) << 21 // hw + m |= 0b100101 << 23 // const + m |= uint32(opc&0x3) << 29 // opc + m |= 0b1 << 31 // sf + + res := make([]byte, 4) + *(*uint32)(unsafe.Pointer(&res[0])) = m + + return res +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go b/vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go new file mode 100644 index 0000000..628dc4f --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/jmp_loong64.go @@ -0,0 +1,73 @@ +//go:build loong64 +// +build loong64 + +package gomonkey + +import "unsafe" + +const ( + REG_R0 uint32 = 0 + REG_R29 = 29 + REG_R30 = 30 +) + +const ( + OP_ORI uint32 = 0x00E << 22 + OP_LU12IW = 0x00A << 25 + OP_LU32ID = 0x00B << 25 + OP_LU52ID = 0x00C << 22 + OP_LDD = 0x0A3 << 22 + OP_JIRL = 0x013 << 26 +) + +func buildJmpDirective(double uintptr) []byte { + res := make([]byte, 0, 24) + + bit11_0 := (double >> 0) & 0xFFF + bit31_12 := (double >> 12) & 0xFFFFF + bit51_32 := (double >> 32) & 0xFFFFF + bit63_52 := (double >> 52) & 0xFFF + + // lu12i.w r29, bit31_12 + // ori r29, r29, bit11_0 + // lu32i.d r29, bit51_32 + // lu52i.d r29, bit63_52 + // ld.d, r30, r29, 0 + // jirl r0, r30, 0 + res = append(res, wireup_opc(OP_LU12IW, REG_R29, 0, bit31_12)...) + res = append(res, wireup_opc(OP_ORI, REG_R29, REG_R29, bit11_0)...) + res = append(res, wireup_opc(OP_LU32ID, REG_R29, 0, bit51_32)...) + res = append(res, wireup_opc(OP_LU52ID, REG_R29, REG_R29, bit63_52)...) + res = append(res, wireup_opc(OP_LDD, REG_R30, REG_R29, 0)...) + res = append(res, wireup_opc(OP_JIRL, REG_R0, REG_R30, 0)...) + + return res +} + +func wireup_opc(opc uint32, rd, rj uint32, val uintptr) []byte { + var m uint32 = 0 + + switch opc { + case OP_ORI, OP_LU52ID, OP_LDD: + m |= opc + m |= (rd & 0x1F) << 0 // rd + m |= (rj & 0x1F) << 5 // rj + m |= (uint32(val) & 0xFFF) << 10 // si12 + + case OP_LU12IW, OP_LU32ID: + m |= opc + m |= (rd & 0x1F) << 0 // rd + m |= (uint32(val) & 0xFFFFF) << 5 // si20 + + case OP_JIRL: + m |= opc + m |= (rd & 0x1F) << 0 // rd + m |= (rj & 0x1F) << 5 // rj + m |= (uint32(val) & 0xFFFF) << 10 // si16 + } + + res := make([]byte, 4) + *(*uint32)(unsafe.Pointer(&res[0])) = m + + return res +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go new file mode 100644 index 0000000..478bbec --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go @@ -0,0 +1,24 @@ +package gomonkey + +import ( + "fmt" + "reflect" + "syscall" + "unsafe" +) + +func PtrOf(val []byte) uintptr { + return (*reflect.SliceHeader)(unsafe.Pointer(&val)).Data +} + +func modifyBinary(target uintptr, bytes []byte) { + targetPage := pageStart(target) + res := write(target, PtrOf(bytes), len(bytes), targetPage, syscall.Getpagesize(), syscall.PROT_READ|syscall.PROT_EXEC) + if res != 0 { + panic(fmt.Errorf("failed to write memory, code %v", res)) + } +} + +//go:cgo_import_dynamic mach_task_self mach_task_self "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic mach_vm_protect mach_vm_protect "/usr/lib/libSystem.B.dylib" +func write(target, data uintptr, len int, page uintptr, pageSize, oriProt int) int diff --git a/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go new file mode 100644 index 0000000..6c16988 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_linux.go @@ -0,0 +1,27 @@ +package gomonkey + +import "syscall" + +func modifyBinary(target uintptr, bytes []byte) { + function := entryAddress(target, len(bytes)) + err := mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC) + if err != nil { + panic(err) + } + copy(function, bytes) + err = mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_EXEC) + if err != nil { + panic(err) + } +} + +func mprotectCrossPage(addr uintptr, length int, prot int) error { + pageSize := syscall.Getpagesize() + for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) { + page := entryAddress(p, pageSize) + if err := syscall.Mprotect(page, prot); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go new file mode 100644 index 0000000..ef0dbc7 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_windows.go @@ -0,0 +1,25 @@ +package gomonkey + +import ( + "syscall" + "unsafe" +) + +func modifyBinary(target uintptr, bytes []byte) { + function := entryAddress(target, len(bytes)) + + proc := syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect") + const PROT_READ_WRITE = 0x40 + var old uint32 + result, _, _ := proc.Call(target, uintptr(len(bytes)), uintptr(PROT_READ_WRITE), uintptr(unsafe.Pointer(&old))) + if result == 0 { + panic(result) + } + copy(function, bytes) + + var ignore uint32 + result, _, _ = proc.Call(target, uintptr(len(bytes)), uintptr(old), uintptr(unsafe.Pointer(&ignore))) + if result == 0 { + panic(result) + } +} \ No newline at end of file diff --git a/vendor/github.com/agiledragon/gomonkey/v2/patch.go b/vendor/github.com/agiledragon/gomonkey/v2/patch.go new file mode 100644 index 0000000..2cd881d --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/patch.go @@ -0,0 +1,391 @@ +package gomonkey + +import ( + "fmt" + "reflect" + "syscall" + "unsafe" + + "github.com/agiledragon/gomonkey/v2/creflect" +) + +type Patches struct { + originals map[uintptr][]byte + targets map[uintptr]uintptr + values map[reflect.Value]reflect.Value + valueHolders map[reflect.Value]reflect.Value +} + +type Params []interface{} +type OutputCell struct { + Values Params + Times int +} + +func ApplyFunc(target, double interface{}) *Patches { + return create().ApplyFunc(target, double) +} + +func ApplyMethod(target interface{}, methodName string, double interface{}) *Patches { + return create().ApplyMethod(target, methodName, double) +} + +func ApplyMethodFunc(target interface{}, methodName string, doubleFunc interface{}) *Patches { + return create().ApplyMethodFunc(target, methodName, doubleFunc) +} + +func ApplyPrivateMethod(target interface{}, methodName string, double interface{}) *Patches { + return create().ApplyPrivateMethod(target, methodName, double) +} + +func ApplyGlobalVar(target, double interface{}) *Patches { + return create().ApplyGlobalVar(target, double) +} + +func ApplyFuncVar(target, double interface{}) *Patches { + return create().ApplyFuncVar(target, double) +} + +func ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches { + return create().ApplyFuncSeq(target, outputs) +} + +func ApplyMethodSeq(target interface{}, methodName string, outputs []OutputCell) *Patches { + return create().ApplyMethodSeq(target, methodName, outputs) +} + +func ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *Patches { + return create().ApplyFuncVarSeq(target, outputs) +} + +func ApplyFuncReturn(target interface{}, output ...interface{}) *Patches { + return create().ApplyFuncReturn(target, output...) +} + +func ApplyMethodReturn(target interface{}, methodName string, output ...interface{}) *Patches { + return create().ApplyMethodReturn(target, methodName, output...) +} + +func ApplyFuncVarReturn(target interface{}, output ...interface{}) *Patches { + return create().ApplyFuncVarReturn(target, output...) +} + +func create() *Patches { + return &Patches{originals: make(map[uintptr][]byte), targets: map[uintptr]uintptr{}, + values: make(map[reflect.Value]reflect.Value), valueHolders: make(map[reflect.Value]reflect.Value)} +} + +func NewPatches() *Patches { + return create() +} + +func (this *Patches) Origin(fn func()) { + for target, bytes := range this.originals { + modifyBinary(target, bytes) + } + fn() + for target, targetPtr := range this.targets { + code := buildJmpDirective(targetPtr) + modifyBinary(target, code) + } +} + +func (this *Patches) ApplyFunc(target, double interface{}) *Patches { + t := reflect.ValueOf(target) + d := reflect.ValueOf(double) + return this.ApplyCore(t, d) +} + +func (this *Patches) ApplyMethod(target interface{}, methodName string, double interface{}) *Patches { + m, ok := castRType(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + d := reflect.ValueOf(double) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyMethodFunc(target interface{}, methodName string, doubleFunc interface{}) *Patches { + m, ok := castRType(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + d := funcToMethod(m.Type, doubleFunc) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyPrivateMethod(target interface{}, methodName string, double interface{}) *Patches { + m, ok := creflect.MethodByName(castRType(target), methodName) + if !ok { + panic("retrieve method by name failed") + } + d := reflect.ValueOf(double) + return this.ApplyCoreOnlyForPrivateMethod(m, d) +} + +func (this *Patches) ApplyGlobalVar(target, double interface{}) *Patches { + t := reflect.ValueOf(target) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + + this.values[t] = reflect.ValueOf(t.Elem().Interface()) + d := reflect.ValueOf(double) + t.Elem().Set(d) + return this +} + +func (this *Patches) ApplyFuncVar(target, double interface{}) *Patches { + t := reflect.ValueOf(target) + d := reflect.ValueOf(double) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + this.check(t.Elem(), d) + return this.ApplyGlobalVar(target, double) +} + +func (this *Patches) ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches { + funcType := reflect.TypeOf(target) + t := reflect.ValueOf(target) + d := getDoubleFunc(funcType, outputs) + return this.ApplyCore(t, d) +} + +func (this *Patches) ApplyMethodSeq(target interface{}, methodName string, outputs []OutputCell) *Patches { + m, ok := castRType(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + d := getDoubleFunc(m.Type, outputs) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *Patches { + t := reflect.ValueOf(target) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + if t.Elem().Kind() != reflect.Func { + panic("target is not a func") + } + + funcType := reflect.TypeOf(target).Elem() + double := getDoubleFunc(funcType, outputs).Interface() + return this.ApplyGlobalVar(target, double) +} + +func (this *Patches) ApplyFuncReturn(target interface{}, returns ...interface{}) *Patches { + funcType := reflect.TypeOf(target) + t := reflect.ValueOf(target) + outputs := []OutputCell{{Values: returns, Times: -1}} + d := getDoubleFunc(funcType, outputs) + return this.ApplyCore(t, d) +} + +func (this *Patches) ApplyMethodReturn(target interface{}, methodName string, returns ...interface{}) *Patches { + m, ok := reflect.TypeOf(target).MethodByName(methodName) + if !ok { + panic("retrieve method by name failed") + } + + outputs := []OutputCell{{Values: returns, Times: -1}} + d := getDoubleFunc(m.Type, outputs) + return this.ApplyCore(m.Func, d) +} + +func (this *Patches) ApplyFuncVarReturn(target interface{}, returns ...interface{}) *Patches { + t := reflect.ValueOf(target) + if t.Type().Kind() != reflect.Ptr { + panic("target is not a pointer") + } + if t.Elem().Kind() != reflect.Func { + panic("target is not a func") + } + + funcType := reflect.TypeOf(target).Elem() + outputs := []OutputCell{{Values: returns, Times: -1}} + double := getDoubleFunc(funcType, outputs).Interface() + return this.ApplyGlobalVar(target, double) +} + +func (this *Patches) Reset() { + for target, bytes := range this.originals { + modifyBinary(target, bytes) + delete(this.originals, target) + } + + for target, variable := range this.values { + target.Elem().Set(variable) + } +} + +func (this *Patches) ApplyCore(target, double reflect.Value) *Patches { + this.check(target, double) + assTarget := *(*uintptr)(getPointer(target)) + original := replace(assTarget, uintptr(getPointer(double))) + if _, ok := this.originals[assTarget]; !ok { + this.originals[assTarget] = original + } + this.targets[assTarget] = uintptr(getPointer(double)) + this.valueHolders[double] = double + return this +} + +func (this *Patches) ApplyCoreOnlyForPrivateMethod(target unsafe.Pointer, double reflect.Value) *Patches { + if double.Kind() != reflect.Func { + panic("double is not a func") + } + assTarget := *(*uintptr)(target) + original := replace(assTarget, uintptr(getPointer(double))) + if _, ok := this.originals[assTarget]; !ok { + this.originals[assTarget] = original + } + this.targets[assTarget] = uintptr(getPointer(double)) + this.valueHolders[double] = double + return this +} + +func (this *Patches) check(target, double reflect.Value) { + if target.Kind() != reflect.Func { + panic("target is not a func") + } + + if double.Kind() != reflect.Func { + panic("double is not a func") + } + + targetType := target.Type() + doubleType := double.Type() + + if targetType.NumIn() < doubleType.NumIn() || + targetType.NumOut() != doubleType.NumOut() || + (targetType.NumIn() == doubleType.NumIn() && targetType.IsVariadic() != doubleType.IsVariadic()) { + panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) + } + + for i, size := 0, doubleType.NumIn(); i < size; i++ { + targetIn := targetType.In(i) + doubleIn := doubleType.In(i) + + if targetIn.AssignableTo(doubleIn) { + continue + } + + panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) + } + + for i, size := 0, doubleType.NumOut(); i < size; i++ { + targetOut := targetType.Out(i) + doubleOut := doubleType.Out(i) + + if targetOut.AssignableTo(doubleOut) { + continue + } + + panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) + } +} + +func replace(target, double uintptr) []byte { + code := buildJmpDirective(double) + bytes := entryAddress(target, len(code)) + original := make([]byte, len(bytes)) + copy(original, bytes) + modifyBinary(target, code) + return original +} + +func getDoubleFunc(funcType reflect.Type, outputs []OutputCell) reflect.Value { + if funcType.NumOut() != len(outputs[0].Values) { + panic(fmt.Sprintf("func type has %v return values, but only %v values provided as double", + funcType.NumOut(), len(outputs[0].Values))) + } + + needReturn := false + slice := make([]Params, 0) + for _, output := range outputs { + if output.Times == -1 { + needReturn = true + slice = []Params{output.Values} + break + } + t := 0 + if output.Times <= 1 { + t = 1 + } else { + t = output.Times + } + for j := 0; j < t; j++ { + slice = append(slice, output.Values) + } + } + + i := 0 + lenOutputs := len(slice) + return reflect.MakeFunc(funcType, func(_ []reflect.Value) []reflect.Value { + if needReturn { + return GetResultValues(funcType, slice[0]...) + } + if i < lenOutputs { + i++ + return GetResultValues(funcType, slice[i-1]...) + } + panic("double seq is less than call seq") + }) +} + +func GetResultValues(funcType reflect.Type, results ...interface{}) []reflect.Value { + var resultValues []reflect.Value + for i, r := range results { + var resultValue reflect.Value + if r == nil { + resultValue = reflect.Zero(funcType.Out(i)) + } else { + v := reflect.New(funcType.Out(i)) + v.Elem().Set(reflect.ValueOf(r)) + resultValue = v.Elem() + } + resultValues = append(resultValues, resultValue) + } + return resultValues +} + +type funcValue struct { + _ uintptr + p unsafe.Pointer +} + +func getPointer(v reflect.Value) unsafe.Pointer { + return (*funcValue)(unsafe.Pointer(&v)).p +} + +func entryAddress(p uintptr, l int) []byte { + return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: p, Len: l, Cap: l})) +} + +func pageStart(ptr uintptr) uintptr { + return ptr & ^(uintptr(syscall.Getpagesize() - 1)) +} + +func funcToMethod(funcType reflect.Type, doubleFunc interface{}) reflect.Value { + rf := reflect.TypeOf(doubleFunc) + if rf.Kind() != reflect.Func { + panic("doubleFunc is not a func") + } + vf := reflect.ValueOf(doubleFunc) + return reflect.MakeFunc(funcType, func(in []reflect.Value) []reflect.Value { + if funcType.IsVariadic() { + return vf.CallSlice(in[1:]) + } else { + return vf.Call(in[1:]) + } + }) +} + +func castRType(val interface{}) reflect.Type { + if rTypeVal, ok := val.(reflect.Type); ok { + return rTypeVal + } + return reflect.TypeOf(val) +} diff --git a/vendor/github.com/agiledragon/gomonkey/v2/ut.sh b/vendor/github.com/agiledragon/gomonkey/v2/ut.sh new file mode 100644 index 0000000..7a66ceb --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/ut.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./test/... | grep -v test/fake); do + echo "--------Run test package: $d" + GO111MODULE=on go test -gcflags="all=-N -l" -v -coverprofile=profile.out -coverpkg=./... -covermode=atomic $d + echo "--------Finish test package: $d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s new file mode 100644 index 0000000..9b20a1f --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_amd64.s @@ -0,0 +1,64 @@ +/* + * Copyright 2022 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. + */ + +#include "textflag.h" + +#define NOP8 BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; +#define NOP64 NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; +#define NOP512 NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; +#define NOP4096 NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; + +#define protRW $(0x1|0x2|0x10) +#define mProtect $(0x2000000+74) + +TEXT ·write(SB),NOSPLIT,$24 + JMP START + NOP4096 +START: + MOVQ mProtect, AX + MOVQ page+24(FP), DI + MOVQ pageSize+32(FP), SI + MOVQ protRW, DX + SYSCALL + CMPQ AX, $0 + JZ PROTECT_OK + CALL mach_task_self(SB) + MOVQ AX, DI + MOVQ target+0(FP), SI + MOVQ len+16(FP), DX + MOVQ $0, CX + MOVQ protRW, R8 + CALL mach_vm_protect(SB) + CMPQ AX, $0 + JNZ RETURN +PROTECT_OK: + MOVQ target+0(FP), DI + MOVQ data+8(FP), SI + MOVQ len+16(FP), CX + MOVQ DI, to-24(SP) + MOVQ SI, from-16(SP) + MOVQ CX, n-8(SP) + CALL runtime·memmove(SB) + MOVQ mProtect, AX + MOVQ page+24(FP), DI + MOVQ pageSize+32(FP), SI + MOVQ oriProt+40(FP), DX + SYSCALL + JMP RETURN + NOP4096 +RETURN: + MOVQ AX, ret+48(FP) + RET diff --git a/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s new file mode 100644 index 0000000..c6cd376 --- /dev/null +++ b/vendor/github.com/agiledragon/gomonkey/v2/write_darwin_arm64.s @@ -0,0 +1,63 @@ +/* + * Copyright 2022 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. + */ + +#include "textflag.h" + +#define NOP64 WORD $0x1f2003d5; WORD $0x1f2003d5; +#define NOP512 NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; +#define NOP4096 NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; +#define NOP16384 NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; + +#define protRW $(0x1|0x2|0x10) +#define mProtect $(0x2000000+74) + +TEXT ·write(SB),NOSPLIT,$24 + B START + NOP16384 +START: + MOVD mProtect, R16 + MOVD page+24(FP), R0 + MOVD pageSize+32(FP), R1 + MOVD protRW, R2 + SVC $0x80 + CMP $0, R0 + BEQ PROTECT_OK + CALL mach_task_self(SB) + MOVD target+0(FP), R1 + MOVD len+16(FP), R2 + MOVD $0, R3 + MOVD protRW, R4 + CALL mach_vm_protect(SB) + CMP $0, R0 + BNE RETURN +PROTECT_OK: + MOVD target+0(FP), R0 + MOVD data+8(FP), R1 + MOVD len+16(FP), R2 + MOVD R0, to-24(SP) + MOVD R1, from-16(SP) + MOVD R2, n-8(SP) + CALL runtime·memmove(SB) + MOVD mProtect, R16 + MOVD page+24(FP), R0 + MOVD pageSize+32(FP), R1 + MOVD oriProt+40(FP), R2 + SVC $0x80 + B RETURN + NOP16384 +RETURN: + MOVD R0, ret+48(FP) + RET -- Gitee