github.com/laevusdexter/asmcgocall@v0.0.0-20200220061330-f484a47e9b97/asmcgocall.go (about) 1 package asmcgocall 2 3 import ( 4 "reflect" 5 "unsafe" 6 ) 7 8 //go:linkname asmcgocall runtime.asmcgocall 9 //go:noescape 10 func asmcgocall(fn unsafe.Pointer, arg unsafe.Pointer) int32 11 12 type parameter struct { 13 packedOffset, offset, size uintptr 14 } 15 16 func Register(cfn unsafe.Pointer, caller interface{}) { 17 fn := reflect.TypeOf(caller) 18 19 if fn.Kind() != reflect.Ptr { 20 panic("caller must be pointer to variable with function type") 21 } 22 23 fn = fn.Elem() 24 25 if fn.Kind() != reflect.Func { 26 panic("caller must be pointer to variable with function type") 27 } 28 29 parameters, returns := calcPadding(fn) 30 31 var proxy func(args [0]byte) (ret [0]byte) 32 33 if returns { 34 proxy = createProxyRet(cfn, parameters) 35 } else { 36 proxy = createProxy(cfn, parameters) 37 } 38 39 /* 40 let proxy(function above) be callable with passed function signature of call 41 */ 42 eface := (*[2]unsafe.Pointer)(unsafe.Pointer(&caller))[1] 43 *(*unsafe.Pointer)(eface) = *(*unsafe.Pointer)(unsafe.Pointer(&proxy)) 44 } 45 46 func calcPadding(fn reflect.Type) (parameters []parameter, returns bool) { 47 if Debug { 48 logf("struct argument_frame {\n") 49 } 50 51 var padoffset uintptr 52 parameters = make([]parameter, 0, 1) 53 54 off := int(0) 55 for i, n := 0, fn.NumIn(); i < n; i++ { 56 param := fn.In(i) 57 psize := param.Size() 58 59 palign := param.Align() 60 if off%palign != 0 { 61 pad := palign - off%palign 62 63 if Debug { 64 logf("\tchar __pad%d[%d]; // %d %s\n", off, pad, pad, bytes(pad)) 65 } 66 67 off += pad 68 padoffset += uintptr(pad) 69 } 70 71 if Debug { 72 logf("\t%s p%d; // %d %s\n", param.Name(), i, psize, bytes(psize)) 73 } 74 75 parameters = append(parameters, parameter{uintptr(off) - padoffset, uintptr(off), psize}) 76 77 off += int(psize) 78 } 79 80 const ptrsize = int(unsafe.Sizeof(uintptr(0))) 81 82 if off%ptrsize != 0 { 83 pad := ptrsize - off%ptrsize 84 85 if Debug { 86 logf("\tchar __pad%d[%d]; // %d %s\n", off, pad, pad, bytes(pad)) 87 } 88 89 off += pad 90 padoffset += uintptr(pad) 91 } 92 93 returns = false 94 if fn.NumOut() > 0 { 95 t := fn.Out(0) 96 retalign := t.Align() 97 retsize := t.Size() 98 99 if off%retalign != 0 { 100 pad := retalign - off%retalign 101 102 if Debug { 103 logf("\tchar __pad%d[%d]; // %d %s\n", off, pad, pad, bytes(pad)) 104 } 105 106 off += pad 107 padoffset += uintptr(pad) 108 } 109 110 if Debug { 111 logf("\t%s ret; // %d %s\n", t.Name(), retsize, bytes(retsize)) 112 } 113 114 parameters = append(parameters, parameter{uintptr(off) - padoffset, uintptr(off), retsize}) 115 116 off += int(retsize) 117 118 returns = true 119 } 120 121 if Debug && off%ptrsize != 0 { 122 pad := ptrsize - off%ptrsize 123 logf("\tchar __pad%d[%d]; // %d %s\n", off, pad, pad, bytes(pad)) 124 off += pad 125 } 126 127 if Debug && off == 0 { 128 logf("\tchar unused;\n") // avoid empty struct 129 } 130 131 if Debug { 132 logf("}; // %d bytes\n", off) 133 } 134 135 return 136 } 137 138 func createProxy(cfn unsafe.Pointer, parameters []parameter) func(args [0]byte) (ret [0]byte) { 139 if len(parameters) == 0 { 140 return func(args [0]byte) (ret [0]byte) { 141 asmcgocall(cfn, nil) 142 143 return 144 } 145 } 146 147 return func(args [0]byte) (ret [0]byte) { 148 params := parameters 149 150 pargs := unsafe.Pointer(&args) 151 152 var ( 153 p parameter 154 i uintptr 155 ) 156 157 /* 158 Pack parameters 159 */ 160 for c := 1; c < len(params); c++ { 161 p = params[c] 162 163 if p.offset == p.packedOffset { 164 continue 165 } 166 167 for i = 0; i < p.size; i++ { 168 *(*byte)(unsafe.Pointer(uintptr(pargs) + p.packedOffset + i)) = *(*byte)(unsafe.Pointer(uintptr(pargs) + p.offset + i)) 169 } 170 } 171 172 asmcgocall(cfn, pargs) 173 174 return 175 } 176 } 177 178 func createProxyRet(cfn unsafe.Pointer, parameters []parameter) func(args [0]byte) (ret [0]byte) { 179 if len(parameters) == 1 { 180 return func(args [0]byte) (ret [0]byte) { 181 asmcgocall(cfn, unsafe.Pointer(&args)) 182 183 return 184 } 185 } 186 187 return func(args [0]byte) (ret [0]byte) { 188 params := parameters 189 190 pargs := unsafe.Pointer(&args) 191 192 var ( 193 p parameter 194 i uintptr 195 ) 196 197 /* 198 Pack parameters 199 */ 200 n := len(params) - 1 201 for c := 1; c < n; c++ { 202 p = params[c] 203 204 if p.offset == p.packedOffset { 205 continue 206 } 207 208 for i = 0; i < p.size; i++ { 209 *(*byte)(unsafe.Pointer(uintptr(pargs) + p.packedOffset + i)) = *(*byte)(unsafe.Pointer(uintptr(pargs) + p.offset + i)) 210 } 211 } 212 213 p = params[n] 214 if p.offset == p.packedOffset { 215 asmcgocall(cfn, pargs) 216 217 return 218 } 219 220 /* 221 Fill return value with nulls (taking pad's spot) 222 */ 223 for i = 0; i < p.size; i++ { 224 *(*byte)(unsafe.Pointer(uintptr(pargs) + p.packedOffset + i)) = 0 225 } 226 227 asmcgocall(cfn, pargs) 228 229 /* 230 Reposition(unpack) return value back before exit 231 */ 232 for i = 0; i < p.size; i++ { 233 *(*byte)(unsafe.Pointer(uintptr(pargs) + p.offset + i)) = *(*byte)(unsafe.Pointer(uintptr(pargs) + p.packedOffset + i)) 234 } 235 236 return 237 } 238 }