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  }