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)