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 }