github.com/notti/go-dynamic@v0.0.0-20190619201224-fc443047424c/internal/ffi/call_amd64.go (about)

     1  package ffi
     2  
     3  import (
     4  	"unsafe"
     5  )
     6  
     7  // amd64 cdecl calling conventions: https://www.uclibc.org/docs/psABI-x86_64.pdf
     8  //   - Align the stack (should be 32byte aligned before the function is called - 16 byte is enough if we don't pass 256bit integers)
     9  //   - Pass first integer arguments in DI, SI, DX, CX, R8, R9
    10  //   - Pass first float arguments in X0-X7
    11  //   - Pass rest on the stack
    12  //   - Pass number of used float registers in AX
    13  // Return is in AX or X0 for floats
    14  // according to libffi clang might require the caller to properly (sign)extend stuff in registers - so we do that
    15  // structs are not supported for now (neither as argument nor as return value) - but this is not hard to do
    16  
    17  type argtype uint16
    18  
    19  const (
    20  	type64     argtype = 0 // movq              64 bit
    21  	typeS32    argtype = 1 // movlqsx    signed 32 bit
    22  	typeU32    argtype = 2 // movlqzx  unsigned 32 bit
    23  	typeS16    argtype = 3 // movwqsx    signed 16 bit
    24  	typeU16    argtype = 4 // movwqzx  unsigned 16 bit
    25  	typeS8     argtype = 5 // movbqsx    signed 8  bit
    26  	typeU8     argtype = 6 // movbqzx  unsigned 8  bit
    27  	typeDouble argtype = 7 // movsd             64 bit
    28  	typeFloat  argtype = 8 // movss             32 bit
    29  	typeUnused argtype = 0xFFFF
    30  )
    31  
    32  type argument struct {
    33  	offset uint16
    34  	t      argtype
    35  }
    36  
    37  // spec a wrapper specifcation with instructions on how to place arguments into registers/stack
    38  type spec struct {
    39  	wrapper uintptr // pointer to callWrapper()
    40  	fn      uintptr // pointer to the C-function
    41  	stack   []argument
    42  	intargs [6]argument
    43  	xmmargs [8]argument
    44  	ret     argument
    45  	rax     uint8
    46  }
    47  
    48  // FIXME: we don't support stuff > 64 bit
    49  
    50  func callWrapper()
    51  
    52  // MakeSpec builds a call specification for the given arguments
    53  func MakeSpec(fn uintptr, fun interface{}) error {
    54  	fptr, arguments, ret, err := stackFields(fun)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	spec := new(spec)
    60  	spec.wrapper = funcPC(callWrapper)
    61  	spec.fn = fn
    62  	spec.ret.t = typeUnused
    63  
    64  	intreg := 0
    65  	xmmreg := 0
    66  
    67  	for _, arg := range arguments {
    68  		var t argtype
    69  		switch arg.c {
    70  		case classInt, classUint:
    71  			switch {
    72  			case arg.size == 8:
    73  				t = type64
    74  			case arg.size == 4:
    75  				if arg.c == classInt {
    76  					t = typeS32
    77  				} else {
    78  					t = typeU32
    79  				}
    80  			case arg.size == 2:
    81  				if arg.c == classInt {
    82  					t = typeS16
    83  				} else {
    84  					t = typeU16
    85  				}
    86  			case arg.size == 1:
    87  				if arg.c == classInt {
    88  					t = typeS8
    89  				} else {
    90  					t = typeU8
    91  				}
    92  			}
    93  			if intreg < 6 {
    94  				spec.intargs[intreg] = argument{uint16(arg.offset), t}
    95  				intreg++
    96  			} else {
    97  				switch t {
    98  				case typeS32:
    99  					t = typeU32
   100  				case typeS16:
   101  					t = typeU16
   102  				case typeS8:
   103  					t = typeU8
   104  				}
   105  				spec.stack = append(spec.stack, argument{uint16(arg.offset), t})
   106  			}
   107  		case classFloat:
   108  			switch {
   109  			case arg.size == 8:
   110  				t = typeDouble
   111  			case arg.size == 4:
   112  				t = typeFloat
   113  			}
   114  			if xmmreg < 8 {
   115  				spec.xmmargs[xmmreg] = argument{uint16(arg.offset), t}
   116  				xmmreg++
   117  			} else {
   118  				switch t {
   119  				case typeDouble:
   120  					t = type64
   121  				case typeFloat:
   122  					t = typeU32
   123  				}
   124  				spec.stack = append(spec.stack, argument{uint16(arg.offset), t})
   125  			}
   126  		}
   127  	}
   128  
   129  	spec.rax = uint8(xmmreg)
   130  	for i := intreg; i < 6; i++ {
   131  		spec.intargs[i].t = typeUnused
   132  	}
   133  	for i := xmmreg; i < 8; i++ {
   134  		spec.xmmargs[i].t = typeUnused
   135  	}
   136  
   137  	if ret.c != classVoid {
   138  		var t argtype
   139  		switch ret.c {
   140  		case classInt, classUint:
   141  			switch ret.size {
   142  			case 8:
   143  				t = type64
   144  			case 4:
   145  				t = typeU32
   146  			case 2:
   147  				t = typeU16
   148  			case 1:
   149  				t = typeU8
   150  			}
   151  		case classFloat:
   152  			switch ret.size {
   153  			case 8:
   154  				t = typeDouble
   155  			case 4:
   156  				t = typeFloat
   157  			}
   158  		}
   159  		spec.ret.t = t
   160  		spec.ret.offset = uint16(ret.offset)
   161  	}
   162  
   163  	*(*unsafe.Pointer)(fptr) = unsafe.Pointer(spec)
   164  
   165  	return nil
   166  }