| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Only build this file if libffi is supported. | |
| //go:build libffi | |
| // +build libffi | |
| package runtime | |
| import "unsafe" | |
| // This file contains the code that converts a Go type to an FFI type. | |
| // This has to be written in Go because it allocates memory in the Go heap. | |
| // C functions to return pointers to libffi variables. | |
| func ffi_type_pointer() *__ffi_type | |
| func ffi_type_sint8() *__ffi_type | |
| func ffi_type_sint16() *__ffi_type | |
| func ffi_type_sint32() *__ffi_type | |
| func ffi_type_sint64() *__ffi_type | |
| func ffi_type_uint8() *__ffi_type | |
| func ffi_type_uint16() *__ffi_type | |
| func ffi_type_uint32() *__ffi_type | |
| func ffi_type_uint64() *__ffi_type | |
| func ffi_type_float() *__ffi_type | |
| func ffi_type_double() *__ffi_type | |
| func ffi_supports_complex() bool | |
| func ffi_type_complex_float() *__ffi_type | |
| func ffi_type_complex_double() *__ffi_type | |
| func ffi_type_void() *__ffi_type | |
| // C functions defined in libffi. | |
| //extern ffi_prep_cif | |
| func ffi_prep_cif(*_ffi_cif, _ffi_abi, uint32, *__ffi_type, **__ffi_type) _ffi_status | |
| // ffiFuncToCIF is called from C code. | |
| //go:linkname ffiFuncToCIF | |
| // ffiFuncToCIF builds an _ffi_cif struct for function described by ft. | |
| func ffiFuncToCIF(ft *functype, isInterface bool, isMethod bool, cif *_ffi_cif) { | |
| nparams := len(ft.in) | |
| nargs := nparams | |
| if isInterface { | |
| nargs++ | |
| } | |
| args := make([]*__ffi_type, nargs) | |
| i := 0 | |
| off := 0 | |
| if isInterface { | |
| args[0] = ffi_type_pointer() | |
| off = 1 | |
| } else if isMethod { | |
| args[0] = ffi_type_pointer() | |
| i = 1 | |
| } | |
| for ; i < nparams; i++ { | |
| args[i+off] = typeToFFI(ft.in[i]) | |
| } | |
| rettype := funcReturnFFI(ft) | |
| var pargs **__ffi_type | |
| if len(args) > 0 { | |
| pargs = &args[0] | |
| } | |
| status := ffi_prep_cif(cif, _FFI_DEFAULT_ABI, uint32(nargs), rettype, pargs) | |
| if status != _FFI_OK { | |
| throw("ffi_prep_cif failed") | |
| } | |
| } | |
| // funcReturnFFI returns the FFI definition of the return type of ft. | |
| func funcReturnFFI(ft *functype) *__ffi_type { | |
| c := len(ft.out) | |
| if c == 0 { | |
| return ffi_type_void() | |
| } | |
| // Compile a function that returns a zero-sized value as | |
| // though it returns void. This works around a problem in | |
| // libffi: it can't represent a zero-sized value. | |
| var size uintptr | |
| for _, v := range ft.out { | |
| size += v.size | |
| } | |
| if size == 0 { | |
| return ffi_type_void() | |
| } | |
| if c == 1 { | |
| return typeToFFI(ft.out[0]) | |
| } | |
| elements := make([]*__ffi_type, c+1) | |
| for i, v := range ft.out { | |
| elements[i] = typeToFFI(v) | |
| } | |
| elements[c] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| // typeToFFI returns the __ffi_type for a Go type. | |
| func typeToFFI(typ *_type) *__ffi_type { | |
| switch typ.kind & kindMask { | |
| case kindBool: | |
| switch unsafe.Sizeof(false) { | |
| case 1: | |
| return ffi_type_uint8() | |
| case 4: | |
| return ffi_type_uint32() | |
| default: | |
| throw("bad bool size") | |
| return nil | |
| } | |
| case kindInt: | |
| return intToFFI() | |
| case kindInt8: | |
| return ffi_type_sint8() | |
| case kindInt16: | |
| return ffi_type_sint16() | |
| case kindInt32: | |
| return ffi_type_sint32() | |
| case kindInt64: | |
| return ffi_type_sint64() | |
| case kindUint: | |
| switch unsafe.Sizeof(uint(0)) { | |
| case 4: | |
| return ffi_type_uint32() | |
| case 8: | |
| return ffi_type_uint64() | |
| default: | |
| throw("bad uint size") | |
| return nil | |
| } | |
| case kindUint8: | |
| return ffi_type_uint8() | |
| case kindUint16: | |
| return ffi_type_uint16() | |
| case kindUint32: | |
| return ffi_type_uint32() | |
| case kindUint64: | |
| return ffi_type_uint64() | |
| case kindUintptr: | |
| switch unsafe.Sizeof(uintptr(0)) { | |
| case 4: | |
| return ffi_type_uint32() | |
| case 8: | |
| return ffi_type_uint64() | |
| default: | |
| throw("bad uinptr size") | |
| return nil | |
| } | |
| case kindFloat32: | |
| return ffi_type_float() | |
| case kindFloat64: | |
| return ffi_type_double() | |
| case kindComplex64: | |
| if ffi_supports_complex() { | |
| return ffi_type_complex_float() | |
| } else { | |
| return complexToFFI(ffi_type_float()) | |
| } | |
| case kindComplex128: | |
| if ffi_supports_complex() { | |
| return ffi_type_complex_double() | |
| } else { | |
| return complexToFFI(ffi_type_double()) | |
| } | |
| case kindArray: | |
| return arrayToFFI((*arraytype)(unsafe.Pointer(typ))) | |
| case kindChan, kindFunc, kindMap, kindPtr, kindUnsafePointer: | |
| // These types are always simple pointers, and for FFI | |
| // purposes nothing else matters. | |
| return ffi_type_pointer() | |
| case kindInterface: | |
| return interfaceToFFI() | |
| case kindSlice: | |
| return sliceToFFI((*slicetype)(unsafe.Pointer(typ))) | |
| case kindString: | |
| return stringToFFI() | |
| case kindStruct: | |
| return structToFFI((*structtype)(unsafe.Pointer(typ))) | |
| default: | |
| throw("unknown type kind") | |
| return nil | |
| } | |
| } | |
| // interfaceToFFI returns an ffi_type for a Go interface type. | |
| // This is used for both empty and non-empty interface types. | |
| func interfaceToFFI() *__ffi_type { | |
| elements := make([]*__ffi_type, 3) | |
| elements[0] = ffi_type_pointer() | |
| elements[1] = elements[0] | |
| elements[2] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| // stringToFFI returns an ffi_type for a Go string type. | |
| func stringToFFI() *__ffi_type { | |
| elements := make([]*__ffi_type, 3) | |
| elements[0] = ffi_type_pointer() | |
| elements[1] = intToFFI() | |
| elements[2] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| // structToFFI returns an ffi_type for a Go struct type. | |
| func structToFFI(typ *structtype) *__ffi_type { | |
| c := len(typ.fields) | |
| if typ.typ.kind&kindDirectIface != 0 { | |
| return ffi_type_pointer() | |
| } | |
| fields := make([]*__ffi_type, 0, c+1) | |
| checkPad := false | |
| lastzero := false | |
| sawnonzero := false | |
| for i, v := range typ.fields { | |
| // Skip zero-sized fields; they confuse libffi, | |
| // and there is no value to pass in any case. | |
| // We do have to check whether the alignment of the | |
| // zero-sized field introduces any padding for the | |
| // next field. | |
| if v.typ.size == 0 { | |
| checkPad = true | |
| if v.name == nil || *v.name != "_" { | |
| lastzero = true | |
| } | |
| continue | |
| } | |
| lastzero = false | |
| sawnonzero = true | |
| if checkPad { | |
| off := uintptr(0) | |
| for j := i - 1; j >= 0; j-- { | |
| if typ.fields[j].typ.size > 0 { | |
| off = typ.fields[j].offset() + typ.fields[j].typ.size | |
| break | |
| } | |
| } | |
| off += uintptr(v.typ.align) - 1 | |
| off &^= uintptr(v.typ.align) - 1 | |
| if off != v.offset() { | |
| fields = append(fields, padFFI(v.offset()-off)) | |
| } | |
| checkPad = false | |
| } | |
| fields = append(fields, typeToFFI(v.typ)) | |
| } | |
| if !sawnonzero { | |
| return emptyStructToFFI() | |
| } | |
| if lastzero { | |
| // The compiler adds one byte padding to non-empty struct ending | |
| // with a zero-sized field (types.cc:get_backend_struct_fields). | |
| // Add this padding to the FFI type. | |
| fields = append(fields, ffi_type_uint8()) | |
| } | |
| fields = append(fields, nil) | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &fields[0], | |
| } | |
| } | |
| // sliceToFFI returns an ffi_type for a Go slice type. | |
| func sliceToFFI(typ *slicetype) *__ffi_type { | |
| elements := make([]*__ffi_type, 4) | |
| elements[0] = ffi_type_pointer() | |
| elements[1] = intToFFI() | |
| elements[2] = elements[1] | |
| elements[3] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| // complexToFFI returns an ffi_type for a Go complex type. | |
| // This is only used if libffi does not support complex types internally | |
| // for this target. | |
| func complexToFFI(ffiFloatType *__ffi_type) *__ffi_type { | |
| elements := make([]*__ffi_type, 3) | |
| elements[0] = ffiFloatType | |
| elements[1] = ffiFloatType | |
| elements[2] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| // arrayToFFI returns an ffi_type for a Go array type. | |
| func arrayToFFI(typ *arraytype) *__ffi_type { | |
| if typ.len == 0 { | |
| return emptyStructToFFI() | |
| } | |
| if typ.typ.kind&kindDirectIface != 0 { | |
| return ffi_type_pointer() | |
| } | |
| elements := make([]*__ffi_type, typ.len+1) | |
| et := typeToFFI(typ.elem) | |
| for i := uintptr(0); i < typ.len; i++ { | |
| elements[i] = et | |
| } | |
| elements[typ.len] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| // intToFFI returns an ffi_type for the Go int type. | |
| func intToFFI() *__ffi_type { | |
| switch unsafe.Sizeof(0) { | |
| case 4: | |
| return ffi_type_sint32() | |
| case 8: | |
| return ffi_type_sint64() | |
| default: | |
| throw("bad int size") | |
| return nil | |
| } | |
| } | |
| // emptyStructToFFI returns an ffi_type for an empty struct. | |
| // The libffi library won't accept a struct with no fields. | |
| func emptyStructToFFI() *__ffi_type { | |
| elements := make([]*__ffi_type, 2) | |
| elements[0] = ffi_type_void() | |
| elements[1] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| // padFFI returns a padding field of the given size | |
| func padFFI(size uintptr) *__ffi_type { | |
| elements := make([]*__ffi_type, size+1) | |
| for i := uintptr(0); i < size; i++ { | |
| elements[i] = ffi_type_uint8() | |
| } | |
| elements[size] = nil | |
| return &__ffi_type{ | |
| _type: _FFI_TYPE_STRUCT, | |
| elements: &elements[0], | |
| } | |
| } | |
| //go:linkname makeCIF reflect.makeCIF | |
| // makeCIF is used by the reflect package to allocate a CIF. | |
| func makeCIF(ft *functype) *_ffi_cif { | |
| cif := new(_ffi_cif) | |
| ffiFuncToCIF(ft, false, false, cif) | |
| return cif | |
| } |