github.com/notti/go-dynamic@v0.0.0-20190619201224-fc443047424c/steps/3_goffi/ffi/ffi.go (about) 1 package ffi 2 3 import ( 4 "reflect" 5 "strings" 6 "unsafe" 7 ) 8 9 //go:generate go tool compile -asmhdr ffi.h ffi.go 10 11 type argtype uint16 12 13 const ( 14 type64 argtype = 0 // movq 64 bit 15 typeS32 argtype = 1 // movlqsx signed 32 bit 16 typeU32 argtype = 2 // movlqzx unsigned 32 bit 17 typeS16 argtype = 3 // movwqsx signed 16 bit 18 typeU16 argtype = 4 // movwqzx unsigned 16 bit 19 typeS8 argtype = 5 // movbqsx signed 8 bit 20 typeU8 argtype = 6 // movbqzx unsigned 8 bit 21 typeDouble argtype = 7 // movsd 64 bit 22 typeFloat argtype = 8 // movss 32 bit 23 typeUnused argtype = 0xFFFF 24 ) 25 26 type argument struct { 27 offset uint16 28 t argtype 29 } 30 31 // Spec is the callspec needed to do the actuall call 32 type Spec struct { 33 fn uintptr 34 base uintptr 35 stack []argument 36 intargs [6]argument 37 xmmargs [8]argument 38 ret0 argument 39 ret1 argument 40 xmmret0 argument 41 xmmret1 argument 42 rax uint8 43 } 44 45 var sliceOffset = reflect.TypeOf(reflect.SliceHeader{}).Field(0).Offset 46 47 func fieldToOffset(k reflect.StructField, t string) (argument, bool) { 48 switch k.Type.Kind() { 49 case reflect.Slice: 50 return argument{uint16(k.Offset + sliceOffset), type64}, false 51 case reflect.Int, reflect.Uint, reflect.Uint64, reflect.Int64, reflect.Ptr: 52 return argument{uint16(k.Offset), type64}, false 53 case reflect.Int32: 54 return argument{uint16(k.Offset), typeS32}, false 55 case reflect.Uint32: 56 return argument{uint16(k.Offset), typeU32}, false 57 case reflect.Int16: 58 return argument{uint16(k.Offset), typeS16}, false 59 case reflect.Uint16: 60 return argument{uint16(k.Offset), typeU16}, false 61 case reflect.Int8: 62 return argument{uint16(k.Offset), typeS8}, false 63 case reflect.Uint8, reflect.Bool: 64 return argument{uint16(k.Offset), typeU8}, false 65 case reflect.Float32: 66 return argument{uint16(k.Offset), typeFloat}, true 67 case reflect.Float64: 68 return argument{uint16(k.Offset), typeDouble}, true 69 } 70 panic("Unknown Type") 71 } 72 73 // FIXME: we don't support stuff > 64 bit 74 75 // MakeSpec builds a call specification for the given arguments 76 func MakeSpec(fn uintptr, args interface{}) Spec { 77 v := reflect.ValueOf(args) 78 for v.Kind() == reflect.Ptr { 79 v = v.Elem() 80 } 81 t := v.Type() 82 83 var spec Spec 84 85 spec.fn = fn 86 87 spec.ret0.t = typeUnused 88 spec.ret1.t = typeUnused 89 spec.xmmret0.t = typeUnused 90 spec.xmmret1.t = typeUnused 91 92 haveRet := false 93 94 intreg := 0 95 xmmreg := 0 96 97 ARGS: 98 for i := 0; i < t.NumField(); i++ { 99 f := t.Field(i) 100 tags := strings.Split(f.Tag.Get("ffi"), ",") 101 ret := false 102 st := "" 103 for _, tag := range tags { 104 if tag == "ignore" { 105 continue ARGS 106 } 107 if tag == "ret" { 108 if haveRet == true { 109 panic("Only one return argument allowed") 110 } 111 ret = true 112 haveRet = true 113 continue 114 } 115 if strings.HasPrefix(tag, "type=") { 116 st = tag[5:] 117 } 118 } 119 if ret { 120 off, xmm := fieldToOffset(f, st) 121 if xmm { 122 spec.xmmret0 = off 123 } else { 124 spec.ret0 = off 125 } 126 // FIXME ret1/xmmret1! - only needed for types > 64 bit 127 continue 128 } 129 off, xmm := fieldToOffset(f, st) 130 if xmm { 131 if xmmreg < 8 { 132 spec.xmmargs[xmmreg] = off 133 xmmreg++ 134 } else { 135 spec.stack = append(spec.stack, off) 136 } 137 } else { 138 if intreg < 6 { 139 spec.intargs[intreg] = off 140 intreg++ 141 } else { 142 spec.stack = append(spec.stack, off) 143 } 144 } 145 } 146 for i := intreg; i < 6; i++ { 147 spec.intargs[i].t = typeUnused 148 } 149 for i := xmmreg; i < 8; i++ { 150 spec.xmmargs[i].t = typeUnused 151 } 152 spec.rax = uint8(xmmreg) 153 return spec 154 } 155 156 // Call calls the given spec with the given arguments 157 func (spec Spec) Call(args unsafe.Pointer) { 158 spec.base = uintptr(args) 159 160 entersyscall() 161 asmcgocall(unsafe.Pointer(asmcallptr), uintptr(unsafe.Pointer(&spec))) 162 exitsyscall() 163 164 if _Cgo_always_false { 165 _Cgo_use(args) 166 _Cgo_use(spec) 167 } 168 } 169 170 //go:linkname _Cgo_always_false runtime.cgoAlwaysFalse 171 var _Cgo_always_false bool 172 173 //go:linkname _Cgo_use runtime.cgoUse 174 func _Cgo_use(interface{}) 175 176 //go:linkname asmcgocall runtime.asmcgocall 177 func asmcgocall(unsafe.Pointer, uintptr) int32 178 179 //go:linkname entersyscall runtime.entersyscall 180 func entersyscall() 181 182 //go:linkname exitsyscall runtime.exitsyscall 183 func exitsyscall() 184 185 func asmcall() 186 187 //go:linkname x_cgo_init x_cgo_init 188 func x_cgo_init() 189 190 // force _cgo_init into the .data segment (instead of .bss), so our "linker" can overwrite its contents 191 //go:linkname _cgo_init _cgo_init 192 var _cgo_init = uintptr(10) 193 194 type emptyComplex64 struct { 195 a complex64 196 } 197 type emptyComplex128 complex128 198 199 func init() { 200 if _Cgo_always_false { 201 x_cgo_init() // prevent x_cgo_init from being optimized out 202 } 203 } 204 205 //go:linkname funcPC runtime.funcPC 206 func funcPC(f interface{}) uintptr 207 208 var asmcallptr = funcPC(asmcall)