├── .github ├── codecov.yaml └── workflows │ └── reviewdog.yaml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── creflect ├── ae1.17.go ├── be1.16.go └── type.go ├── dsl ├── behavior.go ├── constraint.go ├── factory.go ├── matcher.go └── patch_builder.go ├── go.mod ├── go.sum ├── jmp_386.go ├── jmp_amd64.go ├── jmp_arm64.go ├── jmp_loong64.go ├── jmp_riscv64.go ├── modify_binary_darwin.go ├── modify_binary_linux.go ├── modify_binary_windows.go ├── patch.go ├── test ├── apply_func_return_test.go ├── apply_func_seq_test.go ├── apply_func_test.go ├── apply_func_var_return_test.go ├── apply_func_var_seq_test.go ├── apply_func_var_test.go ├── apply_global_var_test.go ├── apply_interface_reused_test.go ├── apply_method_func_test.go ├── apply_method_return_test.go ├── apply_method_seq_test.go ├── apply_method_test.go ├── apply_private_method_test.go ├── dsl_test │ └── func_dsl_test.go ├── fake │ └── fake.go └── patch_pair_test.go ├── ut.sh ├── write_darwin_amd64.s └── write_darwin_arm64.s /.github/codecov.yaml: -------------------------------------------------------------------------------- 1 | # To validate: 2 | # cat codecov.yml | curl --data-binary @- https://codecov.io/validate 3 | 4 | codecov: 5 | notify: 6 | require_ci_to_pass: yes 7 | 8 | allow_coverage_offsets: true 9 | 10 | coverage: 11 | precision: 2 12 | round: down 13 | range: "50...75" 14 | 15 | status: 16 | project: 17 | default: 18 | threshold: 1 19 | # Disable patch since it is noisy and not correct 20 | patch: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yaml: -------------------------------------------------------------------------------- 1 | name: actions 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: 8 | - opened 9 | - reopened 10 | - synchronize 11 | - ready_for_review 12 | jobs: 13 | test: 14 | if: ${{ !github.event.pull_request.draft }} 15 | name: Test 16 | runs-on: ubuntu-latest 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | go-version: 21 | - "golang:1.21" 22 | - "golang:1.20" 23 | - "golang:1.19" 24 | - "golang:1.18" 25 | - "golang:1.17" 26 | - "golang:1.16" 27 | - "golang:1.15" 28 | - "golang:1.14" 29 | 30 | steps: 31 | - name: Check out code 32 | uses: actions/checkout@v3 33 | 34 | - name: Run Unit tests. 35 | env: 36 | BUILD_IMAGE: ${{ matrix.go-version }} 37 | run: make test 38 | 39 | - name: Coverage 40 | run: bash <(curl -s https://codecov.io/bash) 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | !.github 4 | 5 | coverage.* 6 | profile.out -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zhang Xiaolong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | bash ./ut.sh 3 | 4 | .PHONY: test -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gomonkey 2 | 3 | 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. 4 | 5 | ## Features 6 | 7 | + support a patch for a function 8 | + support a patch for a public member method 9 | + support a patch for a private member method 10 | + support a patch for a interface 11 | + support a patch for a function variable 12 | + support a patch for a global variable 13 | + support patches of a specified sequence for a function 14 | + support patches of a specified sequence for a member method 15 | + support patches of a specified sequence for a interface 16 | + support patches of a specified sequence for a function variable 17 | 18 | ## Notes 19 | + 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). 20 | + 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. 21 | 22 | ## Supported Platform: 23 | 24 | - ARCH 25 | - amd64 26 | - arm64 27 | - 386 28 | - loong64 29 | - riscv64 30 | 31 | - OS 32 | - Linux 33 | - MAC OS X 34 | - Windows 35 | 36 | ## Installation 37 | - below v2.1.0, for example v2.0.2 38 | ```go 39 | $ go get github.com/agiledragon/gomonkey@v2.0.2 40 | ``` 41 | - v2.1.0 and above, for example v2.11.0 42 | ```go 43 | $ go get github.com/agiledragon/gomonkey/v2@v2.11.0 44 | ``` 45 | 46 | ## Test Method 47 | ```go 48 | $ cd test 49 | $ go test -gcflags=all=-l 50 | ``` 51 | 52 | ## Using gomonkey 53 | 54 | Please refer to the test cases as idioms, very complete and detailed. 55 | 56 | -------------------------------------------------------------------------------- /creflect/ae1.17.go: -------------------------------------------------------------------------------- 1 | //go:build go1.17 2 | // +build go1.17 3 | 4 | package creflect 5 | 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | // name is an encoded type name with optional extra data. 11 | type name struct { 12 | bytes *byte 13 | } 14 | 15 | func (n name) data(off int, whySafe string) *byte { 16 | return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe)) 17 | } 18 | 19 | func (n name) readVarint(off int) (int, int) { 20 | v := 0 21 | for i := 0; ; i++ { 22 | x := *n.data(off+i, "read varint") 23 | v += int(x&0x7f) << (7 * i) 24 | if x&0x80 == 0 { 25 | return i + 1, v 26 | } 27 | } 28 | } 29 | 30 | func (n name) name() (s string) { 31 | if n.bytes == nil { 32 | return 33 | } 34 | i, l := n.readVarint(1) 35 | hdr := (*String)(unsafe.Pointer(&s)) 36 | hdr.Data = unsafe.Pointer(n.data(1+i, "non-empty string")) 37 | hdr.Len = l 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /creflect/be1.16.go: -------------------------------------------------------------------------------- 1 | //go:build !go1.17 2 | // +build !go1.17 3 | 4 | package creflect 5 | 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | // name is an encoded type name with optional extra data. 11 | type name struct { 12 | bytes *byte 13 | } 14 | 15 | func (n name) name() (s string) { 16 | if n.bytes == nil { 17 | return 18 | } 19 | b := (*[4]byte)(unsafe.Pointer(n.bytes)) 20 | 21 | hdr := (*String)(unsafe.Pointer(&s)) 22 | hdr.Data = unsafe.Pointer(&b[3]) 23 | hdr.Len = int(b[1])<<8 | int(b[2]) 24 | return s 25 | } 26 | -------------------------------------------------------------------------------- /creflect/type.go: -------------------------------------------------------------------------------- 1 | // Customized reflect package for gomonkey,copy most code from go/src/reflect/type.go 2 | 3 | package creflect 4 | 5 | import ( 6 | "reflect" 7 | "unsafe" 8 | ) 9 | 10 | // rtype is the common implementation of most values. 11 | // rtype must be kept in sync with ../runtime/type.go:/^type._type. 12 | type rtype struct { 13 | size uintptr 14 | ptrdata uintptr // number of bytes in the type that can contain pointers 15 | hash uint32 // hash of type; avoids computation in hash tables 16 | tflag tflag // extra type information flags 17 | align uint8 // alignment of variable with this type 18 | fieldAlign uint8 // alignment of struct field with this type 19 | kind uint8 // enumeration for C 20 | // function for comparing objects of this type 21 | // (ptr to object A, ptr to object B) -> ==? 22 | equal func(unsafe.Pointer, unsafe.Pointer) bool 23 | gcdata *byte // garbage collection data 24 | str nameOff // string form 25 | ptrToThis typeOff // type for pointer to this type, may be zero 26 | } 27 | 28 | func Create(t reflect.Type) *rtype { 29 | i := *(*funcValue)(unsafe.Pointer(&t)) 30 | r := (*rtype)(i.p) 31 | return r 32 | } 33 | 34 | type funcValue struct { 35 | _ uintptr 36 | p unsafe.Pointer 37 | } 38 | 39 | func funcPointer(v reflect.Method, ok bool) (unsafe.Pointer, bool) { 40 | return (*funcValue)(unsafe.Pointer(&v.Func)).p, ok 41 | } 42 | func MethodByName(r reflect.Type, name string) (fn unsafe.Pointer, ok bool) { 43 | t := Create(r) 44 | if r.Kind() == reflect.Interface { 45 | return funcPointer(r.MethodByName(name)) 46 | } 47 | ut := t.uncommon(r) 48 | if ut == nil { 49 | return nil, false 50 | } 51 | 52 | for _, p := range ut.methods() { 53 | if t.nameOff(p.name).name() == name { 54 | return t.Method(p), true 55 | } 56 | } 57 | return nil, false 58 | } 59 | 60 | func (t *rtype) Method(p method) (fn unsafe.Pointer) { 61 | tfn := t.textOff(p.tfn) 62 | fn = unsafe.Pointer(&tfn) 63 | return 64 | } 65 | 66 | type tflag uint8 67 | type nameOff int32 // offset to a name 68 | type typeOff int32 // offset to an *rtype 69 | type textOff int32 // offset from top of text section 70 | 71 | //go:linkname resolveTextOff reflect.resolveTextOff 72 | func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer 73 | 74 | func (t *rtype) textOff(off textOff) unsafe.Pointer { 75 | return resolveTextOff(unsafe.Pointer(t), int32(off)) 76 | } 77 | 78 | //go:linkname resolveNameOff reflect.resolveNameOff 79 | func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer 80 | 81 | func (t *rtype) nameOff(off nameOff) name { 82 | return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} 83 | } 84 | 85 | const ( 86 | tflagUncommon tflag = 1 << 0 87 | ) 88 | 89 | // uncommonType is present only for defined types or types with methods 90 | type uncommonType struct { 91 | pkgPath nameOff // import path; empty for built-in types like int, string 92 | mcount uint16 // number of methods 93 | xcount uint16 // number of exported methods 94 | moff uint32 // offset from this uncommontype to [mcount]method 95 | _ uint32 // unused 96 | } 97 | 98 | // ptrType represents a pointer type. 99 | type ptrType struct { 100 | rtype 101 | elem *rtype // pointer element (pointed at) type 102 | } 103 | 104 | // funcType represents a function type. 105 | type funcType struct { 106 | rtype 107 | inCount uint16 108 | outCount uint16 // top bit is set if last input parameter is ... 109 | } 110 | 111 | func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { 112 | return unsafe.Pointer(uintptr(p) + x) 113 | } 114 | 115 | // interfaceType represents an interface type. 116 | type interfaceType struct { 117 | rtype 118 | pkgPath name // import path 119 | methods []imethod // sorted by hash 120 | } 121 | 122 | type imethod struct { 123 | name nameOff // name of method 124 | typ typeOff // .(*FuncType) underneath 125 | } 126 | 127 | type String struct { 128 | Data unsafe.Pointer 129 | Len int 130 | } 131 | 132 | func (t *rtype) uncommon(r reflect.Type) *uncommonType { 133 | if t.tflag&tflagUncommon == 0 { 134 | return nil 135 | } 136 | switch r.Kind() { 137 | case reflect.Ptr: 138 | type u struct { 139 | ptrType 140 | u uncommonType 141 | } 142 | return &(*u)(unsafe.Pointer(t)).u 143 | case reflect.Func: 144 | type u struct { 145 | funcType 146 | u uncommonType 147 | } 148 | return &(*u)(unsafe.Pointer(t)).u 149 | case reflect.Interface: 150 | type u struct { 151 | interfaceType 152 | u uncommonType 153 | } 154 | return &(*u)(unsafe.Pointer(t)).u 155 | case reflect.Struct: 156 | type u struct { 157 | interfaceType 158 | u uncommonType 159 | } 160 | return &(*u)(unsafe.Pointer(t)).u 161 | default: 162 | return nil 163 | } 164 | } 165 | 166 | // Method on non-interface type 167 | type method struct { 168 | name nameOff // name of method 169 | mtyp typeOff // method type (without receiver) 170 | ifn textOff // fn used in interface call (one-word receiver) 171 | tfn textOff // fn used for normal method call 172 | } 173 | 174 | func (t *uncommonType) methods() []method { 175 | if t.mcount == 0 { 176 | return nil 177 | } 178 | return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.mcount > 0"))[:t.mcount:t.mcount] 179 | } 180 | -------------------------------------------------------------------------------- /dsl/behavior.go: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | import . "github.com/agiledragon/gomonkey/v2" 4 | 5 | type Behavior interface { 6 | Apply() []Params 7 | } 8 | 9 | type ReturnBehavior struct { 10 | rets []Params 11 | params Params 12 | } 13 | 14 | func (this *ReturnBehavior) Apply() []Params { 15 | this.rets = append(this.rets, this.params) 16 | return this.rets 17 | } 18 | 19 | type RepeatBehavior struct { 20 | rets []Params 21 | behavior Behavior 22 | times int 23 | } 24 | 25 | func (this *RepeatBehavior) Apply() []Params { 26 | for i := 0; i < this.times; i++ { 27 | this.rets = append(this.rets, this.behavior.Apply()[0]) 28 | } 29 | return this.rets 30 | } 31 | -------------------------------------------------------------------------------- /dsl/constraint.go: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | import "reflect" 4 | 5 | type Constraint interface { 6 | Eval(x interface{}) bool 7 | } 8 | 9 | type AnyConstraint struct { 10 | } 11 | 12 | func (this *AnyConstraint) Eval(x interface{}) bool { 13 | return true 14 | } 15 | 16 | type EqConstraint struct { 17 | x interface{} 18 | } 19 | 20 | func (this *EqConstraint) Eval(x interface{}) bool { 21 | return reflect.DeepEqual(this.x, x) 22 | } 23 | -------------------------------------------------------------------------------- /dsl/factory.go: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | import . "github.com/agiledragon/gomonkey/v2" 4 | 5 | func Any() Constraint { 6 | return &AnyConstraint{} 7 | } 8 | 9 | func Eq(x interface{}) Constraint { 10 | return &EqConstraint{x: x} 11 | } 12 | 13 | func Return(x ...interface{}) Behavior { 14 | r := &ReturnBehavior{rets: make([]Params, 0), params: make(Params, 0)} 15 | r.params = append(r.params, x...) 16 | return r 17 | } 18 | 19 | func Repeat(behavior Behavior, times int) Behavior { 20 | return &RepeatBehavior{rets: make([]Params, 0), behavior: behavior, times: times} 21 | } 22 | -------------------------------------------------------------------------------- /dsl/matcher.go: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | 4 | -------------------------------------------------------------------------------- /dsl/patch_builder.go: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | . "github.com/agiledragon/gomonkey/v2" 8 | ) 9 | 10 | type FuncPara struct { 11 | target interface{} 12 | constraints []Constraint 13 | behaviors []Behavior 14 | } 15 | 16 | type PatchBuilder struct { 17 | patches *Patches 18 | funcPara FuncPara 19 | } 20 | 21 | func NewPatchBuilder(patches *Patches) *PatchBuilder { 22 | funcPara := FuncPara{target: nil, constraints: make([]Constraint, 0), 23 | behaviors: make([]Behavior, 0)} 24 | return &PatchBuilder{patches: patches, funcPara: funcPara} 25 | } 26 | 27 | func (this *PatchBuilder) Func(target interface{}) *PatchBuilder { 28 | this.funcPara.target = target 29 | return this 30 | } 31 | 32 | func (this *PatchBuilder) Stubs() *PatchBuilder { 33 | return this 34 | } 35 | 36 | func (this *PatchBuilder) With(matcher ...Constraint) *PatchBuilder { 37 | this.funcPara.constraints = append(this.funcPara.constraints, matcher...) 38 | return this 39 | } 40 | 41 | func (this *PatchBuilder) Will(behavior Behavior) *PatchBuilder { 42 | this.funcPara.behaviors = append(this.funcPara.behaviors, behavior) 43 | return this 44 | } 45 | 46 | func (this *PatchBuilder) Then(behavior Behavior) *PatchBuilder { 47 | this.funcPara.behaviors = append(this.funcPara.behaviors, behavior) 48 | return this 49 | } 50 | 51 | func (this *PatchBuilder) End() { 52 | funcType := reflect.TypeOf(this.funcPara.target) 53 | t := reflect.ValueOf(this.funcPara.target) 54 | d := reflect.MakeFunc(funcType, func(inputs []reflect.Value) []reflect.Value { 55 | matchers := this.funcPara.constraints 56 | for i, input := range inputs { 57 | if !matchers[i].Eval(input.Interface()) { 58 | info := fmt.Sprintf("input paras %v is not matched", input.Interface()) 59 | panic(info) 60 | } 61 | } 62 | return GetResultValues(funcType, this.funcPara.behaviors[0].Apply()[0]...) 63 | }) 64 | this.patches.ApplyCore(t, d) 65 | } 66 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/agiledragon/gomonkey/v2 2 | 3 | go 1.14 4 | 5 | require github.com/smartystreets/goconvey v1.6.4 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 2 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 3 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 4 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 5 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 6 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 7 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 8 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 9 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 10 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 11 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 12 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 13 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 14 | -------------------------------------------------------------------------------- /jmp_386.go: -------------------------------------------------------------------------------- 1 | package gomonkey 2 | 3 | func buildJmpDirective(double uintptr) []byte { 4 | d0 := byte(double) 5 | d1 := byte(double >> 8) 6 | d2 := byte(double >> 16) 7 | d3 := byte(double >> 24) 8 | 9 | return []byte{ 10 | 0xBA, d0, d1, d2, d3, // MOV edx, double 11 | 0xFF, 0x22, // JMP [edx] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /jmp_amd64.go: -------------------------------------------------------------------------------- 1 | package gomonkey 2 | 3 | func buildJmpDirective(double uintptr) []byte { 4 | d0 := byte(double) 5 | d1 := byte(double >> 8) 6 | d2 := byte(double >> 16) 7 | d3 := byte(double >> 24) 8 | d4 := byte(double >> 32) 9 | d5 := byte(double >> 40) 10 | d6 := byte(double >> 48) 11 | d7 := byte(double >> 56) 12 | 13 | return []byte{ 14 | 0x48, 0xBA, d0, d1, d2, d3, d4, d5, d6, d7, // MOV rdx, double 15 | 0xFF, 0x22, // JMP [rdx] 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /jmp_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build arm64 2 | // +build arm64 3 | 4 | package gomonkey 5 | 6 | import "unsafe" 7 | 8 | func buildJmpDirective(double uintptr) []byte { 9 | res := make([]byte, 0, 24) 10 | d0d1 := double & 0xFFFF 11 | d2d3 := double >> 16 & 0xFFFF 12 | d4d5 := double >> 32 & 0xFFFF 13 | d6d7 := double >> 48 & 0xFFFF 14 | 15 | res = append(res, movImm(0b10, 0, d0d1)...) // MOVZ x26, double[16:0] 16 | res = append(res, movImm(0b11, 1, d2d3)...) // MOVK x26, double[32:16] 17 | res = append(res, movImm(0b11, 2, d4d5)...) // MOVK x26, double[48:32] 18 | res = append(res, movImm(0b11, 3, d6d7)...) // MOVK x26, double[64:48] 19 | res = append(res, []byte{0x4A, 0x03, 0x40, 0xF9}...) // LDR x10, [x26] 20 | res = append(res, []byte{0x40, 0x01, 0x1F, 0xD6}...) // BR x10 21 | 22 | return res 23 | } 24 | 25 | func movImm(opc, shift int, val uintptr) []byte { 26 | var m uint32 = 26 // rd 27 | m |= uint32(val) << 5 // imm16 28 | m |= uint32(shift&3) << 21 // hw 29 | m |= 0b100101 << 23 // const 30 | m |= uint32(opc&0x3) << 29 // opc 31 | m |= 0b1 << 31 // sf 32 | 33 | res := make([]byte, 4) 34 | *(*uint32)(unsafe.Pointer(&res[0])) = m 35 | 36 | return res 37 | } 38 | -------------------------------------------------------------------------------- /jmp_loong64.go: -------------------------------------------------------------------------------- 1 | //go:build loong64 2 | // +build loong64 3 | 4 | package gomonkey 5 | 6 | import "unsafe" 7 | 8 | const ( 9 | REG_R0 uint32 = 0 10 | REG_R29 = 29 11 | REG_R30 = 30 12 | ) 13 | 14 | const ( 15 | OP_ORI uint32 = 0x00E << 22 16 | OP_LU12IW = 0x00A << 25 17 | OP_LU32ID = 0x00B << 25 18 | OP_LU52ID = 0x00C << 22 19 | OP_LDD = 0x0A3 << 22 20 | OP_JIRL = 0x013 << 26 21 | ) 22 | 23 | func buildJmpDirective(double uintptr) []byte { 24 | res := make([]byte, 0, 24) 25 | 26 | bit11_0 := (double >> 0) & 0xFFF 27 | bit31_12 := (double >> 12) & 0xFFFFF 28 | bit51_32 := (double >> 32) & 0xFFFFF 29 | bit63_52 := (double >> 52) & 0xFFF 30 | 31 | // lu12i.w r29, bit31_12 32 | // ori r29, r29, bit11_0 33 | // lu32i.d r29, bit51_32 34 | // lu52i.d r29, bit63_52 35 | // ld.d, r30, r29, 0 36 | // jirl r0, r30, 0 37 | res = append(res, wireup_opc(OP_LU12IW, REG_R29, 0, bit31_12)...) 38 | res = append(res, wireup_opc(OP_ORI, REG_R29, REG_R29, bit11_0)...) 39 | res = append(res, wireup_opc(OP_LU32ID, REG_R29, 0, bit51_32)...) 40 | res = append(res, wireup_opc(OP_LU52ID, REG_R29, REG_R29, bit63_52)...) 41 | res = append(res, wireup_opc(OP_LDD, REG_R30, REG_R29, 0)...) 42 | res = append(res, wireup_opc(OP_JIRL, REG_R0, REG_R30, 0)...) 43 | 44 | return res 45 | } 46 | 47 | func wireup_opc(opc uint32, rd, rj uint32, val uintptr) []byte { 48 | var m uint32 = 0 49 | 50 | switch opc { 51 | case OP_ORI, OP_LU52ID, OP_LDD: 52 | m |= opc 53 | m |= (rd & 0x1F) << 0 // rd 54 | m |= (rj & 0x1F) << 5 // rj 55 | m |= (uint32(val) & 0xFFF) << 10 // si12 56 | 57 | case OP_LU12IW, OP_LU32ID: 58 | m |= opc 59 | m |= (rd & 0x1F) << 0 // rd 60 | m |= (uint32(val) & 0xFFFFF) << 5 // si20 61 | 62 | case OP_JIRL: 63 | m |= opc 64 | m |= (rd & 0x1F) << 0 // rd 65 | m |= (rj & 0x1F) << 5 // rj 66 | m |= (uint32(val) & 0xFFFF) << 10 // si16 67 | } 68 | 69 | res := make([]byte, 4) 70 | *(*uint32)(unsafe.Pointer(&res[0])) = m 71 | 72 | return res 73 | } 74 | -------------------------------------------------------------------------------- /jmp_riscv64.go: -------------------------------------------------------------------------------- 1 | package gomonkey 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | // buildJmpDirective 为 riscv64 架构生成一段跳转指令, 8 | // 将传入的 64 位地址加载到寄存器 x6(t1)中, 9 | // 然后执行 JALR x0, 0(x6) 实现无条件跳转。 10 | func buildJmpDirective(double uintptr) []byte { 11 | var res []byte 12 | // 将地址转换为 64 位无符号整数 13 | d := uint64(double) 14 | // 将 64 位地址拆分成若干部分: 15 | // imm0: 位 [7:0] 16 | // imm1: 位 [19:8] (12 位) 17 | // imm2: 位 [31:20] (12 位) 18 | // imm3: 位 [43:32] (12 位) 19 | // imm4: 位 [63:44] (20 位) 20 | imm0 := d & 0xff 21 | imm1 := (d >> 8) & 0xfff 22 | imm2 := (d >> 20) & 0xfff 23 | imm3 := (d >> 32) & 0xfff 24 | imm4 := (d >> 44) & 0xfffff 25 | 26 | // 依次生成指令: 27 | // 1. LUI x6, imm4 // 将最高 20 位加载到 x6 28 | res = append(res, encodeLUI(6, uint32(imm4))...) 29 | // 2. ADDI x6, x6, imm3 // 加载接下来的 12 位 30 | res = append(res, encodeADDI(6, 6, int32(imm3))...) 31 | // 3. SLLI x6, x6, 12 // 左移 12 位 32 | res = append(res, encodeSLLI(6, 6, 12)...) 33 | // 4. ADDI x6, x6, imm2 // 加载接下来的 12 位 34 | res = append(res, encodeADDI(6, 6, int32(imm2))...) 35 | // 5. SLLI x6, x6, 12 // 再次左移 12 位 36 | res = append(res, encodeSLLI(6, 6, 12)...) 37 | // 6. ADDI x6, x6, imm1 // 加载接下来的 12 位 38 | res = append(res, encodeADDI(6, 6, int32(imm1))...) 39 | // 7. SLLI x6, x6, 8 // 左移 8 位 40 | res = append(res, encodeSLLI(6, 6, 8)...) 41 | // 8. ORI x6, x6, imm0 // 最后加载最低 8 位 42 | res = append(res, encodeORI(6, 6, int32(imm0))...) 43 | // 9. JALR x0, 0(x6) // 跳转到 x6 指定的地址 44 | res = append(res, encodeJALR(0, 6, 0)...) 45 | 46 | return res 47 | } 48 | 49 | // 以下辅助函数生成各条指令的机器码,均返回 4 字节小端表示。 50 | 51 | // LUI 指令格式: 52 | // 31 12 11 7 6 0 53 | // [ imm[31:12] ] [ rd ] [ opcode ] 54 | // opcode LUI 为 0x37。 55 | func encodeLUI(rd int, imm20 uint32) []byte { 56 | inst := (imm20 << 12) | (uint32(rd) << 7) | 0x37 57 | res := make([]byte, 4) 58 | binary.LittleEndian.PutUint32(res, inst) 59 | return res 60 | } 61 | 62 | // ADDI 指令格式(用于加载 12 位立即数): 63 | // 31 20 19 15 14 12 11 7 6 0 64 | // [ imm[11:0] ] [ rs1 ] [funct3] [ rd ] [ opcode ] 65 | // opcode ADDI 为 0x13,funct3 为 0。 66 | func encodeADDI(rd, rs1 int, imm int32) []byte { 67 | inst := ((uint32(imm) & 0xfff) << 20) | (uint32(rs1) << 15) | (uint32(rd) << 7) | 0x13 68 | res := make([]byte, 4) 69 | binary.LittleEndian.PutUint32(res, inst) 70 | return res 71 | } 72 | 73 | // SLLI 指令格式: 74 | // 31 26 25 20 19 15 14 12 11 7 6 0 75 | // [ 0 ] [ shamt ] [ rs1 ] [funct3] [ rd ] [ opcode ] 76 | // opcode 为 0x13,funct3 为 1。 77 | func encodeSLLI(rd, rs1, shamt int) []byte { 78 | inst := (uint32(shamt) << 20) | (uint32(rs1) << 15) | (1 << 12) | (uint32(rd) << 7) | 0x13 79 | res := make([]byte, 4) 80 | binary.LittleEndian.PutUint32(res, inst) 81 | return res 82 | } 83 | 84 | // ORI 指令格式: 85 | // 31 20 19 15 14 12 11 7 6 0 86 | // [ imm[11:0] ] [ rs1 ] [funct3] [ rd ] [ opcode ] 87 | // opcode 为 0x13,funct3 为 6。 88 | func encodeORI(rd, rs1 int, imm int32) []byte { 89 | inst := ((uint32(imm) & 0xfff) << 20) | (uint32(rs1) << 15) | (6 << 12) | (uint32(rd) << 7) | 0x13 90 | res := make([]byte, 4) 91 | binary.LittleEndian.PutUint32(res, inst) 92 | return res 93 | } 94 | 95 | // JALR 指令格式: 96 | // 31 20 19 15 14 12 11 7 6 0 97 | // [ imm[11:0] ] [ rs1 ] [funct3] [ rd ] [ opcode ] 98 | // opcode 为 0x67,funct3 为 0。 99 | // JALR x0, 0(x6) 用于无条件跳转。 100 | func encodeJALR(rd, rs1 int, imm int32) []byte { 101 | inst := ((uint32(imm) & 0xfff) << 20) | (uint32(rs1) << 15) | (uint32(rd) << 7) | 0x67 102 | res := make([]byte, 4) 103 | binary.LittleEndian.PutUint32(res, inst) 104 | return res 105 | } 106 | -------------------------------------------------------------------------------- /modify_binary_darwin.go: -------------------------------------------------------------------------------- 1 | package gomonkey 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | func PtrOf(val []byte) uintptr { 11 | return (*reflect.SliceHeader)(unsafe.Pointer(&val)).Data 12 | } 13 | 14 | func modifyBinary(target uintptr, bytes []byte) { 15 | targetPage := pageStart(target) 16 | res := write(target, PtrOf(bytes), len(bytes), targetPage, syscall.Getpagesize(), syscall.PROT_READ|syscall.PROT_EXEC) 17 | if res != 0 { 18 | panic(fmt.Errorf("failed to write memory, code %v", res)) 19 | } 20 | } 21 | 22 | //go:cgo_import_dynamic mach_task_self mach_task_self "/usr/lib/libSystem.B.dylib" 23 | //go:cgo_import_dynamic mach_vm_protect mach_vm_protect "/usr/lib/libSystem.B.dylib" 24 | func write(target, data uintptr, len int, page uintptr, pageSize, oriProt int) int 25 | -------------------------------------------------------------------------------- /modify_binary_linux.go: -------------------------------------------------------------------------------- 1 | package gomonkey 2 | 3 | import "syscall" 4 | 5 | func modifyBinary(target uintptr, bytes []byte) { 6 | function := entryAddress(target, len(bytes)) 7 | err := mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC) 8 | if err != nil { 9 | panic(err) 10 | } 11 | copy(function, bytes) 12 | err = mprotectCrossPage(target, len(bytes), syscall.PROT_READ|syscall.PROT_EXEC) 13 | if err != nil { 14 | panic(err) 15 | } 16 | } 17 | 18 | func mprotectCrossPage(addr uintptr, length int, prot int) error { 19 | pageSize := syscall.Getpagesize() 20 | for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) { 21 | page := entryAddress(p, pageSize) 22 | if err := syscall.Mprotect(page, prot); err != nil { 23 | return err 24 | } 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /modify_binary_windows.go: -------------------------------------------------------------------------------- 1 | package gomonkey 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | func modifyBinary(target uintptr, bytes []byte) { 9 | function := entryAddress(target, len(bytes)) 10 | 11 | proc := syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect") 12 | const PROT_READ_WRITE = 0x40 13 | var old uint32 14 | result, _, _ := proc.Call(target, uintptr(len(bytes)), uintptr(PROT_READ_WRITE), uintptr(unsafe.Pointer(&old))) 15 | if result == 0 { 16 | panic(result) 17 | } 18 | copy(function, bytes) 19 | 20 | var ignore uint32 21 | result, _, _ = proc.Call(target, uintptr(len(bytes)), uintptr(old), uintptr(unsafe.Pointer(&ignore))) 22 | if result == 0 { 23 | panic(result) 24 | } 25 | } -------------------------------------------------------------------------------- /patch.go: -------------------------------------------------------------------------------- 1 | package gomonkey 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/agiledragon/gomonkey/v2/creflect" 10 | ) 11 | 12 | type Patches struct { 13 | originals map[uintptr][]byte 14 | targets map[uintptr]uintptr 15 | values map[reflect.Value]reflect.Value 16 | valueHolders map[reflect.Value]reflect.Value 17 | } 18 | 19 | type Params []interface{} 20 | type OutputCell struct { 21 | Values Params 22 | Times int 23 | } 24 | 25 | func ApplyFunc(target, double interface{}) *Patches { 26 | return create().ApplyFunc(target, double) 27 | } 28 | 29 | func ApplyMethod(target interface{}, methodName string, double interface{}) *Patches { 30 | return create().ApplyMethod(target, methodName, double) 31 | } 32 | 33 | func ApplyMethodFunc(target interface{}, methodName string, doubleFunc interface{}) *Patches { 34 | return create().ApplyMethodFunc(target, methodName, doubleFunc) 35 | } 36 | 37 | func ApplyPrivateMethod(target interface{}, methodName string, double interface{}) *Patches { 38 | return create().ApplyPrivateMethod(target, methodName, double) 39 | } 40 | 41 | func ApplyGlobalVar(target, double interface{}) *Patches { 42 | return create().ApplyGlobalVar(target, double) 43 | } 44 | 45 | func ApplyFuncVar(target, double interface{}) *Patches { 46 | return create().ApplyFuncVar(target, double) 47 | } 48 | 49 | func ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches { 50 | return create().ApplyFuncSeq(target, outputs) 51 | } 52 | 53 | func ApplyMethodSeq(target interface{}, methodName string, outputs []OutputCell) *Patches { 54 | return create().ApplyMethodSeq(target, methodName, outputs) 55 | } 56 | 57 | func ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *Patches { 58 | return create().ApplyFuncVarSeq(target, outputs) 59 | } 60 | 61 | func ApplyFuncReturn(target interface{}, output ...interface{}) *Patches { 62 | return create().ApplyFuncReturn(target, output...) 63 | } 64 | 65 | func ApplyMethodReturn(target interface{}, methodName string, output ...interface{}) *Patches { 66 | return create().ApplyMethodReturn(target, methodName, output...) 67 | } 68 | 69 | func ApplyFuncVarReturn(target interface{}, output ...interface{}) *Patches { 70 | return create().ApplyFuncVarReturn(target, output...) 71 | } 72 | 73 | func create() *Patches { 74 | return &Patches{originals: make(map[uintptr][]byte), targets: map[uintptr]uintptr{}, 75 | values: make(map[reflect.Value]reflect.Value), valueHolders: make(map[reflect.Value]reflect.Value)} 76 | } 77 | 78 | func NewPatches() *Patches { 79 | return create() 80 | } 81 | 82 | func (this *Patches) Origin(fn func()) { 83 | for target, bytes := range this.originals { 84 | modifyBinary(target, bytes) 85 | } 86 | fn() 87 | for target, targetPtr := range this.targets { 88 | code := buildJmpDirective(targetPtr) 89 | modifyBinary(target, code) 90 | } 91 | } 92 | 93 | func (this *Patches) ApplyFunc(target, double interface{}) *Patches { 94 | t := reflect.ValueOf(target) 95 | d := reflect.ValueOf(double) 96 | return this.ApplyCore(t, d) 97 | } 98 | 99 | func (this *Patches) ApplyMethod(target interface{}, methodName string, double interface{}) *Patches { 100 | m, ok := castRType(target).MethodByName(methodName) 101 | if !ok { 102 | panic("retrieve method by name failed") 103 | } 104 | d := reflect.ValueOf(double) 105 | return this.ApplyCore(m.Func, d) 106 | } 107 | 108 | func (this *Patches) ApplyMethodFunc(target interface{}, methodName string, doubleFunc interface{}) *Patches { 109 | m, ok := castRType(target).MethodByName(methodName) 110 | if !ok { 111 | panic("retrieve method by name failed") 112 | } 113 | d := funcToMethod(m.Type, doubleFunc) 114 | return this.ApplyCore(m.Func, d) 115 | } 116 | 117 | func (this *Patches) ApplyPrivateMethod(target interface{}, methodName string, double interface{}) *Patches { 118 | m, ok := creflect.MethodByName(castRType(target), methodName) 119 | if !ok { 120 | panic("retrieve method by name failed") 121 | } 122 | d := reflect.ValueOf(double) 123 | return this.ApplyCoreOnlyForPrivateMethod(m, d) 124 | } 125 | 126 | func (this *Patches) ApplyGlobalVar(target, double interface{}) *Patches { 127 | t := reflect.ValueOf(target) 128 | if t.Type().Kind() != reflect.Ptr { 129 | panic("target is not a pointer") 130 | } 131 | 132 | this.values[t] = reflect.ValueOf(t.Elem().Interface()) 133 | d := reflect.ValueOf(double) 134 | t.Elem().Set(d) 135 | return this 136 | } 137 | 138 | func (this *Patches) ApplyFuncVar(target, double interface{}) *Patches { 139 | t := reflect.ValueOf(target) 140 | d := reflect.ValueOf(double) 141 | if t.Type().Kind() != reflect.Ptr { 142 | panic("target is not a pointer") 143 | } 144 | this.check(t.Elem(), d) 145 | return this.ApplyGlobalVar(target, double) 146 | } 147 | 148 | func (this *Patches) ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches { 149 | funcType := reflect.TypeOf(target) 150 | t := reflect.ValueOf(target) 151 | d := getDoubleFunc(funcType, outputs) 152 | return this.ApplyCore(t, d) 153 | } 154 | 155 | func (this *Patches) ApplyMethodSeq(target interface{}, methodName string, outputs []OutputCell) *Patches { 156 | m, ok := castRType(target).MethodByName(methodName) 157 | if !ok { 158 | panic("retrieve method by name failed") 159 | } 160 | d := getDoubleFunc(m.Type, outputs) 161 | return this.ApplyCore(m.Func, d) 162 | } 163 | 164 | func (this *Patches) ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *Patches { 165 | t := reflect.ValueOf(target) 166 | if t.Type().Kind() != reflect.Ptr { 167 | panic("target is not a pointer") 168 | } 169 | if t.Elem().Kind() != reflect.Func { 170 | panic("target is not a func") 171 | } 172 | 173 | funcType := reflect.TypeOf(target).Elem() 174 | double := getDoubleFunc(funcType, outputs).Interface() 175 | return this.ApplyGlobalVar(target, double) 176 | } 177 | 178 | func (this *Patches) ApplyFuncReturn(target interface{}, returns ...interface{}) *Patches { 179 | funcType := reflect.TypeOf(target) 180 | t := reflect.ValueOf(target) 181 | outputs := []OutputCell{{Values: returns, Times: -1}} 182 | d := getDoubleFunc(funcType, outputs) 183 | return this.ApplyCore(t, d) 184 | } 185 | 186 | func (this *Patches) ApplyMethodReturn(target interface{}, methodName string, returns ...interface{}) *Patches { 187 | m, ok := reflect.TypeOf(target).MethodByName(methodName) 188 | if !ok { 189 | panic("retrieve method by name failed") 190 | } 191 | 192 | outputs := []OutputCell{{Values: returns, Times: -1}} 193 | d := getDoubleFunc(m.Type, outputs) 194 | return this.ApplyCore(m.Func, d) 195 | } 196 | 197 | func (this *Patches) ApplyFuncVarReturn(target interface{}, returns ...interface{}) *Patches { 198 | t := reflect.ValueOf(target) 199 | if t.Type().Kind() != reflect.Ptr { 200 | panic("target is not a pointer") 201 | } 202 | if t.Elem().Kind() != reflect.Func { 203 | panic("target is not a func") 204 | } 205 | 206 | funcType := reflect.TypeOf(target).Elem() 207 | outputs := []OutputCell{{Values: returns, Times: -1}} 208 | double := getDoubleFunc(funcType, outputs).Interface() 209 | return this.ApplyGlobalVar(target, double) 210 | } 211 | 212 | func (this *Patches) Reset() { 213 | for target, bytes := range this.originals { 214 | modifyBinary(target, bytes) 215 | delete(this.originals, target) 216 | } 217 | 218 | for target, variable := range this.values { 219 | target.Elem().Set(variable) 220 | } 221 | } 222 | 223 | func (this *Patches) ApplyCore(target, double reflect.Value) *Patches { 224 | this.check(target, double) 225 | assTarget := *(*uintptr)(getPointer(target)) 226 | original := replace(assTarget, uintptr(getPointer(double))) 227 | if _, ok := this.originals[assTarget]; !ok { 228 | this.originals[assTarget] = original 229 | } 230 | this.targets[assTarget] = uintptr(getPointer(double)) 231 | this.valueHolders[double] = double 232 | return this 233 | } 234 | 235 | func (this *Patches) ApplyCoreOnlyForPrivateMethod(target unsafe.Pointer, double reflect.Value) *Patches { 236 | if double.Kind() != reflect.Func { 237 | panic("double is not a func") 238 | } 239 | assTarget := *(*uintptr)(target) 240 | original := replace(assTarget, uintptr(getPointer(double))) 241 | if _, ok := this.originals[assTarget]; !ok { 242 | this.originals[assTarget] = original 243 | } 244 | this.targets[assTarget] = uintptr(getPointer(double)) 245 | this.valueHolders[double] = double 246 | return this 247 | } 248 | 249 | func (this *Patches) check(target, double reflect.Value) { 250 | if target.Kind() != reflect.Func { 251 | panic("target is not a func") 252 | } 253 | 254 | if double.Kind() != reflect.Func { 255 | panic("double is not a func") 256 | } 257 | 258 | targetType := target.Type() 259 | doubleType := double.Type() 260 | 261 | if targetType.NumIn() < doubleType.NumIn() || 262 | targetType.NumOut() != doubleType.NumOut() || 263 | (targetType.NumIn() == doubleType.NumIn() && targetType.IsVariadic() != doubleType.IsVariadic()) { 264 | panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) 265 | } 266 | 267 | for i, size := 0, doubleType.NumIn(); i < size; i++ { 268 | targetIn := targetType.In(i) 269 | doubleIn := doubleType.In(i) 270 | 271 | if targetIn.AssignableTo(doubleIn) { 272 | continue 273 | } 274 | 275 | panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) 276 | } 277 | 278 | for i, size := 0, doubleType.NumOut(); i < size; i++ { 279 | targetOut := targetType.Out(i) 280 | doubleOut := doubleType.Out(i) 281 | 282 | if targetOut.AssignableTo(doubleOut) { 283 | continue 284 | } 285 | 286 | panic(fmt.Sprintf("target type(%s) and double type(%s) are different", target.Type(), double.Type())) 287 | } 288 | } 289 | 290 | func replace(target, double uintptr) []byte { 291 | code := buildJmpDirective(double) 292 | bytes := entryAddress(target, len(code)) 293 | original := make([]byte, len(bytes)) 294 | copy(original, bytes) 295 | modifyBinary(target, code) 296 | return original 297 | } 298 | 299 | func getDoubleFunc(funcType reflect.Type, outputs []OutputCell) reflect.Value { 300 | if funcType.NumOut() != len(outputs[0].Values) { 301 | panic(fmt.Sprintf("func type has %v return values, but only %v values provided as double", 302 | funcType.NumOut(), len(outputs[0].Values))) 303 | } 304 | 305 | needReturn := false 306 | slice := make([]Params, 0) 307 | for _, output := range outputs { 308 | if output.Times == -1 { 309 | needReturn = true 310 | slice = []Params{output.Values} 311 | break 312 | } 313 | t := 0 314 | if output.Times <= 1 { 315 | t = 1 316 | } else { 317 | t = output.Times 318 | } 319 | for j := 0; j < t; j++ { 320 | slice = append(slice, output.Values) 321 | } 322 | } 323 | 324 | i := 0 325 | lenOutputs := len(slice) 326 | return reflect.MakeFunc(funcType, func(_ []reflect.Value) []reflect.Value { 327 | if needReturn { 328 | return GetResultValues(funcType, slice[0]...) 329 | } 330 | if i < lenOutputs { 331 | i++ 332 | return GetResultValues(funcType, slice[i-1]...) 333 | } 334 | panic("double seq is less than call seq") 335 | }) 336 | } 337 | 338 | func GetResultValues(funcType reflect.Type, results ...interface{}) []reflect.Value { 339 | var resultValues []reflect.Value 340 | for i, r := range results { 341 | var resultValue reflect.Value 342 | if r == nil { 343 | resultValue = reflect.Zero(funcType.Out(i)) 344 | } else { 345 | v := reflect.New(funcType.Out(i)) 346 | v.Elem().Set(reflect.ValueOf(r)) 347 | resultValue = v.Elem() 348 | } 349 | resultValues = append(resultValues, resultValue) 350 | } 351 | return resultValues 352 | } 353 | 354 | type funcValue struct { 355 | _ uintptr 356 | p unsafe.Pointer 357 | } 358 | 359 | func getPointer(v reflect.Value) unsafe.Pointer { 360 | return (*funcValue)(unsafe.Pointer(&v)).p 361 | } 362 | 363 | func entryAddress(p uintptr, l int) []byte { 364 | return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: p, Len: l, Cap: l})) 365 | } 366 | 367 | func pageStart(ptr uintptr) uintptr { 368 | return ptr & ^(uintptr(syscall.Getpagesize() - 1)) 369 | } 370 | 371 | func funcToMethod(funcType reflect.Type, doubleFunc interface{}) reflect.Value { 372 | rf := reflect.TypeOf(doubleFunc) 373 | if rf.Kind() != reflect.Func { 374 | panic("doubleFunc is not a func") 375 | } 376 | vf := reflect.ValueOf(doubleFunc) 377 | return reflect.MakeFunc(funcType, func(in []reflect.Value) []reflect.Value { 378 | if funcType.IsVariadic() { 379 | return vf.CallSlice(in[1:]) 380 | } else { 381 | return vf.Call(in[1:]) 382 | } 383 | }) 384 | } 385 | 386 | func castRType(val interface{}) reflect.Type { 387 | if rTypeVal, ok := val.(reflect.Type); ok { 388 | return rTypeVal 389 | } 390 | return reflect.TypeOf(val) 391 | } 392 | -------------------------------------------------------------------------------- /test/apply_func_return_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | /* 12 | compare with apply_func_seq_test.go 13 | */ 14 | func TestApplyFuncReturn(t *testing.T) { 15 | Convey("TestApplyFuncReturn", t, func() { 16 | 17 | Convey("declares the values to be returned", func() { 18 | info1 := "hello cpp" 19 | 20 | patches := ApplyFuncReturn(fake.ReadLeaf, info1, nil) 21 | defer patches.Reset() 22 | 23 | for i := 0; i < 10; i++ { 24 | output, err := fake.ReadLeaf("") 25 | So(err, ShouldEqual, nil) 26 | So(output, ShouldEqual, info1) 27 | } 28 | 29 | patches.Reset() // if not reset will occur:patch has been existed 30 | info2 := "hello golang" 31 | patches.ApplyFuncReturn(fake.ReadLeaf, info2, nil) 32 | for i := 0; i < 10; i++ { 33 | output, err := fake.ReadLeaf("") 34 | So(err, ShouldEqual, nil) 35 | So(output, ShouldEqual, info2) 36 | } 37 | }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /test/apply_func_seq_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | 7 | . "github.com/agiledragon/gomonkey/v2" 8 | "github.com/agiledragon/gomonkey/v2/test/fake" 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestApplyFuncSeq(t *testing.T) { 13 | Convey("TestApplyFuncSeq", t, func() { 14 | 15 | Convey("default times is 1", func() { 16 | info1 := "hello cpp" 17 | info2 := "hello golang" 18 | info3 := "hello gomonkey" 19 | outputs := []OutputCell{ 20 | {Values: Params{info1, nil}}, 21 | {Values: Params{info2, nil}}, 22 | {Values: Params{info3, nil}}, 23 | } 24 | patches := ApplyFuncSeq(fake.ReadLeaf, outputs) 25 | defer patches.Reset() 26 | 27 | runtime.GC() 28 | 29 | output, err := fake.ReadLeaf("") 30 | So(err, ShouldEqual, nil) 31 | So(output, ShouldEqual, info1) 32 | output, err = fake.ReadLeaf("") 33 | So(err, ShouldEqual, nil) 34 | So(output, ShouldEqual, info2) 35 | output, err = fake.ReadLeaf("") 36 | So(err, ShouldEqual, nil) 37 | So(output, ShouldEqual, info3) 38 | }) 39 | 40 | Convey("retry succ util the third times", func() { 41 | info1 := "hello cpp" 42 | outputs := []OutputCell{ 43 | {Values: Params{"", fake.ErrActual}, Times: 2}, 44 | {Values: Params{info1, nil}}, 45 | } 46 | patches := ApplyFuncSeq(fake.ReadLeaf, outputs) 47 | defer patches.Reset() 48 | output, err := fake.ReadLeaf("") 49 | So(err, ShouldEqual, fake.ErrActual) 50 | output, err = fake.ReadLeaf("") 51 | So(err, ShouldEqual, fake.ErrActual) 52 | output, err = fake.ReadLeaf("") 53 | So(err, ShouldEqual, nil) 54 | So(output, ShouldEqual, info1) 55 | }) 56 | 57 | Convey("batch operations failed on the third time", func() { 58 | info1 := "hello gomonkey" 59 | outputs := []OutputCell{ 60 | {Values: Params{info1, nil}, Times: 2}, 61 | {Values: Params{"", fake.ErrActual}}, 62 | } 63 | patches := ApplyFuncSeq(fake.ReadLeaf, outputs) 64 | defer patches.Reset() 65 | output, err := fake.ReadLeaf("") 66 | So(err, ShouldEqual, nil) 67 | So(output, ShouldEqual, info1) 68 | output, err = fake.ReadLeaf("") 69 | So(err, ShouldEqual, nil) 70 | So(output, ShouldEqual, info1) 71 | output, err = fake.ReadLeaf("") 72 | So(err, ShouldEqual, fake.ErrActual) 73 | }) 74 | 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /test/apply_func_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | . "github.com/agiledragon/gomonkey/v2" 8 | "github.com/agiledragon/gomonkey/v2/test/fake" 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | var ( 13 | outputExpect = "xxx-vethName100-yyy" 14 | ) 15 | 16 | func TestApplyFunc(t *testing.T) { 17 | Convey("TestApplyFunc", t, func() { 18 | 19 | Convey("one func for succ", func() { 20 | patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { 21 | return outputExpect, nil 22 | }) 23 | defer patches.Reset() 24 | output, err := fake.Exec("", "") 25 | So(err, ShouldEqual, nil) 26 | So(output, ShouldEqual, outputExpect) 27 | }) 28 | 29 | Convey("one func for succ with origin", func() { 30 | patches := ApplyFunc(fake.Belong, func(_ string, _ []string) bool { 31 | return false 32 | }) 33 | defer patches.Reset() 34 | output := fake.Belong("a", []string{"a", "b"}) 35 | So(output, ShouldEqual, false) 36 | patches.Origin(func() { 37 | output = fake.Belong("a", []string{"a", "b"}) 38 | }) 39 | So(output, ShouldEqual, true) 40 | }) 41 | 42 | Convey("one func for succ with origin inside", func() { 43 | var output bool 44 | var patches *Patches 45 | patches = ApplyFunc(fake.Belong, func(_ string, _ []string) bool { 46 | patches.Origin(func() { 47 | output = fake.Belong("a", []string{"a", "b"}) 48 | So(output, ShouldEqual, true) 49 | }) 50 | return false 51 | }) 52 | defer patches.Reset() 53 | }) 54 | 55 | Convey("one func for fail", func() { 56 | patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { 57 | return "", fake.ErrActual 58 | }) 59 | defer patches.Reset() 60 | output, err := fake.Exec("", "") 61 | So(err, ShouldEqual, fake.ErrActual) 62 | So(output, ShouldEqual, "") 63 | }) 64 | 65 | Convey("two funcs", func() { 66 | patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { 67 | return outputExpect, nil 68 | }) 69 | defer patches.Reset() 70 | patches.ApplyFunc(fake.Belong, func(_ string, _ []string) bool { 71 | return true 72 | }) 73 | output, err := fake.Exec("", "") 74 | So(err, ShouldEqual, nil) 75 | So(output, ShouldEqual, outputExpect) 76 | flag := fake.Belong("", nil) 77 | So(flag, ShouldBeTrue) 78 | }) 79 | 80 | Convey("two funcs with origin", func() { 81 | patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { 82 | return outputExpect, nil 83 | }) 84 | defer patches.Reset() 85 | patches.ApplyFunc(fake.Belong, func(_ string, _ []string) bool { 86 | return true 87 | }) 88 | output, err := fake.Exec("", "") 89 | So(err, ShouldEqual, nil) 90 | So(output, ShouldEqual, outputExpect) 91 | flag := fake.Belong("", nil) 92 | So(flag, ShouldBeTrue) 93 | 94 | var outputBool bool 95 | patches.Origin(func() { 96 | outputBool = fake.Belong("c", []string{"a", "b"}) 97 | }) 98 | So(outputBool, ShouldEqual, false) 99 | }) 100 | 101 | Convey("input and output param", func() { 102 | patches := ApplyFunc(json.Unmarshal, func(data []byte, v interface{}) error { 103 | if data == nil { 104 | panic("input param is nil!") 105 | } 106 | p := v.(*map[int]int) 107 | *p = make(map[int]int) 108 | (*p)[1] = 2 109 | (*p)[2] = 4 110 | return nil 111 | }) 112 | defer patches.Reset() 113 | var m map[int]int 114 | err := json.Unmarshal([]byte("123"), &m) 115 | So(err, ShouldEqual, nil) 116 | So(m[1], ShouldEqual, 2) 117 | So(m[2], ShouldEqual, 4) 118 | }) 119 | 120 | Convey("repeat patch same func", func() { 121 | patches := ApplyFunc(fake.ReadLeaf, func(_ string) (string, error) { 122 | return "patch1", nil 123 | }) 124 | output, err := fake.ReadLeaf("") 125 | So(err, ShouldEqual, nil) 126 | So(output, ShouldEqual, "patch1") 127 | 128 | patches.ApplyFunc(fake.ReadLeaf, func(_ string) (string, error) { 129 | return "patch2", nil 130 | }) 131 | output, err = fake.ReadLeaf("") 132 | So(err, ShouldEqual, nil) 133 | So(output, ShouldEqual, "patch2") 134 | 135 | patches.Reset() 136 | output, err = fake.ReadLeaf("") 137 | So(err, ShouldEqual, nil) 138 | So(output, ShouldEqual, "Hello, World!") 139 | }) 140 | 141 | Convey("declare partial args", func() { 142 | patches := ApplyFunc(fake.Exec, func() (string, error) { 143 | return outputExpect, nil 144 | }) 145 | defer patches.Reset() 146 | output, err := fake.Exec("", "") 147 | So(err, ShouldEqual, nil) 148 | So(output, ShouldEqual, outputExpect) 149 | 150 | patches.ApplyFunc(fake.Exec, func(_ string) (string, error) { 151 | return outputExpect, nil 152 | }) 153 | output, err = fake.Exec("", "") 154 | So(err, ShouldEqual, nil) 155 | So(output, ShouldEqual, outputExpect) 156 | 157 | So(func() { 158 | patches.ApplyFunc(fake.Exec, func(_ string, _ []string) (string, error) { 159 | return outputExpect, nil 160 | }) 161 | }, ShouldPanic) 162 | 163 | patches.ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { 164 | return outputExpect, nil 165 | }) 166 | output, err = fake.Exec("", "") 167 | So(err, ShouldEqual, nil) 168 | So(output, ShouldEqual, outputExpect) 169 | }) 170 | }) 171 | } 172 | -------------------------------------------------------------------------------- /test/apply_func_var_return_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | /* 12 | compare with apply_func_var_seq_test.go 13 | */ 14 | func TestApplyFuncVarReturn(t *testing.T) { 15 | Convey("TestApplyFuncVarReturn", t, func() { 16 | 17 | Convey("declares the values to be returned", func() { 18 | info1 := "hello cpp" 19 | 20 | patches := ApplyFuncVarReturn(&fake.Marshal, []byte(info1), nil) 21 | defer patches.Reset() 22 | for i := 0; i < 10; i++ { 23 | bytes, err := fake.Marshal("") 24 | So(err, ShouldEqual, nil) 25 | So(string(bytes), ShouldEqual, info1) 26 | } 27 | 28 | info2 := "hello golang" 29 | patches.ApplyFuncVarReturn(&fake.Marshal, []byte(info2), nil) 30 | for i := 0; i < 10; i++ { 31 | bytes, err := fake.Marshal("") 32 | So(err, ShouldEqual, nil) 33 | So(string(bytes), ShouldEqual, info2) 34 | } 35 | }) 36 | 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /test/apply_func_var_seq_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestApplyFuncVarSeq(t *testing.T) { 12 | Convey("TestApplyFuncVarSeq", t, func() { 13 | 14 | Convey("default times is 1", func() { 15 | info1 := "hello cpp" 16 | info2 := "hello golang" 17 | info3 := "hello gomonkey" 18 | outputs := []OutputCell{ 19 | {Values: Params{[]byte(info1), nil}}, 20 | {Values: Params{[]byte(info2), nil}}, 21 | {Values: Params{[]byte(info3), nil}}, 22 | } 23 | patches := ApplyFuncVarSeq(&fake.Marshal, outputs) 24 | defer patches.Reset() 25 | bytes, err := fake.Marshal("") 26 | So(err, ShouldEqual, nil) 27 | So(string(bytes), ShouldEqual, info1) 28 | bytes, err = fake.Marshal("") 29 | So(err, ShouldEqual, nil) 30 | So(string(bytes), ShouldEqual, info2) 31 | bytes, err = fake.Marshal("") 32 | So(err, ShouldEqual, nil) 33 | So(string(bytes), ShouldEqual, info3) 34 | }) 35 | 36 | Convey("retry succ util the third times", func() { 37 | info1 := "hello cpp" 38 | outputs := []OutputCell{ 39 | {Values: Params{[]byte(""), fake.ErrActual}, Times: 2}, 40 | {Values: Params{[]byte(info1), nil}}, 41 | } 42 | patches := ApplyFuncVarSeq(&fake.Marshal, outputs) 43 | defer patches.Reset() 44 | bytes, err := fake.Marshal("") 45 | So(err, ShouldEqual, fake.ErrActual) 46 | bytes, err = fake.Marshal("") 47 | So(err, ShouldEqual, fake.ErrActual) 48 | bytes, err = fake.Marshal("") 49 | So(err, ShouldEqual, nil) 50 | So(string(bytes), ShouldEqual, info1) 51 | }) 52 | 53 | Convey("batch operations failed on the third time", func() { 54 | info1 := "hello gomonkey" 55 | outputs := []OutputCell{ 56 | {Values: Params{[]byte(info1), nil}, Times: 2}, 57 | {Values: Params{[]byte(""), fake.ErrActual}}, 58 | } 59 | patches := ApplyFuncVarSeq(&fake.Marshal, outputs) 60 | defer patches.Reset() 61 | bytes, err := fake.Marshal("") 62 | So(err, ShouldEqual, nil) 63 | So(string(bytes), ShouldEqual, info1) 64 | bytes, err = fake.Marshal("") 65 | So(err, ShouldEqual, nil) 66 | So(string(bytes), ShouldEqual, info1) 67 | bytes, err = fake.Marshal("") 68 | So(err, ShouldEqual, fake.ErrActual) 69 | }) 70 | 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /test/apply_func_var_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestApplyFuncVar(t *testing.T) { 12 | Convey("TestApplyFuncVar", t, func() { 13 | 14 | Convey("for succ", func() { 15 | str := "hello" 16 | patches := ApplyFuncVar(&fake.Marshal, func(_ interface{}) ([]byte, error) { 17 | return []byte(str), nil 18 | }) 19 | defer patches.Reset() 20 | bytes, err := fake.Marshal(nil) 21 | So(err, ShouldEqual, nil) 22 | So(string(bytes), ShouldEqual, str) 23 | }) 24 | 25 | Convey("for fail", func() { 26 | patches := ApplyFuncVar(&fake.Marshal, func(_ interface{}) ([]byte, error) { 27 | return nil, fake.ErrActual 28 | }) 29 | defer patches.Reset() 30 | _, err := fake.Marshal(nil) 31 | So(err, ShouldEqual, fake.ErrActual) 32 | }) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /test/apply_global_var_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | . "github.com/smartystreets/goconvey/convey" 8 | ) 9 | 10 | var num = 10 11 | 12 | func TestApplyGlobalVar(t *testing.T) { 13 | Convey("TestApplyGlobalVar", t, func() { 14 | 15 | Convey("change", func() { 16 | patches := ApplyGlobalVar(&num, 150) 17 | defer patches.Reset() 18 | So(num, ShouldEqual, 150) 19 | }) 20 | 21 | Convey("recover", func() { 22 | So(num, ShouldEqual, 10) 23 | }) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /test/apply_interface_reused_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestApplyInterfaceReused(t *testing.T) { 12 | e := &fake.Etcd{} 13 | 14 | Convey("TestApplyInterfaceReused", t, func() { 15 | patches := ApplyFunc(fake.NewDb, func(_ string) fake.Db { 16 | return e 17 | }) 18 | defer patches.Reset() 19 | db := fake.NewDb("mysql") 20 | 21 | Convey("TestApplyInterface", func() { 22 | info := "hello interface" 23 | patches.ApplyMethod(e, "Retrieve", 24 | func(_ *fake.Etcd, _ string) (string, error) { 25 | return info, nil 26 | }) 27 | output, err := db.Retrieve("") 28 | So(err, ShouldEqual, nil) 29 | So(output, ShouldEqual, info) 30 | }) 31 | 32 | Convey("TestApplyInterfaceSeq", func() { 33 | info1 := "hello cpp" 34 | info2 := "hello golang" 35 | info3 := "hello gomonkey" 36 | outputs := []OutputCell{ 37 | {Values: Params{info1, nil}}, 38 | {Values: Params{info2, nil}}, 39 | {Values: Params{info3, nil}}, 40 | } 41 | patches.ApplyMethodSeq(e, "Retrieve", outputs) 42 | output, err := db.Retrieve("") 43 | So(err, ShouldEqual, nil) 44 | So(output, ShouldEqual, info1) 45 | output, err = db.Retrieve("") 46 | So(err, ShouldEqual, nil) 47 | So(output, ShouldEqual, info2) 48 | output, err = db.Retrieve("") 49 | So(err, ShouldEqual, nil) 50 | So(output, ShouldEqual, info3) 51 | }) 52 | 53 | Convey("the arg type can be interface", func() { 54 | info := "hello interface" 55 | patches.ApplyMethod(e, "Retrieve", 56 | func(_ fake.Db, _ string) (string, error) { 57 | return info, nil 58 | }) 59 | output, err := db.Retrieve("") 60 | So(err, ShouldEqual, nil) 61 | So(output, ShouldEqual, info) 62 | }) 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /test/apply_method_func_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | /* 12 | compare with apply_method_test.go, no need pass receiver 13 | */ 14 | 15 | func TestApplyMethodFunc(t *testing.T) { 16 | slice := fake.NewSlice() 17 | var s *fake.Slice 18 | Convey("TestApplyMethodFunc", t, func() { 19 | Convey("for succ", func() { 20 | err := slice.Add(1) 21 | So(err, ShouldEqual, nil) 22 | patches := ApplyMethodFunc(s, "Add", func(_ int) error { 23 | return nil 24 | }) 25 | defer patches.Reset() 26 | err = slice.Add(1) 27 | So(err, ShouldEqual, nil) 28 | err = slice.Remove(1) 29 | So(err, ShouldEqual, nil) 30 | So(len(slice), ShouldEqual, 0) 31 | }) 32 | 33 | Convey("for origin", func() { 34 | patches := ApplyMethodFunc(s, "Add", func(_ int) error { 35 | return nil 36 | }) 37 | defer patches.Reset() 38 | 39 | var err error 40 | patches.Origin(func() { 41 | err = slice.Add(1) 42 | So(err, ShouldEqual, nil) 43 | err = slice.Add(1) 44 | So(err, ShouldEqual, fake.ErrElemExsit) 45 | err = slice.Remove(1) 46 | So(err, ShouldEqual, nil) 47 | }) 48 | So(len(slice), ShouldEqual, 0) 49 | }) 50 | 51 | Convey("for already exist", func() { 52 | err := slice.Add(2) 53 | So(err, ShouldEqual, nil) 54 | patches := ApplyMethodFunc(s, "Add", func(_ int) error { 55 | return fake.ErrElemExsit 56 | }) 57 | defer patches.Reset() 58 | err = slice.Add(1) 59 | So(err, ShouldEqual, fake.ErrElemExsit) 60 | err = slice.Remove(2) 61 | So(err, ShouldEqual, nil) 62 | So(len(slice), ShouldEqual, 0) 63 | }) 64 | 65 | Convey("two methods", func() { 66 | err := slice.Add(3) 67 | So(err, ShouldEqual, nil) 68 | defer slice.Remove(3) 69 | patches := ApplyMethodFunc(s, "Add", func(_ int) error { 70 | return fake.ErrElemExsit 71 | }) 72 | defer patches.Reset() 73 | patches.ApplyMethodFunc(s, "Remove", func(_ int) error { 74 | return fake.ErrElemNotExsit 75 | }) 76 | err = slice.Add(2) 77 | So(err, ShouldEqual, fake.ErrElemExsit) 78 | err = slice.Remove(1) 79 | So(err, ShouldEqual, fake.ErrElemNotExsit) 80 | So(len(slice), ShouldEqual, 1) 81 | So(slice[0], ShouldEqual, 3) 82 | }) 83 | 84 | Convey("one func and one method", func() { 85 | err := slice.Add(4) 86 | So(err, ShouldEqual, nil) 87 | defer slice.Remove(4) 88 | patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { 89 | return outputExpect, nil 90 | }) 91 | defer patches.Reset() 92 | patches.ApplyMethodFunc(s, "Remove", func(_ int) error { 93 | return fake.ErrElemNotExsit 94 | }) 95 | output, err := fake.Exec("", "") 96 | So(err, ShouldEqual, nil) 97 | So(output, ShouldEqual, outputExpect) 98 | err = slice.Remove(1) 99 | So(err, ShouldEqual, fake.ErrElemNotExsit) 100 | So(len(slice), ShouldEqual, 1) 101 | So(slice[0], ShouldEqual, 4) 102 | }) 103 | 104 | Convey("for variadic method", func() { 105 | slice = fake.NewSlice() 106 | count := slice.Append(1, 2, 3) 107 | So(count, ShouldEqual, 3) 108 | patches := ApplyMethodFunc(s, "Append", func(_ ...int) int { 109 | return 0 110 | }) 111 | defer patches.Reset() 112 | count = slice.Append(4, 5, 6) 113 | So(count, ShouldEqual, 0) 114 | So(len(slice), ShouldEqual, 3) 115 | }) 116 | }) 117 | } 118 | -------------------------------------------------------------------------------- /test/apply_method_return_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | /* 12 | compare with apply_method_seq_test.go 13 | */ 14 | 15 | func TestApplyMethodReturn(t *testing.T) { 16 | e := &fake.Etcd{} 17 | Convey("TestApplyMethodReturn", t, func() { 18 | Convey("declares the values to be returned", func() { 19 | info1 := "hello cpp" 20 | patches := ApplyMethodReturn(e, "Retrieve", info1, nil) 21 | defer patches.Reset() 22 | for i := 0; i < 10; i++ { 23 | output1, err1 := e.Retrieve("") 24 | So(err1, ShouldEqual, nil) 25 | So(output1, ShouldEqual, info1) 26 | } 27 | 28 | patches.Reset() // if not reset will occur:patch has been existed 29 | info2 := "hello golang" 30 | patches.ApplyMethodReturn(e, "Retrieve", info2, nil) 31 | for i := 0; i < 10; i++ { 32 | output2, err2 := e.Retrieve("") 33 | So(err2, ShouldEqual, nil) 34 | So(output2, ShouldEqual, info2) 35 | } 36 | }) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /test/apply_method_seq_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestApplyMethodSeq(t *testing.T) { 12 | e := &fake.Etcd{} 13 | Convey("TestApplyMethodSeq", t, func() { 14 | 15 | Convey("default times is 1", func() { 16 | info1 := "hello cpp" 17 | info2 := "hello golang" 18 | info3 := "hello gomonkey" 19 | outputs := []OutputCell{ 20 | {Values: Params{info1, nil}}, 21 | {Values: Params{info2, nil}}, 22 | {Values: Params{info3, nil}}, 23 | } 24 | patches := ApplyMethodSeq(e, "Retrieve", outputs) 25 | defer patches.Reset() 26 | output, err := e.Retrieve("") 27 | So(err, ShouldEqual, nil) 28 | So(output, ShouldEqual, info1) 29 | output, err = e.Retrieve("") 30 | So(err, ShouldEqual, nil) 31 | So(output, ShouldEqual, info2) 32 | output, err = e.Retrieve("") 33 | So(err, ShouldEqual, nil) 34 | So(output, ShouldEqual, info3) 35 | }) 36 | 37 | Convey("retry succ util the third times", func() { 38 | info1 := "hello cpp" 39 | outputs := []OutputCell{ 40 | {Values: Params{"", fake.ErrActual}, Times: 2}, 41 | {Values: Params{info1, nil}}, 42 | } 43 | patches := ApplyMethodSeq(e, "Retrieve", outputs) 44 | defer patches.Reset() 45 | output, err := e.Retrieve("") 46 | So(err, ShouldEqual, fake.ErrActual) 47 | output, err = e.Retrieve("") 48 | So(err, ShouldEqual, fake.ErrActual) 49 | output, err = e.Retrieve("") 50 | So(err, ShouldEqual, nil) 51 | So(output, ShouldEqual, info1) 52 | }) 53 | 54 | Convey("batch operations failed on the third time", func() { 55 | info1 := "hello gomonkey" 56 | outputs := []OutputCell{ 57 | {Values: Params{info1, nil}, Times: 2}, 58 | {Values: Params{"", fake.ErrActual}}, 59 | } 60 | patches := ApplyMethodSeq(e, "Retrieve", outputs) 61 | defer patches.Reset() 62 | output, err := e.Retrieve("") 63 | So(err, ShouldEqual, nil) 64 | So(output, ShouldEqual, info1) 65 | output, err = e.Retrieve("") 66 | So(err, ShouldEqual, nil) 67 | So(output, ShouldEqual, info1) 68 | output, err = e.Retrieve("") 69 | So(err, ShouldEqual, fake.ErrActual) 70 | }) 71 | 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /test/apply_method_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2" 7 | "github.com/agiledragon/gomonkey/v2/test/fake" 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestApplyMethod(t *testing.T) { 12 | slice := fake.NewSlice() 13 | var s *fake.Slice 14 | Convey("TestApplyMethod", t, func() { 15 | 16 | Convey("for succ", func() { 17 | err := slice.Add(1) 18 | So(err, ShouldEqual, nil) 19 | patches := ApplyMethod(s, "Add", func(_ *fake.Slice, _ int) error { 20 | return nil 21 | }) 22 | defer patches.Reset() 23 | err = slice.Add(1) 24 | So(err, ShouldEqual, nil) 25 | err = slice.Remove(1) 26 | So(err, ShouldEqual, nil) 27 | So(len(slice), ShouldEqual, 0) 28 | }) 29 | 30 | Convey("for already exist", func() { 31 | err := slice.Add(2) 32 | So(err, ShouldEqual, nil) 33 | patches := ApplyMethod(s, "Add", func(_ *fake.Slice, _ int) error { 34 | return fake.ErrElemExsit 35 | }) 36 | defer patches.Reset() 37 | err = slice.Add(1) 38 | So(err, ShouldEqual, fake.ErrElemExsit) 39 | err = slice.Remove(2) 40 | So(err, ShouldEqual, nil) 41 | So(len(slice), ShouldEqual, 0) 42 | }) 43 | 44 | Convey("two methods", func() { 45 | err := slice.Add(3) 46 | So(err, ShouldEqual, nil) 47 | defer slice.Remove(3) 48 | patches := ApplyMethod(s, "Add", func(_ *fake.Slice, _ int) error { 49 | return fake.ErrElemExsit 50 | }) 51 | defer patches.Reset() 52 | patches.ApplyMethod(s, "Remove", func(_ *fake.Slice, _ int) error { 53 | return fake.ErrElemNotExsit 54 | }) 55 | err = slice.Add(2) 56 | So(err, ShouldEqual, fake.ErrElemExsit) 57 | err = slice.Remove(1) 58 | So(err, ShouldEqual, fake.ErrElemNotExsit) 59 | So(len(slice), ShouldEqual, 1) 60 | So(slice[0], ShouldEqual, 3) 61 | }) 62 | 63 | Convey("one func and one method", func() { 64 | err := slice.Add(4) 65 | So(err, ShouldEqual, nil) 66 | defer slice.Remove(4) 67 | patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) { 68 | return outputExpect, nil 69 | }) 70 | defer patches.Reset() 71 | patches.ApplyMethod(s, "Remove", func(_ *fake.Slice, _ int) error { 72 | return fake.ErrElemNotExsit 73 | }) 74 | output, err := fake.Exec("", "") 75 | So(err, ShouldEqual, nil) 76 | So(output, ShouldEqual, outputExpect) 77 | err = slice.Remove(1) 78 | So(err, ShouldEqual, fake.ErrElemNotExsit) 79 | So(len(slice), ShouldEqual, 1) 80 | So(slice[0], ShouldEqual, 4) 81 | }) 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /test/apply_private_method_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/agiledragon/gomonkey/v2/test/fake" 7 | 8 | . "github.com/smartystreets/goconvey/convey" 9 | 10 | . "github.com/agiledragon/gomonkey/v2" 11 | ) 12 | 13 | func TestApplyPrivateMethod(t *testing.T) { 14 | Convey("TestApplyPrivateMethod", t, func() { 15 | Convey("patch private pointer method in the different package", func() { 16 | f := new(fake.PrivateMethodStruct) 17 | var s *fake.PrivateMethodStruct 18 | patches := ApplyPrivateMethod(s, "ok", func(_ *fake.PrivateMethodStruct) bool { 19 | return false 20 | }) 21 | defer patches.Reset() 22 | result := f.Happy() 23 | So(result, ShouldEqual, "unhappy") 24 | }) 25 | 26 | Convey("patch private value method in the different package", func() { 27 | s := fake.PrivateMethodStruct{} 28 | patches := ApplyPrivateMethod(s, "haveEaten", func(_ fake.PrivateMethodStruct) bool { 29 | return true 30 | }) 31 | defer patches.Reset() 32 | result := s.AreYouHungry() 33 | So(result, ShouldEqual, "I am full") 34 | }) 35 | 36 | Convey("repeat patch same method", func() { 37 | var s *fake.PrivateMethodStruct 38 | patches := ApplyPrivateMethod(s, "ok", func(_ *fake.PrivateMethodStruct) bool { 39 | return false 40 | }) 41 | result := s.Happy() 42 | So(result, ShouldEqual, "unhappy") 43 | 44 | patches.ApplyPrivateMethod(s, "ok", func(_ *fake.PrivateMethodStruct) bool { 45 | return true 46 | }) 47 | result = s.Happy() 48 | So(result, ShouldEqual, "happy") 49 | 50 | patches.Reset() 51 | result = s.Happy() 52 | So(result, ShouldEqual, "unhappy") 53 | }) 54 | }) 55 | 56 | } 57 | -------------------------------------------------------------------------------- /test/dsl_test/func_dsl_test.go: -------------------------------------------------------------------------------- 1 | package dsltest 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/agiledragon/gomonkey/v2/test/fake" 7 | 8 | . "github.com/agiledragon/gomonkey/v2" 9 | . "github.com/agiledragon/gomonkey/v2/dsl" 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | func TestPbBuilderFunc(t *testing.T) { 14 | Convey("TestPbBuilderFunc", t, func() { 15 | 16 | Convey("first dsl", func() { 17 | patches := NewPatches() 18 | defer patches.Reset() 19 | patchBuilder := NewPatchBuilder(patches) 20 | 21 | patchBuilder. 22 | Func(Belong). 23 | Stubs(). 24 | With(Eq("zxl"), Any()). 25 | Will(Return(true)). 26 | Then(Repeat(Return(false), 2)). 27 | End() 28 | 29 | flag := Belong("zxl", []string{}) 30 | So(flag, ShouldBeTrue) 31 | 32 | defer func() { 33 | if p := recover(); p != nil { 34 | str, ok := p.(string) 35 | So(ok, ShouldBeTrue) 36 | So(str, ShouldEqual, "input paras ddd is not matched") 37 | } 38 | }() 39 | Belong("ddd", []string{"abc"}) 40 | }) 41 | 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /test/fake/fake.go: -------------------------------------------------------------------------------- 1 | package fake 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os/exec" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | ErrActual = errors.New("actual") 12 | ErrElemExsit = errors.New("elem already exist") 13 | ErrElemNotExsit = errors.New("elem not exist") 14 | ) 15 | 16 | func Exec(cmd string, args ...string) (string, error) { 17 | cmdPath, err := exec.LookPath(cmd) 18 | if err != nil { 19 | fmt.Errorf("exec.LookPath err: %v, cmd: %s", err, cmd) 20 | return "", errors.New("any") 21 | } 22 | 23 | var output []byte 24 | output, err = exec.Command(cmdPath, args...).CombinedOutput() 25 | if err != nil { 26 | fmt.Errorf("exec.Command.CombinedOutput err: %v, cmd: %s", err, cmd) 27 | return "", errors.New("any") 28 | } 29 | fmt.Println("CMD[", cmdPath, "]ARGS[", args, "]OUT[", string(output), "]") 30 | return string(output), nil 31 | } 32 | 33 | func Belong(points string, lines []string) bool { 34 | flag := false 35 | for _, line := range lines { 36 | flag = true 37 | for _, r := range points { 38 | if !strings.ContainsRune(line, r) { 39 | flag = false 40 | break 41 | } 42 | } 43 | if flag { 44 | return true 45 | } 46 | } 47 | return false 48 | } 49 | 50 | 51 | type Slice []int 52 | 53 | func NewSlice() Slice { 54 | return make(Slice, 0) 55 | } 56 | 57 | func (this* Slice) Add(elem int) error { 58 | for _, v := range *this { 59 | if v == elem { 60 | fmt.Printf("Slice: Add elem: %v already exist\n", elem) 61 | return ErrElemExsit 62 | } 63 | } 64 | *this = append(*this, elem) 65 | fmt.Printf("Slice: Add elem: %v succ\n", elem) 66 | return nil 67 | } 68 | 69 | func (this* Slice) Remove(elem int) error { 70 | found := false 71 | for i, v := range *this { 72 | if v == elem { 73 | if i == len(*this) - 1 { 74 | *this = (*this)[:i] 75 | 76 | } else { 77 | *this = append((*this)[:i], (*this)[i+1:]...) 78 | } 79 | found = true 80 | break 81 | } 82 | } 83 | if !found { 84 | fmt.Printf("Slice: Remove elem: %v not exist\n", elem) 85 | return ErrElemNotExsit 86 | } 87 | fmt.Printf("Slice: Remove elem: %v succ\n", elem) 88 | return nil 89 | } 90 | 91 | func (this *Slice) Append(elems ...int) int { 92 | *this = append(*this, elems...) 93 | fmt.Printf("Slice: Append elem: %v succ\n", elems) 94 | return len(elems) 95 | } 96 | 97 | func ReadLeaf(url string) (string, error) { 98 | output := fmt.Sprintf("%s, %s!", "Hello", "World") 99 | return output, nil 100 | } 101 | 102 | type Etcd struct { 103 | 104 | } 105 | 106 | func (this *Etcd) Retrieve(url string) (string, error) { 107 | output := fmt.Sprintf("%s, %s!", "Hello", "Etcd") 108 | return output, nil 109 | } 110 | 111 | var Marshal = func(v interface{}) ([]byte, error) { 112 | return nil, nil 113 | } 114 | 115 | type Db interface { 116 | Retrieve(url string) (string, error) 117 | } 118 | 119 | type Mysql struct { 120 | 121 | } 122 | 123 | func (this *Mysql) Retrieve(url string) (string, error) { 124 | output := fmt.Sprintf("%s, %s!", "Hello", "Mysql") 125 | return output, nil 126 | } 127 | 128 | func NewDb(style string) Db { 129 | if style == "etcd" { 130 | return new(Etcd) 131 | } else { 132 | return new(Mysql) 133 | } 134 | } 135 | 136 | type PrivateMethodStruct struct { 137 | 138 | } 139 | 140 | func (this *PrivateMethodStruct) ok() bool { 141 | return this != nil 142 | } 143 | 144 | func (this *PrivateMethodStruct) Happy() string { 145 | if this.ok() { 146 | return "happy" 147 | } 148 | return "unhappy" 149 | } 150 | 151 | func (this PrivateMethodStruct) haveEaten() bool { 152 | return this != PrivateMethodStruct{} 153 | } 154 | 155 | func (this PrivateMethodStruct) AreYouHungry() string { 156 | if this.haveEaten() { 157 | return "I am full" 158 | } 159 | 160 | return "I am hungry" 161 | } -------------------------------------------------------------------------------- /test/patch_pair_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | . "github.com/agiledragon/gomonkey/v2" 8 | "github.com/agiledragon/gomonkey/v2/test/fake" 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestPatchPair(t *testing.T) { 13 | 14 | Convey("TestPatchPair", t, func() { 15 | 16 | Convey("TestPatchPair", func() { 17 | patchPairs := [][2]interface{}{ 18 | { 19 | fake.Exec, 20 | func(_ string, _ ...string) (string, error) { 21 | return outputExpect, nil 22 | }, 23 | }, 24 | { 25 | json.Unmarshal, 26 | func(_ []byte, v interface{}) error { 27 | p := v.(*map[int]int) 28 | *p = make(map[int]int) 29 | (*p)[1] = 2 30 | (*p)[2] = 4 31 | return nil 32 | }, 33 | }, 34 | } 35 | patches := NewPatches() 36 | defer patches.Reset() 37 | for _, pair := range patchPairs { 38 | patches.ApplyFunc(pair[0], pair[1]) 39 | } 40 | 41 | output, err := fake.Exec("", "") 42 | So(err, ShouldEqual, nil) 43 | So(output, ShouldEqual, outputExpect) 44 | 45 | var m map[int]int 46 | err = json.Unmarshal(nil, &m) 47 | So(err, ShouldEqual, nil) 48 | So(m[1], ShouldEqual, 2) 49 | So(m[2], ShouldEqual, 4) 50 | }) 51 | 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /ut.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(go list ./test/... | grep -v test/fake); do 7 | echo "--------Run test package: $d" 8 | GO111MODULE=on go test -gcflags="all=-N -l" -v -coverprofile=profile.out -coverpkg=./... -covermode=atomic $d 9 | echo "--------Finish test package: $d" 10 | if [ -f profile.out ]; then 11 | cat profile.out >> coverage.txt 12 | rm profile.out 13 | fi 14 | done 15 | -------------------------------------------------------------------------------- /write_darwin_amd64.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 ByteDance Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "textflag.h" 18 | 19 | #define NOP8 BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90; 20 | #define NOP64 NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; NOP8; 21 | #define NOP512 NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; 22 | #define NOP4096 NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; 23 | 24 | #define protRW $(0x1|0x2|0x10) 25 | #define mProtect $(0x2000000+74) 26 | 27 | TEXT ·write(SB),NOSPLIT,$24 28 | JMP START 29 | NOP4096 30 | START: 31 | MOVQ mProtect, AX 32 | MOVQ page+24(FP), DI 33 | MOVQ pageSize+32(FP), SI 34 | MOVQ protRW, DX 35 | SYSCALL 36 | CMPQ AX, $0 37 | JZ PROTECT_OK 38 | CALL mach_task_self(SB) 39 | MOVQ AX, DI 40 | MOVQ target+0(FP), SI 41 | MOVQ len+16(FP), DX 42 | MOVQ $0, CX 43 | MOVQ protRW, R8 44 | CALL mach_vm_protect(SB) 45 | CMPQ AX, $0 46 | JNZ RETURN 47 | PROTECT_OK: 48 | MOVQ target+0(FP), DI 49 | MOVQ data+8(FP), SI 50 | MOVQ len+16(FP), CX 51 | MOVQ DI, to-24(SP) 52 | MOVQ SI, from-16(SP) 53 | MOVQ CX, n-8(SP) 54 | CALL runtime·memmove(SB) 55 | MOVQ mProtect, AX 56 | MOVQ page+24(FP), DI 57 | MOVQ pageSize+32(FP), SI 58 | MOVQ oriProt+40(FP), DX 59 | SYSCALL 60 | JMP RETURN 61 | NOP4096 62 | RETURN: 63 | MOVQ AX, ret+48(FP) 64 | RET 65 | -------------------------------------------------------------------------------- /write_darwin_arm64.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 ByteDance Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "textflag.h" 18 | 19 | #define NOP64 WORD $0x1f2003d5; WORD $0x1f2003d5; 20 | #define NOP512 NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; NOP64; 21 | #define NOP4096 NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; NOP512; 22 | #define NOP16384 NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; NOP4096; 23 | 24 | #define protRW $(0x1|0x2|0x10) 25 | #define mProtect $(0x2000000+74) 26 | 27 | TEXT ·write(SB),NOSPLIT,$24 28 | B START 29 | NOP16384 30 | START: 31 | MOVD mProtect, R16 32 | MOVD page+24(FP), R0 33 | MOVD pageSize+32(FP), R1 34 | MOVD protRW, R2 35 | SVC $0x80 36 | CMP $0, R0 37 | BEQ PROTECT_OK 38 | CALL mach_task_self(SB) 39 | MOVD target+0(FP), R1 40 | MOVD len+16(FP), R2 41 | MOVD $0, R3 42 | MOVD protRW, R4 43 | CALL mach_vm_protect(SB) 44 | CMP $0, R0 45 | BNE RETURN 46 | PROTECT_OK: 47 | MOVD target+0(FP), R0 48 | MOVD data+8(FP), R1 49 | MOVD len+16(FP), R2 50 | MOVD R0, to-24(SP) 51 | MOVD R1, from-16(SP) 52 | MOVD R2, n-8(SP) 53 | CALL runtime·memmove(SB) 54 | MOVD mProtect, R16 55 | MOVD page+24(FP), R0 56 | MOVD pageSize+32(FP), R1 57 | MOVD oriProt+40(FP), R2 58 | SVC $0x80 59 | B RETURN 60 | NOP16384 61 | RETURN: 62 | MOVD R0, ret+48(FP) 63 | RET 64 | --------------------------------------------------------------------------------